# #10 Array: Spiral Traverse ###### tags:`Array` `Medium` <br> ## Issue ![](https://i.imgur.com/WRn1LEM.png) ![](https://i.imgur.com/Bx01FM1.png) ## Solutions ### Official :::spoiler Solution 1 <br /> ```javascript= // O(n) time | O(n) space where n is the total number of elements in the array 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--) { // Handle the edge case when there's a single row // in the middle of the matrix. In this case, we don't // want to double-count the values in this row, which // we've already counted in the first for loop above. // See Test Case 8 for an example of this edge case. if (startRow === endRow) break; result.push(array[endRow][col]); } for (let row = endRow - 1; row > startRow; row--) { // Handle the edge case when there's a single column // in the middle of the matrix. In this case, we don't // want to double-count the values in this column, which // we've already counted in the second for loop above. // See Test Case 9 for an example of this edge case. if (startCol === endCol) break; result.push(array[row][startCol]); } startRow++; endRow--; startCol++; endCol--; } return result; } ``` **Test case 8 mentioned above** ```javascript= [ [1, 2, 3, 4], [10, 11, 12, 5], [9, 8, 7, 6] ] ``` **Test case 9 mentioned above** ```javascript= [ [1, 2, 3], [12, 13, 4], [11, 14, 5], [10, 15, 6], [9, 8, 7] ] ``` * Third `for-loop` in second round of `while-loop` would run no round for conditional `!col >= startCol` * Forth `for-loop` in second round of `while-loop` needs conditional `startCol === endCol` to break **Employ some conditionals to deal with edge cases** * Conditional `row > startRow` of forth `for-loop` to determine whether to keep iterating (without `===` like conditionals of other `for-loop`) * Conditional `startRow === endRow` to break third`for-loop` * Conditional `startCol === endCol` to break forth`for-loop` ::: <br /> :::spoiler Solution 2 <br /> ```javascript= // O(n) time | O(n) space - where n is the total number of elements in the array 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--) { // Handle the edge case when there's a single row // in the middle of the matrix. In this case, we don't // want to double-count the values in this row, which // we've already counted in the first for loop above. // See Test Case 8 for an example of this edge case. if (startRow === endRow) break; result.push(array[endRow][col]); } for (let row = endRow - 1; row > startRow; row--) { // Handle the edge case when there's a single column // in the middle of the matrix. In this case, we don't // want to double-count the values in this column, which // we've already counted in the second for loop above. // See Test Case 9 for an example of this edge case. if (startCol == endCol) break; result.push(array[row][startCol]); } spiralFill(array, startRow + 1, endRow - 1, startCol + 1, endCol - 1, result); } ``` ::: ### Everyone's :::spoiler 東 ```javascript= // Time O(n) | Space O(n) - where n is the total amount of elemens in the array function spiralTraverse(array) { let topBound = 0; let bottomBound = array.length - 1; let leftBound = 0; let rightBound = array[0].length - 1; const result = []; while(topBound <= bottomBound && leftBound <= rightBound){ for(let hIdx = leftBound; hIdx <= rightBound; hIdx++){ result.push(array[topBound][hIdx]); } topBound++; for(let vIdx = topBound; vIdx <= bottomBound; vIdx++){ result.push(array[vIdx][rightBound]); } rightBound--; if(topBound > bottomBound || leftBound > rightBound) return result; for(let hIdx = rightBound; hIdx >= leftBound; hIdx--){ result.push(array[bottomBound][hIdx]); } bottomBound--; for(let vIdx = bottomBound; vIdx >= topBound; vIdx--){ result.push(array[vIdx][leftBound]); } leftBound++; } return result; } ``` ::: <br> :::spoiler Hao Check if need to keep traverse spirally and conduct different `for-loop` in order ```javascript= /** * Time complexity: O(n) where n stands for array.length * Space complexity: O(n) where n stands for array.length */ class SpiralTraversor { dirs = ['COL_ASC', 'ROW_ASC', 'COL_DESC', 'ROW_DESC']; curDirIdx = -1; result = []; constructor(array) { this.array = array; this.startCol = 0; this.endCol = array[0].length - 1; this.startRow = 0; this.endRow = array.length - 1; } getCurDir() { this.curDirIdx += 1; if (this.curDirIdx === this.dirs.length) this.curDirIdx = 0; return this.dirs[this.curDirIdx]; } visit(dir) { switch (dir) { case 'COL_ASC': { for (let col = this.startCol; col <= this.endCol; col += 1) { this.result.push(this.array[this.startRow][col]); } this.startRow += 1; break; }; case 'ROW_ASC': { for (let row = this.startRow; row <= this.endRow; row += 1) { this.result.push(this.array[row][this.endCol]); } this.endCol -= 1; break; }; case 'COL_DESC': { for (let col = this.endCol; col >= this.startCol; col -= 1) { this.result.push(this.array[this.endRow][col]); } this.endRow -= 1; break; }; case 'ROW_DESC': { for (let row = this.endRow; row >= this.startRow; row -= 1) { this.result.push(this.array[row][this.startCol]); } this.startCol += 1; break; }; } } shouldKeepVisit() { return this.startCol <= this.endCol && this.startRow <= this.endRow; } start() { while (this.shouldKeepVisit()) { this.visit(this.getCurDir()); } } getResult() { return this.result; } } function spiralTraverse(array) { const spiralTraversor = new SpiralTraversor(array); spiralTraversor.start(); return spiralTraversor.getResult(); } ``` ::: <br> :::spoiler YC ```javascript= /* time: O(n*m) - where n is the number of rows in a 2D array, m is the number of columns in a 2D array space: O(n*m) - where n is the number of rows in a 2D array, m is the number of columns in a 2D array */ function spiralTraverse(array) { let ans = []; let left = 0; let right = array[0].length - 1; let top = 0; let bottom = array.length - 1; while(left <= right && top <= bottom){ //top line for(let i = left; i <= right; i++){ ans.push(array[top][i]); } top++; //right line for(let i = top; i <= bottom; i++){ ans.push(array[i][right]); } right--; //bottom line if(left <= right && top <= bottom){ for(let i = right; i >= left; i--){ ans.push(array[bottom][i]); } bottom--; } //left line if(left <= right && top <= bottom){ for(let i = bottom; i >= top; i--){ ans.push(array[i][left]); } left++; } } return ans; } ``` ::: <br> :::spoiler 月薪 ```javascript= function spiralTraverse(array) { let top = 0, bottom = array.length - 1 let left = 0, right = array[0].length - 1 const ans = [] while (left <= right && top <= bottom) { // Go Right for (let i = left; i <= right; i++) { ans.push(array[top][i]) } top++ // Go Down for (let i = top; i <= bottom; i++) { ans.push(array[i][right]) } right-- // Go Left for (let i = right; i >= left; i--) { ans.push(array[bottom][i]) } bottom-- // Go Top for (let i = bottom; i >= top; i--) { ans.push(array[i][left]) } left++ } return ans } ``` ::: <br> ## Discussion ### Concept **Example: Test case 9** ```javascript= [ [1, 2, 3], [12, 13, 4], [11, 14, 5], [10, 15, 6], [9, 8, 7] ] ``` #### Round of `for-loop` conducted 1 1 1 4 5 2 4 6 2 4 6 2 3 3 2 #### Write down the expected output in each round ```javascript= // startCol, endCol, startRow, endRow // 0, 3, 0, 3 // startCol +1 到 === endCol; 轉彎; startRow + 1; // 0, 1, 2, 3 => 1, 2, 3, 4 // 0, 3, 1, 3 // startRow +1 到 === endRow; 轉彎; endCol - 1 // 1, 2, 3 => 5, 6, 7 // 0, 2, 1, 3 // endCol - 1 到 === startCol; 轉彎; endRow - 1 // 2, 1, 0 => 8, 9 ,10 // 0, 2, 1, 2 // endRow - 1 到 === startRow; 轉彎; startCol + 1 // 2, 1 => 11, 12 // 1, 2, 1, 2 // startCol +1 到 === endCol; 轉彎; startRow + 1 // 1, 2 => 13, 14 // 1, 2, 2, 2 // startRow +1 到 === endRow; 轉彎; endCol - 1 // 2 => 15 // 1, 1, 2, 2 // endCol - 1 到 === startCol; 轉彎; endRow - 1 // 1 => 16 // 1, 1, 2, 1 => 不符, 結束 ```