iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0
自我挑戰組

30天演算法解題系列 第 10

Day 10:spiral traverse

  • 分享至 

  • xImage
  •  

problem

輸入為一個長寬為 m * n (m 可以等於 n) 的雙層陣列,將其中所有的元素以螺旋的順序放入另一陣列回傳。

這裡的螺旋順序指的是從雙層陣列的左上角開始向右,以螺旋的方式看過每個陣列中的每個元素。

sample input:
array = [
  [1,   2,   3,   4],
  [12, 13, 14, 5],
  [11, 16, 15, 6],
  [10,   9,   8,   7]
]

sample output:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

解這題的其中一個想法是追蹤目前遍歷陣列的方向,例如一開始的方向是向右,直到碰到底變成向下,再碰到底變成向左...以此類推。

或者另外一種想法是,要用螺旋順序遍歷陣列,代表先看過整個陣列的外圍
1       2       3     4
12   13   14     5
11   16   15     6
10     9     8       7

接著再看陣列剩餘部份的外圍...如此一圈一圈向內,直到看完整個陣列,且不論雙層陣列長寬多少,都適用這個規則。也就是說,只要知道如何遍歷最外圍,就可以遍歷整個陣列。

要遍歷外圍,首先利用幾個變數來指向陣列的首列 (sR)、末列 (eR)、首行 (sC)、末行 (eC)。而且這幾個變數可以直接用雙層陣列的長度來表達。

      sC       eC
      ↓           ↓
sR → 1       2       3     4
        12   13   14     5
        11   16   15     6
eR → 10     9     8      7

接著,
看過 sC 到 eC,在 sR 上的數字 (1, 2, 3, 4)
看過 (sR + 1) 到 eR,在 eC 上的數字 (5, 6, 7)
看過 (eC - 1) 到 sC,在 eR 上的數字 (8, 9 10)
看過 (eR - 1) 到 (sR + 1),在 sC 上的數字 (11, 12)

此時已經看完整個雙層陣列的最外圍,接下來只要把同樣的邏輯重複在內層進行,也就是將 sR、sC 加一,eR、eC 減一,進行上方的步驟。直到 sR、eR 或 sC、eC 交錯,就可以知道已經看完所有數字。

最後再考慮可能會有首列末列或首行末行重疊的情況,例如下方陣列,最後會只剩下一列數字要看。這時候如果還以四邊形的方式遍歷,就會出現重複。因此在遍歷每一圈外圍的下邊左邊時,再加入 sR == eR 和 sC == eC 的判斷。
[
  [1, 1, 1, 1],
  [2, 2, 2, 2],
  [3, 3, 3, 3]
]

N 為雙層陣列中所有元素的數量
time: O(N)
space: O(N)

迴圈的寫法:

function spiralTraverse(array) {
  const result = [];
  let startRow = 0, endRow = array.length - 1;
  let startCol = 0, endCol = array[0].length - 1;

  while (startRow <= endRow && startCol <= endCol) {
    for (let col = startCol; col <= endCol; col++) {
      result.push(array[startRow][col]);
    }

    for (let row = startRow + 1; row <= endRow; row++) {
      result.push(array[row][endCol]);
    }

    for (let col = endCol - 1; col >= startCol; col--) {
      // 處理只剩一列的特殊情況
      if (startRow === endRow) break;
      result.push(array[endRow][col]);
    }

    for (let row = endRow - 1; row >= startRow + 1; row--) {
      // 處裡只剩一行的特殊情況
      if (startCol === endCol) break;
      result.push(array[row][startCol]);
    }

    startRow++;
    endRow--;
    startCol++;
    endCol--
  }

  return result;
}

遞迴的寫法:

function spiralTraverse(array) {
  const result = [];
  spiralFill(array, 0, array.length - 1, 0, array[0].length - 1, result);
  return result;
}

function spiralFill(array, startRow, endRow, startCol, endCol, result) {
  if (startRow > endRow || startCol > endCol) return;

  for (let col = startCol; col <= endCol; col++) {
    result.push(array[startRow][col]);
  }

  for (let row = startRow + 1; row <= endRow; row++) {
    result.push(array[row][endCol]);
  }

  for (let col = endCol - 1; col >= startCol; col--) {
    if (startRow === endRow) break;
    result.push(array[endRow][col]);
  }

  for (let row = endRow - 1; row >= startRow + 1; row--) {
    if (startCol === endCol) break;
    result.push(array[row][startCol]);
  }

  spiralFill(array, startRow + 1, endRow - 1, startCol + 1, endCol - 1, result);
}

上一篇
Day 09:monotonic array
下一篇
Day 11:palindrome check
系列文
30天演算法解題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言