Easy
题目描述
井字棋是由两名玩家 A 和 B 在 3 x 3 的网格上进行的游戏。井字棋游戏的规则如下:
- 玩家轮流将字符放入空方格 (" “) 中。
- 第一个玩家 A 总是放字符 “X”,而第二个玩家 B 总是放字符 “O”。
- “X” 和 “O” 只能放入空方格,永远不能放在已经被占用的方格上。
- 当有 3 个相同的(非空)字符填充任何行、列或对角线时,游戏结束。
- 当所有方格都被填满时,游戏也会结束。
- 如果游戏结束,则无法再进行任何移动。
给你一个数组 moves,其中 moves[i] = [rowi, coli] 表示第 i 次移动将会放在网格中的 grid[rowi][coli] 上。如果游戏存在获胜者(A 或 B),就返回该获胜者,如果游戏以平局结束,则返回 “Draw”,如果仍会有移动进行,则返回 “Pending”。
你可以假定 moves 都 有效(遵循井字棋规则),网格最初是空的,A 将先行动。
示例 1:
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:"A"
解释:A 获胜,他总是先走。
示例 2:
输入:moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
输出:"B"
解释:B 获胜。
示例 3:
输入:moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
输出:"Draw"
解释:由于没有办法再行动,游戏以平局结束。
提示:
1 <= moves.length <= 9moves[i].length == 20 <= rowi, coli <= 2moves中没有重复的元素moves遵循井字棋的规则
解题思路
这道题要求我们模拟井字棋游戏并判断结果。解题思路分为以下几个步骤:
基本思路:
- 首先重建棋盘状态:根据moves数组逐步在3x3棋盘上放置棋子,奇数位置是A(‘X’),偶数位置是B(‘O’)
- 判断获胜条件:检查所有行、列和对角线是否有三个相同的棋子
- 返回结果:如果有获胜者返回对应玩家,如果棋盘满了但无获胜者返回"Draw”,否则返回"Pending"
优化思路: 可以不用真正构建棋盘,而是使用计数的方式。对于每个玩家,维护行、列、对角线的计数器。当某个计数器达到3时,该玩家获胜。这样可以边放置棋子边检查获胜条件,提前结束游戏。
推荐解法: 使用计数器的优化方法,时间复杂度更优,代码也更简洁。每次放置棋子后立即检查是否获胜,避免了最后遍历整个棋盘的开销。
代码实现
class Solution {
public:
string tictactoe(vector<vector<int>>& moves) {
vector<int> rows(3, 0), cols(3, 0);
int diag = 0, anti_diag = 0;
for (int i = 0; i < moves.size(); i++) {
int r = moves[i][0], c = moves[i][1];
int player = (i % 2 == 0) ? 1 : -1;
rows[r] += player;
cols[c] += player;
if (r == c) diag += player;
if (r + c == 2) anti_diag += player;
if (abs(rows[r]) == 3 || abs(cols[c]) == 3 ||
abs(diag) == 3 || abs(anti_diag) == 3) {
return (player == 1) ? "A" : "B";
}
}
return (moves.size() == 9) ? "Draw" : "Pending";
}
};
class Solution:
def tictactoe(self, moves: List[List[int]]) -> str:
rows, cols = [0] * 3, [0] * 3
diag = anti_diag = 0
for i, (r, c) in enumerate(moves):
player = 1 if i % 2 == 0 else -1
rows[r] += player
cols[c] += player
if r == c:
diag += player
if r + c == 2:
anti_diag += player
if abs(rows[r]) == 3 or abs(cols[c]) == 3 or abs(diag) == 3 or abs(anti_diag) == 3:
return "A" if player == 1 else "B"
return "Draw" if len(moves) == 9 else "Pending"
public class Solution {
public string Tictactoe(int[][] moves) {
int[] rows = new int[3], cols = new int[3];
int diag = 0, antiDiag = 0;
for (int i = 0; i < moves.Length; i++) {
int r = moves[i][0], c = moves[i][1];
int player = (i % 2 == 0) ? 1 : -1;
rows[r] += player;
cols[c] += player;
if (r == c) diag += player;
if (r + c == 2) antiDiag += player;
if (Math.Abs(rows[r]) == 3 || Math.Abs(cols[c]) == 3 ||
Math.Abs(diag) == 3 || Math.Abs(antiDiag) == 3) {
return (player == 1) ? "A" : "B";
}
}
return (moves.Length == 9) ? "Draw" : "Pending";
}
}
var tictactoe = function(moves) {
const board = Array(3).fill().map(() => Array(3).fill(''));
for (let i = 0; i < moves.length; i++) {
const [row, col] = moves[i];
board[row][col] = i % 2 === 0 ? 'A' : 'B';
if (checkWinner(board, row, col, board[row][col])) {
return board[row][col];
}
}
return moves.length === 9 ? "Draw" : "Pending";
};
function checkWinner(board, row, col, player) {
if (board[row][0] === player && board[row][1] === player && board[row][2] === player) return true;
if (board[0][col] === player && board[1][col] === player && board[2][col] === player) return true;
if (row === col && board[0][0] === player && board[1][1] === player && board[2][2] === player) return true;
if (row + col === 2 && board[0][2] === player && board[1][1] === player && board[2][0] === player) return true;
return false;
}
复杂度分析
| 复杂度类型 | 大小 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | n为moves的长度,最多9步,每步检查获胜条件为O(1) |
| 空间复杂度 | O(1) | 只使用固定大小的数组存储行列对角线计数 |