Skip to content

Commit 688785e

Browse files
committed
initialize solution for leetcode 240 and cicd
1 parent 6088b79 commit 688785e

File tree

7 files changed

+294
-0
lines changed

7 files changed

+294
-0
lines changed

.github/workflows/test.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ${{ matrix.os }}
12+
13+
strategy:
14+
matrix:
15+
go-version: [ '1.25.6' ]
16+
os: [ ubuntu-latest, windows-latest, macos-latest ]
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Set up Go ${{ matrix.go-version }}
22+
uses: actions/setup-go@v5
23+
with:
24+
go-version: ${{ matrix.go-version }}
25+
26+
- name: Cache Go modules
27+
uses: actions/cache@v4
28+
with:
29+
path: ~/go/pkg/mod
30+
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
31+
restore-keys: |
32+
${{ runner.os }}-go-${{ matrix.go-version }}-
33+
34+
- name: Download dependencies
35+
run: |
36+
find . -name "go.mod" -execdir go mod download \;
37+
38+
- name: Run tests
39+
run: |
40+
find . -name "go.mod" -execdir go test -v ./... \;
41+
42+
- name: Run tests with race detector
43+
run: |
44+
find . -name "go.mod" -execdir go test -race -v ./... \;
45+
46+
47+
48+
- name: Check code builds
49+
run: |
50+
find . -name "go.mod" -execdir go build -v ./... \;

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Introduction
2+
3+
This repository contains notes and solutions for selected coding exercises, with examples of automated testing and CI/CD workflows.
4+
5+
For each problem, I focus on the thought process rather than providing a full, final solution. That includes analysis of time and space complexity, discussion of constraints, intuition, and invariants used to reason about correctness.
6+
7+
Solutions may not be optimal, but they serve as a starting point for further improvement and learning.
8+
9+
# CI/CD setup
10+
11+
This repository uses GitHub Actions for automated testing. The workflow:
12+
13+
- **Triggers**: Runs on pushes to `main`/`develop` branches and pull requests to `main`
14+
- **Go versions**: Tests against Go 1.21, 1.22, and 1.23 for compatibility
15+
- **Test steps**:
16+
1. Downloads dependencies for all Go modules in the repository
17+
2. Runs tests with `go test -v ./...`
18+
3. Runs tests with race detector enabled
19+
4. Verifies code builds successfully
20+
21+
22+
Each problem directory with a `go.mod` file is automatically discovered and tested independently.
23+
24+
25+
26+
# Notes on CI/CD
27+
28+
I only used gitlab before so this note will mention some reference to gitlab but still trying to offer the concept of how CI/CD is setup in github acitons.
29+
30+
Please also check [text](https://docs.github.com/en/actions/tutorials/build-and-test-code/go) where it has a good starting example for go.
31+
32+
## Key Concepts
33+
34+
**GitHub Actions Basics:**
35+
- **`uses`**: Runs pre-built actions instead of custom shell commands, good to check in [text](https://github.com/marketplace?query=checkout&type=actions)
36+
- **`runs-on`**: Specifies what machine/VM to run the job on (like GitHub's free EC2)
37+
- **`actions/checkout@v4`**: Downloads repository source code to the runner like running clone - cd - checkout
38+
- **`.github/workflows/*.yml`**: GitHub's config location (vs GitLab's single `.gitlab-ci.yml`)
39+
40+
**Matrix Strategy (Parallel Jobs):**
41+
- **`strategy` + `matrix`**: Creates parallel jobs with different configurations (like GitLab's parallel:matrix)
42+
- **Cross-platform testing**: Simply use multiple OS runners (ubuntu/windows/macos)
43+
44+
**Caching (Speed Optimization):**
45+
- **`actions/cache@v4`**: Speeds up workflows by saving/restoring files between CI/CD runs
46+
- **`path`**: What directory/files to cache (~/go/pkg/mod for Go modules)
47+
- **`key`**: Unique identifier for the cache (includes OS, Go version, dependency hash)
48+
- **`restore-keys`**: Fallback cache keys to try if exact match not found
49+
- **Cache timing**: Restore happens on the step of actions/cache, save happens automatically at job end. go actions has its own caching mechanism, this repo use the custom cache just to show one of the machanism for caching.
50+
- **Cache fallback**: Uses partial matches when dependencies change incrementally, but still try to get the previous installed packages
51+
52+
**Go Commands:**
53+
- **`go test -v`**: Runs tests with verbose output showing individual test names
54+
- **`go build`**: Compiles Go source code into executables (compilation verification)
55+
- **`find . -name "go.mod" -execdir`**: Finds all Go modules and runs commands in their directories
56+
57+
## Local testing
58+
59+
To run tests locally for a specific problem:
60+
61+
```bash
62+
cd "Problem Directory"
63+
go test -v ./...
64+
```
65+
66+
To run tests with race detection:
67+
68+
```bash
69+
go test -race -v ./...
70+
```

Search a 2D Matrix II/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# 240. Search a 2D Matrix II
2+
Write an efficient algorithm that searches for a value target in an m x n integer matrix matrix. This matrix has the following properties:
3+
4+
- Integers in each row are sorted in ascending from left to right.
5+
- Integers in each column are sorted in ascending from top to bottom.
6+
7+
Constraints:
8+
9+
- m == matrix.length
10+
- n == matrix[i].length
11+
- 1 <= n, m <= 300
12+
- -109 <= matrix[i][j] <= 109
13+
- All the integers in each row are sorted in ascending order.
14+
- All the integers in each column are sorted in ascending order.
15+
- -109 <= target <= 109
16+
17+
# solution process
18+
As the description suggests, there might be someway we can utilize this property of ascending order to achieve a running complexity less than O(m * n)
19+
20+
As we can easily spot that any number in the mastrix lies in the range of [M[topleft], [bottomright]], we can guess the invariant to be ``if the target falls into the range of between the top left and bottom right corner number, then it may exist. If not, the number definitly does not exists.``
21+
22+
So is it possible to remove a part of the range in the matrix, so we always remove a part that definitly does not contain the target and keep the rest?
23+
24+
There must be a loop in this solution, so when do we exit?
25+
26+
Is there any special situation that target falls into all ranges, how can we make the code terminate under such situation?
27+

Search a 2D Matrix II/go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module search-matrix
2+
3+
go 1.25.6
4+
5+
require (
6+
github.com/davecgh/go-spew v1.1.1 // indirect
7+
github.com/pmezard/go-difflib v1.0.0 // indirect
8+
github.com/stretchr/testify v1.11.1 // indirect
9+
gopkg.in/yaml.v3 v3.0.1 // indirect
10+
)

Search a 2D Matrix II/go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
6+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
8+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
9+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Search a 2D Matrix II/solution.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package main
2+
3+
import "fmt"
4+
5+
// searchMatrix searches for a target value in a 2D matrix
6+
func searchMatrix(matrix [][]int, target int) bool {
7+
if len(matrix) == 0 || len(matrix[0]) == 0 {
8+
return false
9+
}
10+
11+
i := 0
12+
j := 0
13+
k := len(matrix) - 1
14+
l := len(matrix[0]) - 1
15+
M := matrix
16+
t := target
17+
18+
// Check if target is outside the matrix range
19+
if M[i][j] > t || M[k][l] < t {
20+
return false
21+
}
22+
23+
// Shrink the search area by eliminating rows/columns
24+
for i < k && j < l {
25+
if M[i][j] > t || M[i][l] < t {
26+
i++
27+
} else if M[i][j] > t || M[k][j] < t {
28+
j++
29+
} else if M[k][j] > t || M[k][l] < t {
30+
k--
31+
} else if M[i][l] > t || M[k][l] < t {
32+
l--
33+
} else if M[i][j] == t || M[i][l] == t || M[k][j] == t || M[k][l] == t {
34+
return true
35+
}
36+
}
37+
38+
// Search in remaining row or column
39+
if i == k {
40+
// Search remaining row
41+
for col := j; col <= l; col++ {
42+
if M[i][col] == t {
43+
return true
44+
}
45+
}
46+
return false
47+
} else {
48+
// Search remaining column
49+
for row := i; row <= k; row++ {
50+
if M[row][j] == t {
51+
return true
52+
}
53+
}
54+
return false
55+
}
56+
}
57+
58+
func main() {
59+
// Example case, generated by AI
60+
matrix := [][]int{
61+
{1, 4, 7, 11, 15},
62+
{2, 5, 8, 12, 19},
63+
{3, 6, 9, 16, 22},
64+
{10, 13, 14, 17, 24},
65+
{18, 21, 23, 26, 30},
66+
}
67+
68+
target := 5
69+
result := searchMatrix(matrix, target)
70+
fmt.Printf("Target %d found: %t\n", target, result)
71+
72+
target = 20
73+
result = searchMatrix(matrix, target)
74+
fmt.Printf("Target %d found: %t\n", target, result)
75+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestSearchMatrix(t *testing.T) {
10+
matrix := [][]int{
11+
{1, 4, 7, 11, 15},
12+
{2, 5, 8, 12, 19},
13+
{3, 6, 9, 16, 22},
14+
{10, 13, 14, 17, 24},
15+
{18, 21, 23, 26, 30},
16+
}
17+
18+
testCases := []struct {
19+
target int
20+
expected bool
21+
}{
22+
{5, true},
23+
{14, true},
24+
{20, false},
25+
{1, true},
26+
{30, true},
27+
{0, false},
28+
{31, false},
29+
}
30+
31+
for _, tc := range testCases {
32+
result := searchMatrix(matrix, tc.target)
33+
assert.Equal(t, tc.expected, result, "searchMatrix(matrix, %d)", tc.target)
34+
}
35+
}
36+
37+
func TestSearchMatrixEmptyMatrix(t *testing.T) {
38+
// Test empty matrix
39+
emptyMatrix := [][]int{}
40+
result := searchMatrix(emptyMatrix, 1)
41+
assert.False(t, result, "searchMatrix should return false for empty matrix")
42+
}
43+
44+
func TestSearchMatrixSingleElement(t *testing.T) {
45+
// Test single element matrix
46+
singleMatrix := [][]int{{1}}
47+
48+
result := searchMatrix(singleMatrix, 1)
49+
assert.True(t, result, "searchMatrix should find existing element")
50+
51+
result = searchMatrix(singleMatrix, 2)
52+
assert.False(t, result, "searchMatrix should return false for non-existing element")
53+
}

0 commit comments

Comments
 (0)