算法题-顺时针打印矩阵

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战数组

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每个数字。markdown

分析:矩阵能够用二维数组来模拟。oop

示例 :post

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

题解

方法一:模拟打印顺序

关键点:在四个拐角处改变方向。spa

模拟打印矩阵的路径:初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或者进入以前访问过的位置时,顺时针旋转,进入下一个方向。code

判断路径是否进入以前访问过的位置须要使用一个与输入矩阵大小相同的辅助矩阵visited,其中的每一个元素表示该位置是否被访问过。当一个元素被访问时,将 visited 中的对应位置的元素设为已访问。orm

判断路径是否结束:当路径的长度 等于 矩阵中的元素数量时即为完整路径,将该路径返回。leetcode

var spiralOrder = function(matrix) {
   if (!matrix.length || !matrix[0].length) {
       return [];
    }
    // rows和columns是边界,总数是total,
    var rows = matrix.length, columns = matrix[0].length;
    var total = rows * columns;
    // 定义一个辅助数组,初始值都为false,判断某个位置是否赋值过
    let varArr = new Array(rows).fill(0).map (() => new Array(columns).fill(false))
    // 定义一个初始值是0的一维数组
    var order = new Array(total).fill(0)
    // 定义 变量 row column nextRow nextColumn dirIndex
    var row=0, column=0, dirIndex = 0;
    // 定义一个用来拐弯的数组
    let direction = [ [0,1], [1,0],[0,-1], [-1,0]  ]
    for( let i=0; i< total; i++ ) {
        // 把当前定位到的值取出放入结果数组中
        order[i] = matrix[row][column]
        varArr[row][column] = true
        const nextRow = row + direction[dirIndex][0]
        const nextColumn = column + direction[dirIndex][1]
    if( !( 0 <= nextRow && nextRow < rows && 0 <= nextColumn && nextColumn < columns && !(varArr[nextRow][nextColumn]) )){
        dirIndex = (dirIndex+1) % 4
    }
    row += direction[dirIndex][0]
    column += direction[dirIndex][1]
    }
    return order;
​
};
复制代码

代码分析:get

  1. 若是数组1形式为‘[]’或者‘[ [] ]’, 直接返回空数组。
  2. 记录行数和列数,做用是横向时:当走完列数个数字,就转弯;纵向时:走完行数个数字就转弯。
  3. 定义一个行数列数与输入矩阵相同、初始值为false的辅助矩阵,当走完某个数字就把辅助矩阵的相应位置变为true。做用是
  4. 在for循环里面定义局部变量nextRow、nextColumn,是为了在if判断条件以前也就是row、column变化以前,让nextRow、nextColumn先变化,而后根据if条件判断这样变化可不可行若是不可行,就转弯,而后再让row、column变化。

方法二:按层输出矩阵

能够将矩阵当作若干层,首先打印最外层的元素,其次打印次外层的元素,直到打印最内层的元素。it

对于每层,从左上方开始以顺时针的顺序遍历全部元素。假设当前层的左上角位于 (top,left),右下角位于 (bottom,right),按照以下顺序遍历当前层的元素。

从左到右遍历上侧元素,依次为 (top,left) 到 (top,right)。

从上到下遍历右侧元素,依次为 (top+1,right) 到(bottom,right)。

若是 left<right 且 top<bottom,则从右到左遍历下侧元素,依次为(bottom,right−1) 到 (bottom,left+1),以及从下到上遍历左侧元素,依次为 (bottom,left) 到 (top+1,left)。

遍历完当前层的元素以后,将 left 和 top 分别增长 1,将 right 和 bottom 分别减小 1,进入下一层继续遍历,直到遍历完全部元素为止。

image-20210809215251162

var spiralOrder = function(matrix) {
    if (!matrix.length || !matrix[0].length) {
        return [];
    }
​
    const rows = matrix.length, columns = matrix[0].length;
    const order = [];
    let left = 0, right = columns - 1, top = 0, bottom = rows - 1;
    while (left <= right && top <= bottom) {
        for (let column = left; column <= right; column++) {
            order.push(matrix[top][column]);
        }
        for (let row = top + 1; row <= bottom; row++) {
            order.push(matrix[row][right]);
        }
        if (left < right && top < bottom) {
            for (let column = right - 1; column > left; column--) {
                order.push(matrix[bottom][column]);
            }
            for (let row = bottom; row > top; row--) {
                order.push(matrix[row][left]);
            }
        }
        [left, right, top, bottom] = [left + 1, right - 1, top + 1, bottom - 1];
    }
    return order;
};
复制代码

代码分析:

这种方法比较好理解,就是每一层四个方向走完,以后将 left 和 top 分别增长 1,将 right 和 bottom 分别减小 1,进入下一层继续走,直到走完全部元素为止。

image-20210809215251162

方法三:剥洋葱

var spiralOrder = function(matrix) {
    const res = []
    let flag = true
    while(matrix.length) {
        // 从左到右
        if(flag){
            // 第一层
            res = res.concat(matrix.shift())
            // '如今'的第一层到最后一层的末尾
            for(let i=0; i<matrix.length; i++){
                matrix[i].length && res.push(matrix[i].pop())
            }
        // 右到左   
        } else {
            // 最后一层
            res = res.concat(matrix.pop().reverse())
            // '如今'的最后一层到第一层 
            for(let i=matrix.length - 1; i>= 0; i--){
               matrix[i].length && res.push(matrix[i].shift())
            }
        }
        flag = !flag
    }
    return res
};
​
复制代码

代码分析:

这段代码每遍历一个数都是在原数组中直接删除这个数。拿一个4*3的二维数组举例:

[[1,2,3,4],[5,6,7,8],[9,10,11,12]]

首先,先遍历最上面的一行[1,2,3,4],并在原数组中删除这一行,如今原数组变成了:[[5,6,7,8],[9,10,11,12]];而后遍历垂直的左右边的一列8和12,如今原数组变成了:[[5,6,7],[9,10,11]];接着,弹出原数组最下面一层[9,10,11]并反转为[11,10,9],如今原数组变成了:[[5,6,7]];而后从头一个一个弹出最后一层的五、六、7;

参考

连接:leetcode-cn.com/\

相关文章
相关标签/搜索