二维数组相关算法题

151 阅读2分钟

867.矩阵转置

给你一个二维整数数组 matrix, 返回 matrix 的 转置矩阵 。

矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[1,4,7],[2,5,8],[3,6,9]]

示例 2:

输入:matrix = [[1,2,3],[4,5,6]]
输出:[[1,4],[2,5],[3,6]]

思路

对于col不等row的二维数组,申请一个新的二位数组, newMatrix[col][row],然后[i,j] ->[j,i],对于col等于row也是一样处理

代码

/**
 * @param {number[][]} matrix
 * @return {number[][]}
 */
var transpose = function(matrix) {
    const row = matrix.length;
    const col = matrix[0].length;
    const newMatrix = [];
    for(let i = 0; i < row; i++){
        for(let j = 0; j < col; j++){
            if(!newMatrix[j]) newMatrix[j] = [];
            newMatrix[j][i] = matrix[i][j]
        }
    }
    return newMatrix;
};

47.旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[[7,4,1],[8,5,2],[9,6,3]]

解法一原地旋转

/**
 * 思路 
 * n是偶数,一共有n^2个数,每次可以放对4个数,需要操作n^2 /4 = (n/2)*(n/2)次 
 * n是奇数, 矩阵最中间的数,无法替换,需要替换 n^2-1个数,每次可以放对4个数,需要操作(n^2-1)/4 = (n-1)/2 * (n+1)/2
 * 对于偶数(n/2) 和奇数(n-1)/2,结果都可以用 n/2表示
 * 对于偶数和奇数(n+1)/2,结果一样,可以用(n+1)/2
 * 交换等式 arr[row][col] -> arr[col][n-row-1],然后推导四次交换的下标
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var rotate = function (matrix) {
    const n = matrix.length;
    for (let row = 0; row < Math.floor(n / 2); row++) {
        for (let col = 0; col < Math.floor((n + 1) / 2); col++) {
            const temp = matrix[n - col - 1][row];
            matrix[n - col - 1][row] = matrix[n - row - 1][n - col - 1];
            matrix[n - row - 1][n - col - 1] = matrix[col][n - row - 1];
            matrix[col][n - row - 1] = matrix[row][col];
            matrix[row][col] = temp;
        }
    }
};

解法二原地翻转

/**
思路2
目标是 arr[row][col] -> arr[col][n-row-1]
可以拆解为水平翻转,然后对角线翻转
arr[row][col] -> arr[n-row-1][col] -> arr[col][n-row-1]
*/
const n = matrix.length;
// 水平翻转
for (let row = 0; row < Math.floor(n / 2); row++) {
    for (let col = 0; col < n; col++) {
        const temp = matrix[row][col];
        matrix[row][col] = matrix[n - row - 1][col]
        matrix[n - row - 1][col] = temp;
    }
}
// 对角线翻转
for (let row = 0; row < n; row++) {
    // 注意这里是col < row,不是col < n,因为是在原二维数组对角线进行翻转,而不是新申请的二维数组
    for (let col = 0; col < row; col++) {
        const temp = matrix[row][col];
        matrix[row][col] = matrix[col][row];
        matrix[col][row] = temp;
    }
}

36.有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)  

注意:

一个有效的数独(部分已被填充)不一定是可解的。 只需要根据以上规则,验证已经填入的数字是否有效即可。 空白格用 '.' 表示。

代码

/**
 * @param {character[][]} board
 * @return {boolean}
 */
var isValidSudoku = function(board) {
    const rowUsed = new Array(9).fill(0).map(()=>new Array(9).fill(0));
    const colUsed = new Array(9).fill(0).map(()=>new Array(9).fill(0));
    const boxUsed = new Array(9).fill(0).map(()=>new Array(9).fill(0));
    for(let i = 0; i < board.length; i++) {
        for(let j = 0; j < board.length; j++) {
            if(board[i][j] !== '.') {
                const num = Number(board[i][j]) - 1;
                // rowUsed[i][num] 表示i行num是否出现
                if(rowUsed[i][num]) {
                    return false;
                } else {
                    rowUsed[i][num] = true;
                }

                if(colUsed[j][num]) {
                    return false
                } else {
                    colUsed[j][num] = true;
                }

                const boxIndex = (Math.floor( i / 3 )) + (Math.floor( j / 3)) * 3;
                if(boxUsed[boxIndex][num]) {
                    return false
                } else {
                    boxUsed[boxIndex][num] = true;
                }
            }
        }
    }
    return true;
};

