题目

https://leetcode.cn/problems/valid-sudoku/description/

核心思路

  • 遍历二维数组,需要两层 for 循环
  • 判断是否有重复数字,可通过 set 数据结构来判断
  • 每一行,每一列,每大格,分别都是9个,因此需要用 Array 来包裹 set 元素
  • 行 set: data[i]
  • 列 set: data[j]

粗实线分隔宫格索引 index 计算

粗实线分隔宫格的索引 index 计算比较有难度,核心是在于找到坐标 i 和 j 和 index 的数学规律

    • 可以先横向看偏移(0,1,2 ): index = 纵坐标 j / 3
  • 那纵向看(3,4,5,6,7,8)的数学规律,
    • 每一行有3个宫格,所以宫格编号 = 纵向偏移 * 3 + 横向偏移
  • 粗实线分隔宫格 index 就是 3 * Math.floor(i /3) + Math.floor(j/3)

解答

function isValidSudoku(board: string[][]): boolean {
    if (!Array.isArray(board)) {
    throw new Error("board should be array type");
  }
  const DOT = ".";
  // 初始化横向数据 set 数组
  const rowSetArray = new Array(9).fill(0).map((item) => new Set());
  // 初始化纵向数据 set 数组
  const colSetArray = new Array(9).fill(0).map((item) => new Set());
  // 初始化粗实线分隔宫格数据 set 数组
  const chunkSetArray = new Array(9).fill(0).map((item) => new Set());
  
  for (let i = 0; i < board.length; i++) {
    // 纵向遍历
    for (let j = 0; j < board[i].length; j++) {
      // 横向遍历
      const itemValue = board[i][j];
      if (itemValue === DOT) {
        // 跳过 DOT 节点
        continue;
      }
      // i 表示是属于哪行
      if (rowSetArray[i].has(itemValue)) {
        return false;
      } else {
        rowSetArray[i].add(itemValue);
      }
      // j 表示是属于哪列
      if (colSetArray[j].has(itemValue)) {
        return false;
      } else {
        colSetArray[j].add(itemValue);
      }

      // 粗实线宫格分隔 index 计算逻辑
      const chunkIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);
      if (chunkSetArray[chunkIndex].has(itemValue)) {
        return false;
      } else {
        chunkSetArray[chunkIndex].add(itemValue);
      }
    }
  }
  return true;
};

总结

  • 是否有重复值,常见的做法就是用 set 的数据结构来实现,比较简单直接
  • 这类题的有趣的点是坐标[i, j] 计算出所在行,列,粗实线分隔宫格中位置的映射关系
  • 每个元素[i, j], 有且只存在于一行,一列,一个宫格中
  • 粗实线宫格的计算,可以先横向拆解,再纵向找规律
    • 需要用整除取整的方式
    • 存在理论上的系数,一般是行列的个数,比如这题中是3