diff --git a/top-interview-leetcode150/matrix/289生命游戏.js b/top-interview-leetcode150/matrix/289生命游戏.js new file mode 100644 index 0000000..4501d51 --- /dev/null +++ b/top-interview-leetcode150/matrix/289生命游戏.js @@ -0,0 +1,117 @@ +/** + * https://leetcode.cn/problems/game-of-life/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +const gameOfLife = function (board) { + +}; + +/* +模拟:按照题目要求模拟,遍历所有的细胞,通过细胞周围的8个细胞来判断它下一次更新的状态,细胞的状态由下面的条件决定 +1.细胞是活的,会有三种情况,如果周围活细胞数量小于2,那么这个细胞死亡,如果周围活细胞数量为2个或3个,那么这个细胞维持 +存活状态,如果这个细胞周围的活细胞超过了三个,那么这个细胞死亡. +2.细胞是死的,如果周围正好有三个活细胞那么就复活这个细胞。 +按照上面的要求我们可以把问题拆分为这样,如果这个细胞是活的 并且周围的活细胞少于两个,或者周围的活细胞大于三个,那么这个细胞死亡; +如果这个细胞是死的,周围正好有三个活细胞,那么就复活这个细胞 +*/ +function f1(board) { + const m = board.length - 1; + const n = board[0].length - 1; + let lives = 0; // 表示当前细胞周围的活细胞数量 + const updates = []; // 细胞下一次要更新的状态 + + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + // 查看左上角的细胞是否存活 + if (i > 0 && j > 0 && board[i - 1][j - 1] === 1) lives++; + // 查看上面的细胞是否存活 + if (i > 0 && board[i - 1][j] === 1) lives++; + // 查看右上角的细胞是否存活 + if (i > 0 && j < n && board[i - 1][j + 1] === 1) lives++; + // 查看右边的细胞是否存活 + if (j < n && board[i][j + 1] === 1) lives++; + // 查看右下角的细胞是否存活 + if (i < m && j < n && board[i + 1][j + 1] === 1) lives++; + // 查看下面的细胞是否存活 + if (i < m && board[i + 1][j] === 1) lives++; + // 查看左下角的细胞是否存活 + if (i < m && j > 0 && board[i + 1][j - 1] === lives) lives++; + // 查看左边的细胞是否存活 + if (j > 0 && board[i][j - 1] === 1) lives++; + + // 如果当前细胞是活的,只要它周围存活的细胞小于二,或者大于三,那么下一次更新的状态是死亡,把更新状态存入更新数组 + if (board[i][j] === 1 && (lives < 2 || lives > 3)) { + updates.push(0); + continue; + } + + // 如果当前细胞是死的, 只要周围存活的细胞恰好等于3,那么下次更新的状态就是复活,把更新状态存入更新数组 + if (board[i][j] === 0 && lives === 3) { + updates.push(1); + continue; + } + + // 如果细胞状态无需变化往更新数组插入数据-1,表示无需更新 + updates.push(-1); + } + } + + // 通过updates更新数组来更新整个board + + let k = 0; + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + if (updates[k] >= 0) board[i][j] = updates[k]; + k++; + } + } +} + +/* +利用次低位来保存下一次更新的状态,省去了updates更新数组的空间 +*/ +function f2(board) { + const dx = [-1, 0, 1, -1, 1, -1, 0, 1]; + const dy = [-1, -1, -1, 0, 0, 1, 1, 1]; + + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[0].length; j++) { + let sum = 0; + + // 计算周围8个邻居的活细胞数 + for (let k = 0; k < 8; k++) { + const nx = i + dx[k]; + const ny = j + dy[k]; + if (nx >= 0 && nx < board.length && ny >= 0 && ny < board[0].length) { + sum += (board[nx][ny] & 1); // 累加邻居细胞的最低位(表示当前活或死) + } + } + + // // 根据规则设置细胞的未来状态 + // if (board[i][j] === 1) { + // // 如果当前细胞是活的 + // if (sum === 2 || sum === 3) { + // board[i][j] |= 2; // 设次低位为1,表示细胞将在下一轮继续存活 + // } + // } else { + // // 如果当前细胞是死的 + // if (sum === 3) { + // board[i][j] |= 2; // 设次低位为1,表示细胞将在下一轮复活 + // } + // } + + // 根据规则设置细胞的未来状态 + if (sum === 3 || (board[i][j] === 1 && (sum === 2 || sum === 3))) { + board[i][j] |= 2; // 设次低位为1,表示细胞将在下一轮存活或复活 + } + } + } + + // 第二次遍历,更新细胞的状态 + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + board[i][j] >>= 1; // 右移1位,更新状态为新的细胞状态 + } + } +} diff --git a/top-interview-leetcode150/matrix/36有效的数独.js b/top-interview-leetcode150/matrix/36有效的数独.js new file mode 100644 index 0000000..c972e7e --- /dev/null +++ b/top-interview-leetcode150/matrix/36有效的数独.js @@ -0,0 +1,63 @@ +/** + * https://leetcode.cn/problems/valid-sudoku/?envType=study-plan-v2&envId=top-interview-150 + * @param {character[][]} board + * @return {boolean} + */ +const isValidSudoku = function (board) { + return f1(board); +}; + +function f1(board) { + const rows = Array(9).fill(null).map(() => new Set()); + const cols = Array(9).fill(null).map(() => new Set()); + const boxes = Array(9).fill(null).map(() => new Set()); + + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + const num = board[i][j]; + if (num === '.') continue; // 空格跳过 + + const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3); // 计算所在的3x3子格的索引 + + // 检查当前数字是否已经出现过 + if (rows[i].has(num) || cols[j].has(num) || boxes[boxIndex].has(num)) { + return false; + } + + // 将当前数字加入对应的set中 + rows[i].add(num); + cols[j].add(num); + boxes[boxIndex].add(num); + } + } + + return true; +} + +function f2(board) { + // 使用位运算加速 + const rows = Array(9).fill(0); + const cols = Array(9).fill(0); + const block = Array(9).fill(0); + + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + if (board[i][j] === '.') continue; + + const x = board[i][j] - '0'; // 转换字符为数字 + const blockIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3); // 计算所在的3x3子格的索引 + + // 检查当前数字是否已经出现过 + if ((rows[i] >> x & 1) || (cols[j] >> x & 1) || (block[blockIndex] >> x & 1)) { + return false; + } + + // 使用位运算设置当前数字已出现 + rows[i] |= 1 << x; + cols[j] |= 1 << x; + block[blockIndex] |= 1 << x; + } + } + + return true; +} diff --git a/top-interview-leetcode150/matrix/48旋转矩阵.js b/top-interview-leetcode150/matrix/48旋转矩阵.js new file mode 100644 index 0000000..7fb1791 --- /dev/null +++ b/top-interview-leetcode150/matrix/48旋转矩阵.js @@ -0,0 +1,39 @@ +/** + * https://leetcode.cn/problems/rotate-image/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ +const rotate = function (matrix) { + +}; + +/** + * 思路: + * 1. 首先进行矩阵的转置操作:将 matrix[i][j] 和 matrix[j][i] 交换,遍历时确保 i < j,避免重复交换。 + * 2. 然后反转每一行,将每一行的元素顺序倒过来。 + * 这两步操作完成后,矩阵就会被顺时针旋转 90 度。 + */ +function f1(matrix) { + const n = matrix.length; + // 对矩阵装置 + for (let i = 0; i < n; i++) { + for (let j = i; j < n; j++) { + const tmp = matrix[i][j]; + matrix[i][j] = matrix[j][i]; + matrix[j][i] = tmp; + } + } + + // 反转每一行 + for (let i = 0; i < n; i++) { + let left = 0; + let right = n - 1; + while (left < right) { + const tmp = matrix[i][left]; + matrix[i][left] = matrix[i][right]; + matrix[i][right] = tmp; + left++; + right--; + } + } +} diff --git a/top-interview-leetcode150/matrix/54螺旋矩阵.js b/top-interview-leetcode150/matrix/54螺旋矩阵.js new file mode 100644 index 0000000..fc13245 --- /dev/null +++ b/top-interview-leetcode150/matrix/54螺旋矩阵.js @@ -0,0 +1,50 @@ +/** + * https://leetcode.cn/problems/spiral-matrix/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[][]} matrix + * @return {number[]} + */ +const spiralOrder = function (matrix) { + +}; + +function f1(matrix) { + if (!matrix.length || !matrix[0].length) return []; // 矩阵中没有元素 + const result = []; // 存放遍历的结果 + // 定义四个边界的起始位置 + let top = 0; // 上边界 + let bottom = matrix.length - 1; // 下边界 + let left = 0; // 左边界 + let right = matrix[0].length - 1; // 右边界 + + // 开始模拟顺时针遍历,遍历结束的条件 top > bottom 或 left > right + while (left <= right && top <= bottom) { + // 从左到右遍历上边界 + for (let i = left; i <= right; i++) { + result.push(matrix[top][i]); + } + top++; // 上边界下移 + + // 从上往下遍历右边界 + for (let i = top; i <= bottom; i++) { + result.push(matrix[i][right]); + } + right--; // 右边界向左移动 + + if (top <= bottom) { + // 从右往左遍历下边界 + for (let i = right; i >= left; i--) { + result.push(matrix[bottom][i]); + } + bottom--; // 下边界向上移动 + } + + if (left <= right) { + // 从下往上遍历左边界 + for (let i = bottom; i >= top; i--) { + result.push(matrix[i][left]); + } + left++; // 左边界向右移动 + } + } + return result; +} diff --git a/top-interview-leetcode150/matrix/73矩阵置零.js b/top-interview-leetcode150/matrix/73矩阵置零.js new file mode 100644 index 0000000..0197187 --- /dev/null +++ b/top-interview-leetcode150/matrix/73矩阵置零.js @@ -0,0 +1,110 @@ +/** + * https://leetcode.cn/problems/set-matrix-zeroes/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ +const setZeroes = function (matrix) { + +}; + +/* +遍历每一个元素,如果遍历到的元素是零就把它横竖的所有元素都设置成零,并标记这一行和这一列已经设置过了,如果在这一列遇到了某个元素 +也是0,那么就检查行标记是否已经右这一行,如果有就不处理,如果没有设置这一行的所有元素为0,并标记这一行,行处理反之 +*/ + +function f1(matrix) { + const m = matrix.length; + const n = matrix[0].length; + const rowFlag = new Array(m).fill(false); // 行标记 + const colFlag = new Array(n).fill(false); // 列标记 + + // 第一次遍历,记录哪些行和列需要置零 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (matrix[i][j] === 0) { + rowFlag[i] = true; + colFlag[j] = true; + } + } + } + + // 第二次遍历,根据标记设置矩阵元素为0 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (rowFlag[i] || colFlag[j]) { + matrix[i][j] = 0; + } + } + } +} + +/* +利用第一行来标记哪些列应该处理为零,利用第一列来标记哪些行应该处理为零,遍历除第一行和第一列的所有元素,假设matrix[i][j]===0,那么就在 +matrix[0][j] = 0, matrix[i][0] = 0, 最后遍历所有的元素,如果它所在的行和列,只要其中之一等于零,那么就设置它为零,由于利用第一列和第一 +行来存储信息,所以在收集信息之前需要判断第一行和第一列是否原本就有零,如果就记录,最后在上面所有步骤都处理完成之后通过记录的信息设置第一行 +和第一列是否需要设置为零 +*/ + +function f2(matrix) { + let rowFlag = false; // 标记矩阵的第一行是否原本就有零 + let colFlag = false; // 标记矩阵的第一列是否原本就有零 + const m = matrix.length; + const n = matrix[0].length; + + // 如果第一行中某个元素是零,就标记为true + for (let i = 0; i < n; i++) { + if (matrix[0][i] === 0) { + rowFlag = true; + break; // 找到就可以退出了,不需要继续循环 + } + } + + // 如果第一列中某个元素是零,就标记为true + for (let i = 0; i < m; i++) { + if (matrix[i][0] === 0) { + colFlag = true; + break; // 找到就可以退出了,不需要继续循环 + } + } + + // 遍历剩余的元素,如果是零,就在第一行和第一列标记为零 + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + if (matrix[i][j] === 0) { + matrix[0][j] = 0; + matrix[i][0] = 0; + } + } + } + + // 遍历第一行,如果发现当前的值为零,就设置其列为零 + for (let i = 1; i < n; i++) { // 从1开始,跳过matrix[0][i] + if (matrix[0][i] === 0) { + for (let j = 0; j < m; j++) { // 设置i列的所有元素为零 + matrix[j][i] = 0; + } + } + } + + // 遍历第一列,如果发现当前的值为零,就设置其行为零 + for (let i = 1; i < m; i++) { // 从1开始,跳过matrix[i][0] + if (matrix[i][0] === 0) { + for (let j = 0; j < n; j++) { // 设置i行的所有元素为零 + matrix[i][j] = 0; + } + } + } + + // 通过rowFlag和colFlag来判断第一列和第一行是否需要全部设置为零 + if (rowFlag) { + for (let i = 0; i < n; i++) { + matrix[0][i] = 0; + } + } + + if (colFlag) { + for (let i = 0; i < m; i++) { + matrix[i][0] = 0; + } + } +}