73.矩阵置零

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。

解法一

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
时间复杂度 O(m*n) 空间复杂度 O(m + n)
var setZeroes = function(matrix) {
    const m = matrix.length;
    const n = matrix[0].length;

    // 记录当前行和列是否需要设置为0
    const rows = new Array(m).fill(false);
    const cols = new Array(n).fill(false);

    for(let i = 0; i < m; i++){
        for(let j = 0; j < n; j++){
            if(matrix[i][j] === 0) {
                rows[i] = true;
                cols[j] = true;
            }
        }
    }

    for(let i = 0; i < m; i++){
        for(let j = 0; j < n; j++){
            if(rows[i] || cols[j]) {
                matrix[i][j] = 0;
            }
        }
    }
};

解法二

// 时间复杂度 O(m*n) 空间复杂度 O(1)
var setZeroes = function (matrix) {
    const m = matrix.length;
    const n = matrix[0].length;

    // 记录第一列是否为0
    let flagClo1 = false;
    // 从第二列开始遍历,遇到0,就将二维数组的第一列和第一行设置为0
    for (let row = 0; row < m; row++) {
        // 判断第一列是否有0
        if (matrix[row][0] === 0) flagClo1 = true;
        for (let col = 1; col < n; col++) {
            if (matrix[row][col] === 0) {
                matrix[row][0] = 0;
                matrix[0][col] = 0;
            }
        }
    }
    // 从最后一行的第二列开始遍历(为了避免设置为0后,覆盖),
    // 遇到这一行的第一行为0(或者这一列的第一列为0),就将当前元设置为0
    for (let row = m - 1; row >= 0; row--) {
        for (let col = 1; col < n; col++) {
            if(matrix[row][0] === 0 || matrix[0][col] === 0) {
                matrix[row][col] = 0;
            }
        }
        // 处理每一行的第一列是否要设置为0
        if(flagClo1) matrix[row][0] = 0;
    }
};

54.螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

直接模拟

// 直接模拟
var spiralOrder = function (matrix) {
    // 记录方向下标
    const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]];
    const m = matrix.length;
    const n = matrix[0].length;
    const res = [];
    let row = 0, col = 0;
    let di = 0;
    // 记录矩阵是否被遍历过
    const used = new Array(m).fill(false).map(()=>new Array(n).fill(false));
    for(let i = 0; i < m * n; i++) {
        res.push(matrix[row][col]);
        used[row][col] = true
        let nextRow = row + dir[di][0];
        let nextCol = col + dir[di][1];
        // 判断是否到数组边界和下一个元素是否已经遍历过了,遍历过就要改变方向
        if(nextRow < 0 || nextRow >= m 
            || nextCol < 0 || nextCol >= n
            || used[nextRow][nextCol]
        )  {
            di = ( di + 1 ) % 4;
        }
        row = row + dir[di][0];
        col = col + dir[di][1]
    }
    return res;
};

按层遍历

var spiralOrder = function (matrix) {
    const m = matrix.length;
    const n = matrix[0].length;
    let startRow = 0, startCol = 0;
    let endRow = m - 1, endCol = n -1;
    const res = []
    while(startRow <= endRow && startCol <= endCol ) {
        // 遍历上面
        for(let i = startCol; i <= endCol; i++) {
            res.push(matrix[startRow][i]);
        }
        // 遍历右边
        for(let i = startRow + 1; i <= endRow; i++) {
            res.push(matrix[i][endCol]);
        }
        // 不是一行和一列
        if(startRow < endRow && startCol < endCol) {
            // bottom
            for(let i = endCol - 1; i >= startCol + 1; i--) {
                res.push(matrix[endRow][i]);
            }
            // right
            for(let i = endRow; i >= startRow + 1; i--) {
                res.push(matrix[i][startCol]);
            }
        };
        startRow++;
        endRow--;
        startCol++;
        endCol--;
    }
    return res;
};

