Skip to content

Add: Spiral Matrix #929

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Spiral Matrix/Images/spiral-matrix.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
156 changes: 156 additions & 0 deletions Spiral Matrix/README.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Spiral Matrix

Goal: Traverse a 2D array in spiral order.

## Overview
Given a 2D array, which can be a square array with `n == m`, we want to return a one-dimensional array of all the elements of the 2D array in spiral order.

Spiral order will start at the top left corner of the 2D array move all the way to the right-most column on that row, then continue in a spiral pattern all the way until every element has been visited.

![spiral-matrix](Images/spiral-matrix.jpg)

## Example
Given the 2D array

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

we want to traverse this in spiral order and return the following one-dimensional array

```swift
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
```

## Iterative approach
We want to traverse this in clockwise spiral order. We go from the outer-most ‘layer’ to the inner-most ‘ layer’.

Let’s hold the indices of

```swift
var startRow = 0
var startCol = 0
var endRow = array.count - 1
var endCol = array[0].count - 1
```

The order in which we will move through the first layer of the spiral using these indices is

### Steps

1. Loop while `startRow <= endRow, startCol <= endCol`
2. Horizontally traverse towards the right from `startCol` to (inclusive) `endCol` by `1`.
3. Vertically traverse down from `startRow + 1` to (inclusive) `endRow` by `1` .
4. Horizontally traverse towards the left from `endCol - 1` to (inclusive) `startCol` by `-1`.
5. Vertically traverse up from `endRow - 1` to (inclusive) `startCol + 1` by `-1`.
6. Increment indices `startRow += 1` `startCol += 1`
7. Decrement indices `endRow -= 1` `endCol -= 1`

### Edge cases
The matrix may have single row or a single columns in the middle. In this case, we don’t want to double-count values during step `4` and `5`, which we have already counted in step `2` and `3`. We will do an index check in both cases and break if it is true.

### Code

```swift
func spiralTraverse(array: [[Int]]) -> [Int] {
guard array.count != 0 else { return [] }

var result = [Int]()
var startRow = 0
var endRow = array.count - 1
var startCol = 0
var endCol = array[0].count - 1

while startRow <= endRow, startCol <= endCol {
for col in stride(from: startCol, through: endCol, by: 1) {
result.append(array[startRow][col])
}

for row in stride(from: startRow + 1, through: endRow, by: 1) {
result.append(array[row][endCol])
}

for col in stride(from: endCol - 1, through: startCol, by: -1) {
if startRow == endRow {
break
}
result.append(array[endRow][col])
}

for row in stride(from: endRow - 1, through: startRow + 1, by: -1) {
if startCol == endCol {
break
}
result.append(array[row][startCol])
}

startRow += 1
endRow -= 1
startCol += 1
endCol -= 1
}
return result
}
```

## Recursive approach
Now let’s check out the recursive implementation of the algorithm. In this version we don’t need to maintain and update indices since we will be calling the function recursively based on different values.

### Code
```swift
func spiralTraverse(array: [[Int]]) -> [Int] {
var result = [Int]()
spiralHelper(array, 0, array.count - 1, 0, array[0].count - 1, &result)
return result
}

func spiralHelper(_ array: [[Int]], _ startRow: Int, _ endRow: Int, _ startCol: Int, _ endCol: Int, _ result: inout [Int]) {
if startRow > endRow || startCol > endCol {
return
}

for col in stride(from: startCol, through: endCol, by: 1) {
result.append(array[startRow][col])
}

for row in stride(from: startRow + 1, through: endRow, by: 1) {
result.append(array[row][endCol])
}

for col in stride(from: endCol - 1, through: startCol, by: -1) {
if startRow == endRow { break }
result.append(array[endRow][col])
}

for row in stride(from: endRow - 1, through: startRow + 1, by: -1) {
if startCol == endCol { break }
result.append(array[row][startCol])
}

spiralHelper(array, startRow + 1, endRow - 1, startCol + 1, endCol - 1, &result)
}
```

## Test

```swift
let array = [
[1,2,3,4],
[12,13,14,5],
[11,16,15,6],
[10,9,8,7]
]
spiralTraverse(array: array)
```

## Complexity

* Time: `O(n)` - where `n` is the total number of elements in the 2D array
* Space: `O(n)` - since we are storing every element of the 2D array into a one-dimensional array
---
Written for Swift Algorithm Club by Azhar Anwar
103 changes: 103 additions & 0 deletions Spiral Matrix/spiral-matrix.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Foundation

/**
Complexity:
Time: O(n) - where n is the total number of elements in the 2D array
Space: O(n)
*/

/// Iterative Spiral Matrix traversal
/// - Parameter array: 2D input array
/// - Returns: One-dimensional result array
func spiralTraverseIterative(array: [[Int]]) -> [Int] {
if array.count == 0 { return [] }

var result = [Int]()

var startRow = 0
var startCol = 0
var endRow = array.count - 1
var endCol = array[0].count - 1

while startRow <= endRow, startCol <= endCol {
for col in stride(from: startCol, through: endCol, by: 1) {
result.append(array[startRow][col])
}

for row in stride(from: startRow + 1, through: endRow, by: 1) {
result.append(array[row][endCol])
}

for col in stride(from: endCol - 1, through: startCol, by: -1) {
if startRow == endRow { break }
result.append(array[endRow][col])
}

for row in stride(from: endRow - 1, through: startCol + 1, by: -1) {
if startCol == endCol { break }
result.append(array[row][startCol])
}

startRow += 1
endRow -= 1
startCol += 1
endCol -= 1
}

return result
}


/// Recursive Spiral Matrix traversal
/// - Parameter array: 2D array
/// - Returns: One-dimensional array of all elements from the 2D array traversed in spiral order.
func spiralTraverseRecursive(array: [[Int]]) -> [Int] {
var result = [Int]()
spiralHelper(array, 0, array.count - 1, 0, array[0].count - 1, &result)
return result
}

/// Spiral traversal helper function
/// - Parameters:
/// - array: 2D array
/// - startRow: Index of starting row
/// - endRow: Index of last row
/// - startCol: Index of first column
/// - endCol: index of last column
/// - result: One-dimensional result array.
func spiralHelper(_ array: [[Int]], _ startRow: Int, _ endRow: Int, _ startCol: Int, _ endCol: Int, _ result: inout [Int]) {
if startRow > endRow || startCol > endCol {
return
}

for col in stride(from: startCol, through: endCol, by: 1) {
result.append(array[startRow][col])
}

for row in stride(from: startRow + 1, through: endRow, by: 1) {
result.append(array[row][endCol])
}

for col in stride(from: endCol - 1, through: startCol, by: -1) {
if startRow == endRow { break }
result.append(array[endRow][col])
}

for row in stride(from: endRow - 1, through: startRow + 1, by: -1) {
if startCol == endCol { break }
result.append(array[row][startCol])
}

spiralHelper(array, startRow + 1, endRow - 1, startCol + 1, endCol - 1, &result)
}


///Test
let array = [
[1,2,3,4],
[12,13,14,5],
[11,16,15,6],
[10,9,8,7]
]
spiralTraverseIterative(array: array)
spiralTraverseRecursive(array: array)
4 changes: 4 additions & 0 deletions Spiral Matrix/spiral-matrix.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios'>
<timeline fileName='timeline.xctimeline'/>
</playground>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>