diff --git a/54.spiral_matrix/main.go b/54.spiral_matrix/main.go new file mode 100644 index 0000000..fef84a4 --- /dev/null +++ b/54.spiral_matrix/main.go @@ -0,0 +1,124 @@ +package main + +import "math" + +// brute force will result in a time limit exceeded error +func spiralOrder(matrix [][]int) []int { + mixRow, mixCol := len(matrix), len(matrix[0]) + result := make([]int, mixRow*mixCol) + row, col := 0, 0 + result[0] = matrix[row][col] + visit := 101 + matrix[row][col] = visit + direction := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} + for i := 1; i < len(result); { + for j := 0; j < len(direction); { + newRow, newCol := row+direction[j][0], col+direction[j][1] + if newRow == mixRow || newCol == mixCol || + newRow < 0 || newCol < 0 { + j++ + continue + } + + if matrix[newRow][newCol] == visit { + j++ + continue + } + + row, col = newRow, newCol + result[i] = matrix[row][col] + matrix[row][col] = visit + i++ + if i == len(result) { + break + } + } + } + + return result +} + +// optimize spiralOrder +// time complexity: O(M * N) this is because we visit each element once. +// space complexity: O(1) +// +// if we mark the cells that we visited, then when we run into a visited cell, +// we know we need to run. +// +// if `changeDirection` is larger than 1, it means we are continuously +// changing our directions, and therefore we've visited all of the elements. +// +// modifying the original data may not be a good choice sometimes. therefore, +// we can also prepare another boolean matrix to store the cells we visited. +// however, the implementation of this approach is quite similar. +func spiralOrder2(matrix [][]int) []int { + m, n := len(matrix), len(matrix[0]) + row, col := 0, 0 + result := make([]int, 0, m*n) + visit := 101 + result = append(result, matrix[row][col]) + matrix[row][col] = visit + + directions := [4][2]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} + currentDirection := 0 + changeDirection := 0 + + for changeDirection < 2 { + for row+directions[currentDirection][0] >= 0 && + row+directions[currentDirection][0] < m && + col+directions[currentDirection][1] >= 0 && + col+directions[currentDirection][1] < n && + matrix[row+directions[currentDirection][0]][col+directions[currentDirection][1]] != visit { + row += directions[currentDirection][0] + col += directions[currentDirection][1] + result = append(result, matrix[row][col]) + matrix[row][col] = visit + + // reset this to 0 since we did not break and change the direction + changeDirection = 0 + } + // change our direction + currentDirection = (currentDirection + 1) % 4 + // increment changeDirection because we changed our direction + changeDirection++ + } + + return result +} + +// set up boundaries +// time complexity: O(M * N) +// space complexity: O(1) + +// there are only two movement pattern, right + down and left + up. +// in the first case we increment either the row or column by 1, and +// in the latter case we increment either the row or column by -1. +// also notice that after we move horizontally the number of possible +// vertical steps decrease by 1, and after we move vertically the number +// of possible horizontal steps decrease by 1. when we run out of either +// vertical steps or horizontal steps we reach the end. +func spiralOrder3(matrix [][]int) []int { + m, n := len(matrix), len(matrix[0]) + direction := 1 + row, col := 0, -1 + result := []int{} + for math.Min(float64(m), float64(n)) != 0 { + // move horizontally + for i := 0; i < n; i++ { + col += direction + result = append(result, matrix[row][col]) + } + m -= 1 + + // move vertically + for i := 0; i < m; i++ { + row += direction + result = append(result, matrix[row][col]) + } + n -= 1 + + // flip direction + direction *= -1 + } + return result +} diff --git a/54.spiral_matrix/main_test.go b/54.spiral_matrix/main_test.go new file mode 100644 index 0000000..358f57e --- /dev/null +++ b/54.spiral_matrix/main_test.go @@ -0,0 +1,64 @@ +package main + +import ( + "testing" +) + +func TestSpiralOrder(t *testing.T) { + for _, tt := range []struct { + matrix [][]int + want []int + }{{ + matrix: [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, + want: []int{1, 2, 3, 6, 9, 8, 7, 4, 5}, + }, { + matrix: [][]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, + want: []int{1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7}, + }, { + matrix: [][]int{{8, 9, 4, 1, 2, 5}, {10, 19, 32, 14, 3, 6}}, + want: []int{8, 9, 4, 1, 2, 5, 6, 3, 14, 32, 19, 10}, + }, { + matrix: [][]int{{-10, 0, -7}, {0, 9, -100}}, + want: []int{-10, 0, -7, -100, 9, 0}, + }} { + matrix1 := shallowCopy2DSlice(tt.matrix) + got := spiralOrder(matrix1) + if !equal(tt.want, got) { + t.Errorf("got: %v, want: %v", got, tt.want) + } + + matrix2 := shallowCopy2DSlice(tt.matrix) + got = spiralOrder2(matrix2) + if !equal(tt.want, got) { + t.Errorf("got: %v, want: %v", got, tt.want) + } + + matrix3 := shallowCopy2DSlice(tt.matrix) + got = spiralOrder3(matrix3) + if !equal(tt.want, got) { + t.Errorf("got: %v, want: %v", got, tt.want) + } + } +} + +func shallowCopy2DSlice(matrix [][]int) [][]int { + shallow := make([][]int, len(matrix)) + for i := range matrix { + arr := make([]int, len(matrix[i])) + copy(arr, matrix[i]) + shallow[i] = arr + } + return shallow +} + +func equal(expect, actual []int) bool { + if len(expect) != len(actual) { + return false + } + for i := 0; i < len(expect); i++ { + if expect[i] != actual[i] { + return false + } + } + return true +} diff --git a/README.md b/README.md index 7d73003..ba93476 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ |35|[Search Insert Position](https://leetcode.com/problems/search-insert-position)|$\color{green}{\textsf{Easy}}$|[Go]()| |46|[Permutations](https://leetcode.com/problems/permutations/)|$\color{orange}{\textsf{Medium}}$|[Go](https://github.com/xxxVitoxxx/leetcode/blob/main/46.permutations/main.go)| |52|[N-Queens II](https://leetcode.com/problems/n-queens-ii/)|$\color{red}{\textsf{Hard}}$|[Go](https://github.com/xxxVitoxxx/leetcode/blob/main/52.N-queens_II/main.go)| -|53|[Maximum Subarray](https://leetcode.com/problems/maximum-subarray)|$\color{orange}{\textsf{Medium}}$|| +|53|[Maximum Subarray](https://leetcode.com/problems/maximum-subarray)|$\color{orange}{\textsf{Medium}}$|| +|54|[Spiral Matrix](https://leetcode.com/problems/spiral-matrix/submissions)|$\color{orange}{\textsf{Medium}}$|[Go](https://github.com/xxxVitoxxx/leetcode/blob/main/54.spiral_matrix/main.go)| |58|[Length of Last Word](https://leetcode.com/problems/length-of-last-word)|$\color{green}{\textsf{Easy}}$|| |61|[Rotate List](https://leetcode.com/problems/rotate-list)|$\color{orange}{\textsf{Medium}}$|[Go](https://github.com/xxxVitoxxx/leetcode/blob/main/61.rotate_list/main.go)| |66|[Plus One](https://leetcode.com/problems/plus-one)|$\color{green}{\textsf{Easy}}$||