LeetCode 348. 设计井字棋
题目描述
题意分析
在
n × n棋盘上,两位玩家交替落子。每次合法落子后,判断是否有玩家占满某一整行、整列、主对角线或副对角线,获胜时返回玩家编号,否则返回0。本篇沿用落子合法、不重复占用格子的约定,重点是在每次
move中用常数时间判定胜负。判断的是整条长度为n的线,不是任意方向上出现若干相邻棋子。
解法:分别累计行、列和对角线
核心思路
[!blue]
一次落子只会增加所在行、所在列,以及可能属于的两条对角线中的棋子数。其他行列没有变化,因此可以在落子时更新少数计数,而不必每次重新扫描整个棋盘。
为两个玩家分别维护一组计数,每组共
2n + 2项:前n项保存各行,接下来的n项保存各列,最后两项保存主对角线和副对角线。用player - 1选择当前玩家的数据,避免两人的棋子被混到同一个数量中。由于合法落子不会重复占据同一格,而每条线恰好只有
n个格子,当前玩家的某条线计数达到n,就意味着这条线全部由他占据,足以判胜。计数未到n则不能宣告这一条线完成。每次固定更新行和列。
row == col时还更新主对角线,row + col == n - 1时更新副对角线;两条条件必须分别判断,中心格可能同时属于两条线。更新后检查当前行、当前列与两个对角计数即可,全程只有固定数量的操作。合法调用前提使这里无需额外棋盘去记录每格占用情况;所维护的数据只承担各条线的累计与胜负检测。
解题步骤
- 构造两个玩家的计数数组,每组长度为
2n + 2,初始为零。- 当前玩家的行计数
row与列计数n + col分别加一。- 落在主对角线时增加下标
2n,落在副对角线时增加下标2n + 1。- 当前行、当前列或任一对角计数达到
n,返回当前玩家编号;否则返回0。
代码实现
class TicTacToe {
private int n;
private int[][] cnt;
public TicTacToe(int n) {
this.n = n;
cnt = new int[2][(n << 1) + 2];
}
public int move(int row, int col, int player) {
int[] cur = cnt[player - 1];
++cur[row];
++cur[n + col];
if (row == col) {
++cur[n << 1];
}
if (row + col == n - 1) {
++cur[n << 1 | 1];
}
if (cur[row] == n || cur[n + col] == n || cur[n << 1] == n || cur[n << 1 | 1] == n) {
return player;
}
return 0;
}
}
type TicTacToe struct {
n int
cnt [][]int
}
func Constructor(n int) TicTacToe {
cnt := make([][]int, 2)
for i := range cnt {
cnt[i] = make([]int, (n<<1)+2)
}
return TicTacToe{n, cnt}
}
func (this *TicTacToe) Move(row int, col int, player int) int {
cur := this.cnt[player-1]
cur[row]++
cur[this.n+col]++
if row == col {
cur[this.n<<1]++
}
if row+col == this.n-1 {
cur[this.n<<1|1]++
}
if cur[row] == this.n || cur[this.n+col] == this.n || cur[this.n<<1] == this.n || cur[this.n<<1|1] == this.n {
return player
}
return 0
}
复杂度分析
- 时间复杂度:构造时初始化 $O(n)$ 个计数;单次
move只更新并比较固定数量的值,为 $O(1)$。- 空间复杂度:$O(n)$,两个玩家各保存
2n + 2个计数。
关键点总结
[!green]
- 增量维护受当前落子影响的线,避免每轮重复读取全部棋盘。
- 两位玩家分开计数,达到整条线长度时才表示同一玩家占满。
- 两条对角线独立更新,不能因为命中一条就跳过另一条。
易错点总结
[!yellow]
- 行与列使用同一段下标,会让不同线的棋子数量相互污染。
- 忘记玩家编号从
1开始,直接作为数组下标会选错玩家或越界。- 两个对角条件写成
if / else if,会漏记同时位于两条对角线的中心格。- 只判断是否在网格外缘,不能确定是否属于副对角线,后者要满足行列下标之和为
n - 1。- 在重复落子或其他非法调用下直接使用这套计数,数量可能超过真实占据格数;本文依照既有合法调用约定。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 面试题 16.04. 井字游戏 | 中等 | 原题读取完整棋盘后检查结果,本题维护增量计数来支持每次落子的常数时间查询。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!