From 2cac846a4d898c6c76a970735756301d9dc79d0d Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 13 Feb 2026 18:29:01 -0500 Subject: [PATCH] feat: friday --- pkg/pacific_atlantic_water_flow/main.go | 105 +++++++++++++++++++ pkg/pacific_atlantic_water_flow/main_test.go | 62 +++++++++++ 2 files changed, 167 insertions(+) create mode 100644 pkg/pacific_atlantic_water_flow/main.go create mode 100644 pkg/pacific_atlantic_water_flow/main_test.go diff --git a/pkg/pacific_atlantic_water_flow/main.go b/pkg/pacific_atlantic_water_flow/main.go new file mode 100644 index 0000000..8d96d64 --- /dev/null +++ b/pkg/pacific_atlantic_water_flow/main.go @@ -0,0 +1,105 @@ +package pacificatlanticwaterflow + +type Position struct{ X, Y int } + +func (p Position) Neighbors() []Position { + return []Position{ + {p.X + 1, p.Y}, + {p.X - 1, p.Y}, + {p.X, p.Y + 1}, + {p.X, p.Y - 1}, + } +} + +func (p Position) ToList() []int { + return []int{p.Y, p.X} +} + +type Board [][]int + +func (b Board) Get(p Position) int { + if b.OutOfBounds(p) { + return -1 + } + + return b[p.Y][p.X] +} + +func (b Board) Height() int { return len(b) } +func (b Board) Width() int { return len(b[0]) } + +func (b Board) OutOfBounds(p Position) bool { + return p.Y < 0 || p.Y >= b.Height() || p.X < 0 || p.X >= b.Width() +} + +type Flow struct{ Pacific, Atlantic bool } + +func (f *Flow) Merge(o Flow) { + f.Atlantic = f.Atlantic || o.Atlantic + f.Pacific = f.Pacific || o.Pacific +} + +func (f Flow) Both() bool { + return f.Pacific && f.Atlantic +} + +func (b Board) GetFlow(p Position) Flow { + return Flow{ + Atlantic: p.Y >= b.Height() || p.X >= b.Width(), + Pacific: p.Y < 0 || p.X < 0, + } +} + +func pacificAtlanticHelper(board Board, position Position, flows map[Position]Flow) Flow { + if flow, ok := flows[position]; ok { + return flow + } + + if board.OutOfBounds(position) { + return board.GetFlow(position) + } + + flow := Flow{} + + var top Position + plateau := map[Position]bool{} + unvisited := []Position{position} + for len(unvisited) > 0 { + top, unvisited = unvisited[len(unvisited)-1], unvisited[:len(unvisited)-1] + + if board.Get(top) < board.Get(position) { + flow.Merge(pacificAtlanticHelper(board, top, flows)) + } else if board.Get(top) == board.Get(position) { + if _, ok := plateau[top]; !ok { + plateau[top] = true + unvisited = append(unvisited, top.Neighbors()...) + } + } + } + + for mem := range plateau { + flows[mem] = flow + } + + return flow +} + +func PacificAtlantic(heights [][]int) [][]int { + flows := map[Position]Flow{} + board := Board(heights) + + for y := range board.Height() { + for x := range board.Width() { + pacificAtlanticHelper(board, Position{x, y}, flows) + } + } + + result := [][]int{} + for position, flow := range flows { + if flow.Both() { + result = append(result, position.ToList()) + } + } + + return result +} diff --git a/pkg/pacific_atlantic_water_flow/main_test.go b/pkg/pacific_atlantic_water_flow/main_test.go new file mode 100644 index 0000000..fa1d180 --- /dev/null +++ b/pkg/pacific_atlantic_water_flow/main_test.go @@ -0,0 +1,62 @@ +package pacificatlanticwaterflow + +import ( + "sort" + "testing" +) + +func sortResult(result [][]int) { + sort.Slice(result, func(i, j int) bool { + if result[i][0] != result[j][0] { + return result[i][0] < result[j][0] + } + return result[i][1] < result[j][1] + }) +} + +func TestPacificAtlantic(t *testing.T) { + tests := []struct { + name string + heights [][]int + expected [][]int + }{ + { + name: "5x5 grid", + heights: [][]int{ + {1, 2, 2, 3, 5}, + {3, 2, 3, 4, 4}, + {2, 4, 5, 3, 1}, + {6, 7, 1, 4, 5}, + {5, 1, 1, 2, 4}, + }, + expected: [][]int{{0, 4}, {1, 3}, {1, 4}, {2, 2}, {3, 0}, {3, 1}, {4, 0}}, + }, + { + name: "3x2 all ones", + heights: [][]int{ + {1, 1}, + {1, 1}, + {1, 1}, + }, + expected: [][]int{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := PacificAtlantic(tc.heights) + sortResult(result) + sortResult(tc.expected) + + if len(result) != len(tc.expected) { + t.Fatalf("expected %d results, got %d: %v", len(tc.expected), len(result), result) + } + + for i := range tc.expected { + if result[i][0] != tc.expected[i][0] || result[i][1] != tc.expected[i][1] { + t.Errorf("result[%d] = %v, expected %v", i, result[i], tc.expected[i]) + } + } + }) + } +}