算法題-順時針打印矩陣

這是我參與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/\

相關文章
相關標籤/搜索