59.螺旋矩阵二

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function (n) {
    // const dir = [[0,1], [1,0], [0,-1], [-1,0]];
    // const res = new Array(n).fill(0).map(()=>new Array(n).fill(0));
    // let row = 0, col = 0, di = 0;
    // const seen = new Array(n).fill(false).map(()=>new Array(n).fill(false));
    // for(let i = 0; i < n * n; i++) {
    //     res[row][col] = i + 1;
    //     seen[row][col] = true
    //     let nextRow = row + dir[di][0];
    //     let nextCol = col + dir[di][1];
    //     // 注意nextRow = n
    //     if(nextRow < 0 || nextRow >= n 
    //         || nextCol < 0 || nextCol >= n
    //         || seen[nextRow][nextCol]
    //         ) {
    //             di = ( di + 1 ) % 4;
    //         }
    //     row = row + dir[di][0];
    //     col = col + dir[di][1];
    // }
    // return res;

    let startRow = 0, endRow = n - 1;
    let startCol = 0, endCol = n - 1;
    const res = new Array(n).fill(0).map(() => new Array(n).fill(0));
    let num = 1;
    while (startRow <= endRow && startCol <= endCol) {
        // 上面
        for (let i = startCol; i <= endCol; i++) {
            res[startRow][i] = num;
            num++;
        }
        // 右面
        for (let i = startRow + 1; i <= endRow; i++) {
            res[i][endCol] = num;
            num++;
        }
        if (startRow < endRow && startCol < endCol) {
            // 下面
            for (let i = endCol - 1; i >= startCol + 1; i--) {
                res[endRow][i] = num;
                num++;
            }
            // 左面
            for (let i = endRow; i >= startRow + 1; i--) {
                res[i][startCol] = num;
                num++;
            }
        }
        startRow++;
        endRow--;
        startCol++;
        endCol--;
    }
    return res;
};

498.对角线遍历

/**
 * @param {number[][]} mat
 * @return {number[]}
 */
//  画一个 4 * 3推导一下
var findDiagonalOrder = function (mat) {
    const m = mat.length;
    const n = mat[0].length;
    const dir = [[-1, 1], [1, -1]];
    let row = 0, col = 0, di = 0;
    const res = [];
    for (let i = 0; i < m * n; i++) {
        res.push(mat[row][col]);
        row = row + dir[di][0];
        col = col + dir[di][1];
        if (row >= m) {
            row = m - 1; // 不是row -1
            col = col + 2;
            di = 1 - di; // 换方向
        }
        if (col >= n) {
            row = row + 2;
            col = n - 1; // 不是col -1
            di = 1 - di;
        }

        if (row < 0) {
            row = 0;
            di = 1 - di;
        }

        if (col < 0) {
            col = 0;
            di = 1 - di;
        }
    }
    return res;
};

118.杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

/**
 * @param {number} numRows
 * @return {number[][]}
 */
// 把杨辉三角看成一个一半的二维数组
var generate = function (numRows) {
    const res = [];
    for (let row = 0; row < numRows; row++) {
        const rowArr = [];
        for (let col = 0; col <= row; col++) {
            // 第一列或者最后一列设置为1
            if (col === 0 || col == row) {
                rowArr[col] = 1;
            } else {
                // 将上一行上一列和上一行前一列相加
                const prevRow = res[row - 1];
                rowArr[col] = prevRow[col] + prevRow[col - 1];
            }
        }
        res[row] = rowArr;
    }
    return res;
};

119.杨辉三角二

/**
 * @param {number} rowIndex
 * @return {number[]}
 */
//  用一个数组,存储,反向遍历
var getRow = function(rowIndex) {
    const res = new Array(rowIndex+1).fill(0);
    res[0] = 1;
    for(let row = 1; row <= rowIndex; row++) {
        for(let col = row; col > 0; col--) {
            res[col] = res[col] + res[col-1];
        } 
    }
    return res;
};