---
# System prepended metadata

title: '#10 Array: Spiral Traverse'
tags: [Medium, Array]

---

# #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 => 不符, 結束
```
