LeetCode 算法:井字游戏

298 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 18 天,点击查看活动详情

井字游戏

原题地址

设计一个算法,判断玩家是否赢了井字游戏。输入是一个 N x N 的数组棋盘,由字符 " ""X""O" 组成,其中字符 " " 代表一个空位。

以下是井字游戏的规则:

  • 玩家轮流将字符放入空位(" ")中。
  • 第一个玩家总是放字符 "O",且第二个玩家总是放字符 "X"
  • "X""O" 只允许放置在空位中,不允许对已放有字符的位置进行填充。
  • 当有N个相同(且非空)的字符填充任何行、列或对角线时,游戏结束,对应该字符的玩家获胜。
  • 当所有位置非空时,也算为游戏结束。
  • 如果游戏结束,玩家不允许再放置字符。
  • 如果游戏存在获胜者,就返回该游戏的获胜者使用的字符("X"或"O");如果游戏以平局结束,则返回 "Draw";如果仍会有行动(游戏未结束),则返回 "Pending"。

示例 1:

输入: board = ["O X"," XO","X O"]
输出: "X"

示例 2:

输入: board = ["OOX","XXO","OXO"]
输出: "Draw"
解释: 没有玩家获胜且不存在空位

示例 3:

输入: board = ["OOX","XXO","OX "]
输出: "Pending"
解释: 没有玩家获胜且仍存在空位

提示:

  • 1 <= board.length == board[i].length <= 100
  • 输入一定遵循井字棋规则

思路分析

  1. 分析题目,可以得知,若 行、列或者对角线有相同的 X 或者 O,则说明一方胜利;否则若还有空格,则为 Pending,若没有空格则为平局 Draw
  2. 处理行:因为数组中是字符串,因此处理行的时候,只需要判断每一项是否与 全是X 或者 全是O 的字符串相同即可;
  3. 处理列:定义 xRoR 分别存储每一列中 X 和 O 出现的次数,统计完成后,判断次数中是否有跟棋盘长度相同的值,若有则返回对应的一方。另外在遍历过程中,判断是否有空位,赋值给 havaSpace
  4. 处理对角线:首先循环处理正对角线,出现次数赋值给 xZD 和 oZD,然后再处理斜对角线,出现次数赋值给 xFD 和 oFD,最后出现次数判断是否跟棋盘长度相同,返回对应的一方;
  5. 上述情况都没有返回值,说明两方都未胜利,那么需要判断是否还有空位,即 havaSpace 的值,若有,返回 Pending,若无,返回 Draw

AC 代码

/**
 * @param {string[]} board
 * @return {string}
 */
var tictactoe = function(board) {
    const m = board.length

    // 行
    const x = 'X'.repeat(m)
    const o = 'O'.repeat(m)
    for(let i = 0; i < m; i++) {
        if(board[i] === x) {
            return 'X'
        }
        if(board[i] === o) {
            return 'O'
        }
    }

    // 列
    let haveSpace = false  // 是否有空格
    let xR = {}
    let oR = {}
    for(let i = 0; i < m; i++) {
        for(let j = 0; j < m; j++) {
            if(board[i][j] === 'X') {
                if(xR[j]) {
                    xR[j] += 1
                } else {
                    xR[j] = 1
                } 
            }
            if(board[i][j] === 'O') {
                if(oR[j]) {
                    oR[j] += 1
                } else {
                    oR[j] = 1
                } 
            }
            if(board[i][j] === ' ') haveSpace = true
        }
    }
    if(Object.values(xR).some(item => item === m)) return 'X'
    if(Object.values(oR).some(item => item === m)) return 'O'
    
    // 对角线
    let xZD = 0
    let oZD = 0
    for(let i = 0; i < m; i++) {
        if(board[i][i] === 'X') xZD += 1
        if(board[i][i] === 'O') oZD += 1
    }
    let xFD = 0
    let oFD = 0
    for(let i = 0; i < m; i++) {
        if(board[i][m - i - 1] === 'X') xFD += 1
        if(board[i][m - i - 1] === 'O') oFD += 1
    }
    if(xZD === m || xFD === m) return 'X'
    if(oZD === m || oFD === m) return 'O'
    
    return haveSpace ? 'Pending' : 'Draw'
};

结果:

  • 执行结果: 通过
  • 执行用时:76 ms, 在所有 JavaScript 提交中击败了25.00%的用户
  • 内存消耗:43.1 MB, 在所有 JavaScript 提交中击败了60.00%的用户
  • 通过测试用例:70 / 70

END