feat: osijvrsoi

This commit is contained in:
2026-03-06 19:17:30 -05:00
parent 2cac846a4d
commit aad6f3e91f
43 changed files with 136 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package besttimetobuyandsellstock
func MaxProfit(prices []int) int {
buy, sell := 1_000_000_000, 0
for _, price := range prices {
buy, sell = min(buy, price), max(sell, price-buy)
}
return sell
}

View File

@@ -0,0 +1,105 @@
package car_fleet
import (
"math"
"slices"
)
type Car struct {
Position float64
Speed float64
}
// The TIME at which the two cars will collide with each other.
func (c Car) CollidesInto(o Car) float64 {
if c.Speed == o.Speed {
return math.Inf(1)
}
return float64(c.Position-o.Position) / float64(o.Speed-c.Speed)
}
// The DISTANCE the car will be at a point in time.
func (c Car) At(time float64) float64 {
return c.Position + c.Speed*time
}
// The TIME the car will reach a certain distance.
func (c Car) Reaches(target float64) float64 {
if c.Speed == 0 {
return math.Inf(1)
}
return (target - c.Position) / c.Speed
}
type Interval struct {
From float64
To float64
}
func (i Interval) Contains(point float64) bool {
return point >= i.From && point <= i.To
}
func (i Interval) Intersect(o Interval) Interval {
return Interval{
From: max(i.From, o.From),
To: min(i.To, o.To),
}
}
type Trajectory struct {
Car
Interval
}
func sortByInversePosition(a Car, b Car) int {
return int(b.Position - a.Position)
}
func CarFleet(target int, position []int, speed []int) int {
cars := []Car{}
ends := map[float64]bool{}
for i := range position {
cars = append(cars, Car{
Position: float64(position[i]),
Speed: float64(speed[i]),
})
}
slices.SortFunc(cars, sortByInversePosition)
stack := []Trajectory{}
for _, car := range cars {
fastest_trajectory := Trajectory{
Car: car,
Interval: Interval{
From: 0,
To: car.Reaches(float64(target)),
},
}
for {
if len(stack) == 0 {
stack = append(stack, fastest_trajectory)
break
}
first_trajectory := stack[len(stack)-1]
collision_time := car.CollidesInto(first_trajectory.Car)
if fastest_trajectory.Intersect(first_trajectory.Interval).Contains(collision_time) {
first_trajectory.From = collision_time
fastest_trajectory.To = collision_time
stack = append(stack, fastest_trajectory)
break
}
stack = stack[:len(stack)-1]
}
ends[stack[0].To] = true
}
return len(ends)
}

View File

@@ -0,0 +1,44 @@
package car_fleet_test
import (
"testing"
"git.maximhutz.com/practice/pkg/car_fleet"
"github.com/stretchr/testify/assert"
)
func Test1(t *testing.T) {
target := 12
position := []int{10, 8, 0, 5, 3}
speed := []int{2, 4, 1, 1, 3}
output := 3
assert.Equal(t, output, car_fleet.CarFleet(target, position, speed))
}
func Test2(t *testing.T) {
target := 10
position := []int{3}
speed := []int{3}
output := 1
assert.Equal(t, output, car_fleet.CarFleet(target, position, speed))
}
func Test3(t *testing.T) {
target := 100
position := []int{0, 2, 4}
speed := []int{4, 2, 1}
output := 1
assert.Equal(t, output, car_fleet.CarFleet(target, position, speed))
}
func Test4(t *testing.T) {
target := 10
position := []int{6, 8}
speed := []int{3, 2}
output := 2
assert.Equal(t, output, car_fleet.CarFleet(target, position, speed))
}

View File

@@ -0,0 +1,65 @@
package constructbinarytreefrompreorderandinordertraversal
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
type NodeInfo struct {
Val int
Preorder int
Inorder int
}
func buildTreeInfo(info []NodeInfo) *TreeNode {
if len(info) == 0 {
return nil
}
root_info := info[0]
for _, datum := range info {
if datum.Preorder < root_info.Preorder {
root_info = datum
}
}
left_info := []NodeInfo{}
right_info := []NodeInfo{}
for _, datum := range info {
if datum.Inorder < root_info.Inorder {
left_info = append(left_info, datum)
} else if datum.Inorder > root_info.Inorder {
right_info = append(right_info, datum)
} else {
continue
}
}
return &TreeNode{
Val: root_info.Val,
Left: buildTreeInfo(left_info),
Right: buildTreeInfo(right_info),
}
}
func buildTree(preorder []int, inorder []int) *TreeNode {
info_map := map[int]*NodeInfo{}
for o, v := range preorder {
info_map[v] = &NodeInfo{
Val: v,
Preorder: o,
}
}
for o, v := range inorder {
info_map[v].Inorder = o
}
info := []NodeInfo{}
for _, datum := range info_map {
info = append(info, *datum)
}
return buildTreeInfo(info)
}

View File

@@ -0,0 +1,36 @@
package container_with_most_water
func MaxArea(height []int) int {
if len(height) < 2 {
return 0
}
start := 0
end := len(height) - 1
// Let:
// - A = the smaller side,
// - B = the larger side.
//
// Notice that:
// - Any container with A cannot have a height larger than `min(A, B)`.
// - Any container with A cannot have a width larger than `B - A`.
//
// Because of this, all solutions containing A cannot be larger than `area`.
area := (end - start) * min(height[start], height[end])
// Futhermore, we can completely ignore A.
//
// The maximum area will either be the current `area`, or some other two
// lines in the subset without A.
var subset []int
if height[start] < height[end] {
subset = height[start+1:]
} else {
subset = height[:end]
}
// We can now evaluate the problem space in the subset, and compute the
// maximum area.
return max(area, MaxArea(subset))
}

View File

@@ -0,0 +1,26 @@
package count_paths_that_can_form_a_palindrome_in_a_tree
func isPalindromable(s string) bool {
set := map[rune]int{}
for _, b := range s {
set[b]++
}
singletons := 0
for _, v := range set {
switch v {
case 1:
singletons++
case 2:
default:
return false
}
}
return singletons < 2
}
func CountPalindromePaths(parent []int, s string) int64 {
return 0
}

View File

@@ -0,0 +1,53 @@
package courseschedule
func visit(prereqs map[int][]int, temporary map[int]bool, permanent map[int]bool, class int) bool {
if temporary[class] {
return false
}
if permanent[class] {
return true
}
if _, ok := prereqs[class]; !ok {
return true
}
temporary[class] = true
for _, p := range prereqs[class] {
if !visit(prereqs, temporary, permanent, p) {
return false
}
}
permanent[class] = true
temporary[class] = false
return true
}
func canFinish(numCourses int, prerequisites [][]int) bool {
prereqs := map[int][]int{}
for _, p := range prerequisites {
class, prerequisite := p[0], p[1]
if _, ok := prereqs[class]; !ok {
prereqs[class] = []int{}
}
prereqs[class] = append(prereqs[class], prerequisite)
}
temporary, permanent := map[int]bool{}, map[int]bool{}
for c := range prereqs {
if permanent[c] {
continue
}
if !visit(prereqs, temporary, permanent, c) {
return false
}
}
return true
}

View File

@@ -0,0 +1,82 @@
package findmedianfromdatastream
import "container/heap"
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x any) {
*h = append(*h, x.(int))
}
func (h IntHeap) Peek() int {
return h[0]
}
func (h *IntHeap) Pop() any {
old := *h
x := old[len(old)-1]
*h = old[:len(old)-1]
return x
}
type MedianFinder struct {
// Positive min-heap of top half of values.
top *IntHeap
// Negative min-heap of bottom half of values.
bottom *IntHeap
}
func Constructor() MedianFinder {
result := MedianFinder{
top: &IntHeap{},
bottom: &IntHeap{},
}
heap.Init(result.top)
heap.Init(result.bottom)
return result
}
func (m *MedianFinder) AddNum(num int) {
if m.bottom.Len() > m.top.Len() {
if -m.bottom.Peek() <= num {
heap.Push(m.top, num)
} else {
val := heap.Pop(m.bottom).(int)
heap.Push(m.top, -val)
heap.Push(m.bottom, -num)
}
} else if m.bottom.Len() < m.top.Len() {
if m.top.Peek() >= num {
heap.Push(m.bottom, -num)
} else {
val := heap.Pop(m.top).(int)
heap.Push(m.bottom, -val)
heap.Push(m.top, num)
}
} else {
if m.top.Len() == 0 {
heap.Push(m.top, num)
} else {
if median := m.FindMedian(); float64(num) > median {
heap.Push(m.top, num)
} else {
heap.Push(m.bottom, -num)
}
}
}
}
func (m *MedianFinder) FindMedian() float64 {
if m.bottom.Len() > m.top.Len() {
return float64(-m.bottom.Peek())
} else if m.bottom.Len() < m.top.Len() {
return float64(m.top.Peek())
} else {
return float64(m.top.Peek()-m.bottom.Peek()) / 2.0
}
}

View File

@@ -0,0 +1,13 @@
package findminimuminrotatedsortedarray
func FindMin(nums []int) int {
middle := len(nums) / 2
if nums[0] > nums[middle] {
return FindMin(nums[1 : middle+1])
} else if nums[middle] > nums[len(nums)-1] {
return FindMin(nums[middle+1:])
} else {
return nums[0]
}
}

View File

@@ -0,0 +1,41 @@
package group_anagrams
import (
"hash/maphash"
"slices"
)
func SortString(s string) string {
runes := []rune(s)
slices.Sort(runes)
return string(runes)
}
func AnagramHash(letters string) uint64 {
var h maphash.Hash
h.WriteString(SortString(letters))
return h.Sum64()
}
func GroupAnagrams(items []string) [][]string {
anagrams := map[uint64][]string{}
for _, item := range items {
hash := AnagramHash(item)
similar, exists := anagrams[hash]
if exists {
anagrams[hash] = append(similar, item)
} else {
anagrams[hash] = []string{item}
}
}
result := [][]string{}
for _, group := range anagrams {
result = append(result, group)
}
return result
}

View File

@@ -0,0 +1,14 @@
package group_anagrams_test
// import (
// "testing"
// . "git.maximhutz.com/practice/pkg/group_anagrams"
// "github.com/stretchr/testify/assert"
// )
// func Test1(t *testing.T) {
// assert.Equal(t,
// [][]string{{"bat"}, {"nat", "tan"}, {"ate", "eat", "tea"}},
// GroupAnagrams([]string{"eat", "tea", "tan", "ate", "nat", "bat"}))
// }

View File

@@ -0,0 +1,60 @@
package largest_rectangle_in_histogram
type Box struct {
Start int
Height int
}
// Calculates the size of the box, from its starting point upto (and NOT
// including) a certain point.
func (b Box) Size(upto int) int {
return (upto - b.Start) * b.Height
}
func LargestRectangleArea(heights []int) int {
// This is a stack, containing all boxes that haven't been completed yet. Each
// box contains two integers. The first is the starting index, and the second
// is the height of the box.
boxes := []Box{}
// The accumulation of the biggest box so far.
biggest := 0
// Add a number that is guaranted to be lower than any other. This will pop out
// any boxes left in the stack at the end of the program.
heights = append(heights, -1)
for i, height := range heights {
// This is the farthest index back where the eventual new box can start.
// Everytime we pop a box off the top of the stack, this means every item
// from the start of the box until now is > the current height. So, we can
// extend that box at least that far back.
farthest := i
for len(boxes) > 0 {
top := boxes[len(boxes)-1]
// The boxes are implicitly ordered by height. When one gets the top box,
// all below must have a <= height.
//
// If the very top box has a height <= to the current height, that means
// all boxes left will carry over into the next iteration, so we can continue.
if top.Height < height {
break
}
// Since this box is taller than the current height, it ends here.
// We calculate its size, and accumulate it in the `biggest` variable.
biggest = max(biggest, top.Size(i))
// Remove it from the stack.
boxes = boxes[:len(boxes)-1]
// Push the starting point of the box farther back.
farthest = top.Start
}
// A box is always created for each new height.
boxes = append(boxes, Box{Start: farthest, Height: height})
}
return biggest
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
package linked_list_cycle
type ListNode struct {
Val int
Next *ListNode
}
func succeed(n *ListNode) *ListNode {
if n == nil {
return n
} else {
return n.Next
}
}
func HasCycle(head *ListNode) bool {
one, two := head, succeed(head)
for one != nil && two != nil {
if one == two {
return true
}
one, two = succeed(one), succeed(succeed(two))
}
return false
}

View File

@@ -0,0 +1,86 @@
package longest_consecutive_sequence
// type Node struct {
// // The location of the parent node.
// Parent *Node
// // The number of descendent nodes, including itself.
// Children int
// }
// func FindAncestor(n *Node) *Node {
// ancestor := n
// for ancestor.Parent != nil {
// ancestor = ancestor.Parent
// }
// return ancestor
// }
// func LongestConsecutive(nums []int) int {
// nodes := map[int]*Node{}
// for _, num := range nums {
// if _, exists := nodes[num]; exists {
// continue
// }
// node := &Node{Parent: nil, Children: 1}
// if lower, lower_exists := nodes[num-1]; lower_exists {
// ancestor := FindAncestor(lower)
// ancestor.Parent = node
// node.Children += ancestor.Children
// }
// if upper, lower_exists := nodes[num+1]; lower_exists {
// ancestor := FindAncestor(upper)
// ancestor.Parent = node
// node.Children += ancestor.Children
// }
// nodes[num] = node
// }
// maximum := 0
// for _, node := range nodes {
// maximum = max(maximum, node.Children)
// }
// return maximum
// }
func LongestConsecutive(nums []int) int {
items := map[int]bool{}
for _, num := range nums {
items[num] = true
}
maximum := 0
for item, unused := range items {
if !unused {
continue
}
items[item] = false
sequence := 1
for i := item - 1; items[i]; i-- {
items[i] = false
sequence++
}
for i := item + 1; items[i]; i++ {
items[i] = false
sequence++
}
maximum = max(maximum, sequence)
}
return maximum
}

View File

@@ -0,0 +1,32 @@
package longest_consecutive_sequence_test
import (
"testing"
"git.maximhutz.com/practice/pkg/longest_consecutive_sequence"
"github.com/stretchr/testify/assert"
)
func Test1(t *testing.T) {
assert.Equal(t, 4, longest_consecutive_sequence.LongestConsecutive([]int{100, 4, 200, 1, 3, 2}))
}
func Test2(t *testing.T) {
assert.Equal(t, 9, longest_consecutive_sequence.LongestConsecutive([]int{0, 3, 7, 2, 5, 8, 4, 6, 0, 1}))
}
func Test3(t *testing.T) {
assert.Equal(t, 3, longest_consecutive_sequence.LongestConsecutive([]int{1, 0, 1, 2}))
}
func Test4(t *testing.T) {
assert.Equal(t, 0, longest_consecutive_sequence.LongestConsecutive([]int{}))
}
func Test5(t *testing.T) {
assert.Equal(t, 7, longest_consecutive_sequence.LongestConsecutive([]int{1, 3, 5, 7, 2, 6, 4}))
}
func Test6(t *testing.T) {
assert.Equal(t, 7, longest_consecutive_sequence.LongestConsecutive([]int{1, 3, 5, 7, 2, 6, 4}))
}

View File

@@ -0,0 +1,63 @@
package mergeksortedlists
import "container/heap"
type ListNode struct {
Val int
Next *ListNode
}
type ListNodeHeap []*ListNode
func (l ListNodeHeap) Len() int {
return len(l)
}
func (l ListNodeHeap) Less(i int, j int) bool {
if l[i] == nil {
return true
}
if l[j] == nil {
return false
}
return l[i].Val < l[j].Val
}
func (l *ListNodeHeap) Pop() (popped any) {
*l, popped = (*l)[:len(*l)-1], (*l)[len(*l)-1]
return
}
func (l *ListNodeHeap) Push(x any) {
*l = append(*l, x.(*ListNode))
}
func (l ListNodeHeap) Swap(i int, j int) {
l[i], l[j] = l[j], l[i]
}
func MergeKLists(lists []*ListNode) *ListNode {
hp := ListNodeHeap(lists)
heap.Init(&hp)
sorted := ListNode{}
head := &sorted
for len(hp) > 0 {
min := heap.Pop(&hp).(*ListNode)
if min == nil {
continue
}
rest := min.Next
head.Next, head = min, min
if rest != nil {
heap.Push(&hp, rest)
}
}
return sorted.Next
}

View File

@@ -0,0 +1,33 @@
package merge_two_sorted_lists
type ListNode struct {
Val int
Next *ListNode
}
func MergeTwoLists(a *ListNode, b *ListNode) *ListNode {
result := &ListNode{}
head := result
for {
if a == nil {
if b == nil {
break
} else {
head.Next, head, b = b, b, b.Next
}
} else {
if b == nil {
head.Next, head, a = a, a, a.Next
} else {
if a.Val < b.Val {
head.Next, head, a = a, a, a.Next
} else {
head.Next, head, b = b, b, b.Next
}
}
}
}
return result.Next
}

View File

@@ -0,0 +1,95 @@
package minimumwindowsubstring
import "fmt"
type Set[T comparable] map[T]int
type Substring struct {
Main string
Left int
Right int
Set Set[byte]
}
func Better(a, b string) string {
if len(a) == 0 {
return b
} else if len(b) < len(a) {
return b
}
return a
}
func NewSubstring(s string) Substring {
return Substring{s, 0, len(s) - 1, NewSet(s)}
}
func (s Set[T]) SubsetOf(t Set[T]) bool {
for k := range s {
if s[k] > t[k] {
return false
}
}
return true
}
func NewSet(t string) Set[byte] {
set := Set[byte]{}
for i := range t {
set[t[i]]++
}
return set
}
func (s *Substring) CullLeft(t Substring) {
for s.Set[s.Main[s.Left]] > t.Set[s.Main[s.Left]] {
s.Set[s.Main[s.Left]]--
s.Left++
}
fmt.Println("CANNOT CULL", string(s.Main[s.Left]))
}
func (s *Substring) IncRight() bool {
if s.Right == len(s.Main)-1 {
return false
}
s.Right++
s.Set[s.Main[s.Right]]++
return true
}
func (s Substring) String() string {
return s.Main[s.Left : s.Right+1]
}
func (s *Substring) CullRight(t Substring) {
for s.Set[s.Main[s.Right]] > t.Set[s.Main[s.Right]] {
s.Set[s.Main[s.Right]]--
s.Right--
}
}
func minWindow(s string, t string) string {
ss, tt := NewSubstring(s), NewSubstring(t)
best_answer := ""
if !tt.Set.SubsetOf(ss.Set) {
return best_answer
}
ss.CullRight(tt)
ss.CullLeft(tt)
best_answer = Better(best_answer, ss.String())
for ss.IncRight() {
ss.CullLeft(tt)
best_answer = Better(best_answer, ss.String())
}
return best_answer
}

View File

@@ -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
}

View File

@@ -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])
}
}
})
}
}

View File

@@ -0,0 +1,25 @@
package product_of_array_except_self
func ProductExceptSelf(nums []int) []int {
result := []int{}
for range nums {
result = append(result, 1)
}
lower_sum := 1
for i := range len(nums) {
result[i] *= lower_sum
lower_sum *= nums[i]
}
upper_sum := 1
for i := range len(nums) {
result[len(nums)-1-i] *= upper_sum
upper_sum *= nums[len(nums)-1-i]
}
return result
}

View File

@@ -0,0 +1,16 @@
package product_of_array_except_self_test
import (
"testing"
"git.maximhutz.com/practice/pkg/product_of_array_except_self"
"github.com/stretchr/testify/assert"
)
func Test1(t *testing.T) {
assert.Equal(t, []int{24, 12, 8, 6}, product_of_array_except_self.ProductExceptSelf([]int{1, 2, 3, 4}))
}
func Test2(t *testing.T) {
assert.Equal(t, []int{0, 0, 9, 0, 0}, product_of_array_except_self.ProductExceptSelf([]int{-1, 1, 0, -3, 3}))
}

View File

@@ -0,0 +1,27 @@
package removenthnodefromendoflist
type ListNode struct {
Val int
Next *ListNode
}
func RemoveNthFromEnd(head *ListNode, n int) *ListNode {
nodes := []*ListNode{}
for head != nil {
nodes = append(nodes, head)
head = head.Next
}
if n == len(nodes) {
return nodes[0].Next
}
if n == 1 {
nodes[len(nodes)-2].Next = nil
return nodes[0]
}
nodes[len(nodes)-(n+1)].Next = nodes[len(nodes)-(n-1)]
return nodes[0]
}

View File

@@ -0,0 +1,34 @@
package reorderlist
type ListNode struct {
Val int
Next *ListNode
}
func ReorderList(head *ListNode) {
nodes := []*ListNode{}
for head != nil {
nodes = append(nodes, head)
head = head.Next
}
start := 0
end := len(nodes) - 1
for {
if start == end {
nodes[start].Next = nil
return
}
nodes[start].Next = nodes[end]
start++
if start == end {
nodes[end].Next = nil
return
}
nodes[end].Next = nodes[start]
end--
}
}

View File

@@ -0,0 +1,19 @@
package reverse_linked_list
type ListNode struct {
Val int
Next *ListNode
}
func ReverseList(head *ListNode) *ListNode {
var reversed *ListNode = nil
current := head
for current != nil {
next := current.Next
current.Next = reversed
reversed, current = current, next
}
return reversed
}

View File

@@ -0,0 +1,30 @@
package searchinrotatedsortedarray
func searchHelper(nums []int, target int, offset int) int {
if len(nums) == 0 {
return -1
}
middle := len(nums) / 2
if target == nums[middle] {
return middle + offset
}
if nums[0] > nums[middle] {
if target >= nums[0] || target <= nums[middle] {
return searchHelper(nums[:middle], target, offset)
} else {
return searchHelper(nums[middle+1:], target, offset+(middle+1))
}
} else {
if target >= nums[0] && target <= nums[middle] {
return searchHelper(nums[:middle], target, offset)
} else {
return searchHelper(nums[middle+1:], target, offset+(middle+1))
}
}
}
func Search(nums []int, target int) int {
return searchHelper(nums, target, 0)
}

View File

@@ -0,0 +1,41 @@
package three_sum
import (
"slices"
)
func TwoSum(numbers []int, target int, solutions map[[3]int]bool) {
start := 0
end := len(numbers) - 1
for start < end {
sum := numbers[start] + numbers[end]
if sum < target {
start++
} else if sum > target {
end--
} else {
solutions[[3]int{-target, numbers[start], numbers[end]}] = true
start++
}
}
}
func ThreeSum(nums []int) [][]int {
slices.Sort(nums)
solutions := map[[3]int]bool{}
for i := range nums {
TwoSum(nums[i+1:], -nums[i], solutions)
}
result := [][]int{}
for solution := range solutions {
result = append(result, solution[:])
}
return result
}

View File

@@ -0,0 +1,26 @@
package three_sum_test
import (
"testing"
"git.maximhutz.com/practice/pkg/three_sum"
"github.com/stretchr/testify/assert"
)
func Test1(t *testing.T) {
assert.ElementsMatch(t,
[][]int{{-1, -1, 2}, {-1, 0, 1}},
three_sum.ThreeSum([]int{-1, 0, 1, 2, -1, -4}))
}
func Test2(t *testing.T) {
assert.Equal(t,
[][]int{},
three_sum.ThreeSum([]int{0, 1, 1}))
}
func Test3(t *testing.T) {
assert.Equal(t,
[][]int{{0, 0, 0}},
three_sum.ThreeSum([]int{0, 0, 0}))
}

View File

@@ -0,0 +1,89 @@
package top_k_frequent_elements
/// O(n*log(n))
// func ToMultiset(nums []int) map[int]int {
// result := map[int]int{}
// for _, num := range nums {
// result[num]++
// }
// return result
// }
// func ToEntries(multiset map[int]int) [][2]int {
// result := [][2]int{}
// for num, amount := range multiset {
// result = append(result, [2]int{num, amount})
// }
// return result
// }
// func ByDescendingAmount(a, b [2]int) int {
// return b[1] - a[1]
// }
// func GetNumbers(entries [][2]int) []int {
// amounts := []int{}
// for _, entry := range entries {
// amounts = append(amounts, entry[0])
// }
// return amounts
// }
// func TopKFrequent(nums []int, k int) []int {
// multiset := ToMultiset(nums)
// entries := ToEntries(multiset)
// slices.SortFunc(entries, ByDescendingAmount)
// return GetNumbers(entries[0:k])
// }
/// O(n)
func ToMultiset(nums []int) map[int]int {
result := map[int]int{}
for _, num := range nums {
result[num]++
}
return result
}
func TopKFrequent(nums []int, k int) []int {
multiset := ToMultiset(nums)
// Bucket Sort.
//
// A multiset's frequency is always less then or equal to the length of the
// original list. So, you can use bucket sort to sort the items in O(n).
frequencies := make([][]int, len(nums))
for value, amount := range multiset {
if frequencies[amount-1] == nil {
frequencies[amount-1] = []int{}
}
frequencies[amount-1] = append(frequencies[amount-1], value)
}
top_frequencies := []int{}
for i := len(nums) - 1; i >= 0; i-- {
for _, num := range frequencies[i] {
top_frequencies = append(top_frequencies, num)
if len(top_frequencies) == k {
return top_frequencies
}
}
}
return top_frequencies
}

View File

@@ -0,0 +1,20 @@
package top_k_frequent_elements_test
import (
"testing"
"git.maximhutz.com/practice/pkg/top_k_frequent_elements"
"github.com/stretchr/testify/assert"
)
func Test1(t *testing.T) {
assert.Equal(t, []int{1, 2}, top_k_frequent_elements.TopKFrequent([]int{1, 1, 1, 2, 2, 3}, 2))
}
func Test2(t *testing.T) {
assert.Equal(t, []int{1}, top_k_frequent_elements.TopKFrequent([]int{1}, 1))
}
func Test3(t *testing.T) {
assert.Equal(t, []int{1, 4}, top_k_frequent_elements.TopKFrequent([]int{1, 4, 1, 4, 1, 4, 3, 1, 3, 4}, 2))
}

View File

@@ -0,0 +1,31 @@
package trapping_rain_water
func Trap(heights []int) int {
// Calculate area taken up by the boundary.
boundary_area := 0
for _, height := range heights {
boundary_area += height
}
// Calculate the total area taken up by the boundary and the water.
total_area := 0
left := 0
left_height := 0
right := len(heights) - 1
right_height := 0
for left <= right {
left_height = max(left_height, heights[left])
right_height = max(right_height, heights[right])
if left_height < right_height {
total_area += left_height
left++
} else {
total_area += right_height
right--
}
}
return total_area - boundary_area
}

View File

@@ -0,0 +1,31 @@
package trapping_rain_water
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestTrap_Example1(t *testing.T) {
height := []int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}
expected := 6
result := Trap(height)
assert.Equal(t, expected, result)
}
func TestTrap_Example2(t *testing.T) {
height := []int{4, 2, 0, 3, 2, 5}
expected := 9
result := Trap(height)
assert.Equal(t, expected, result)
}
func TestTrap_Example3(t *testing.T) {
height := []int{0}
expected := 0
result := Trap(height)
assert.Equal(t, expected, result)
}

View File

@@ -0,0 +1,49 @@
package two_sum
import (
"slices"
)
type Index struct {
Index int
Value int
}
func ByValue(a, b Index) int {
return a.Value - b.Value
}
func ToIndices(list []int) []Index {
result := []Index{}
for index, value := range list {
result = append(result, Index{index, value})
}
slices.SortFunc(result, ByValue)
return result
}
// https://leetcode.com/problems/two-sum/description/
func TwoSum(nums []int, target int) []int {
numbers := ToIndices(nums)
lower, upper := 0, len(numbers)-1
for lower < upper {
current_sum := numbers[lower].Value + numbers[upper].Value
if current_sum < target {
lower++
} else if current_sum > target {
upper--
} else {
return []int{
numbers[lower].Index,
numbers[upper].Index,
}
}
}
return nil
}

View File

@@ -0,0 +1,14 @@
package two_sum_test
import (
"testing"
"git.maximhutz.com/practice/pkg/two_sum"
"github.com/stretchr/testify/assert"
)
func TestTwoSum(t *testing.T) {
assert.Equal(t, two_sum.TwoSum([]int{2, 7, 11, 15}, 9), []int{0, 1})
assert.Equal(t, two_sum.TwoSum([]int{3, 2, 4}, 6), []int{1, 2})
assert.Equal(t, two_sum.TwoSum([]int{3, 3}, 6), []int{0, 1})
}

View File

@@ -0,0 +1,30 @@
package valid_palindrome
import (
"regexp"
"strings"
)
var notAlphanumeric = regexp.MustCompile(`[^a-zA-Z0-9]`)
func KeepAlphanumeric(text string) string {
return notAlphanumeric.ReplaceAllString(text, "")
}
func IsPalindrome(text string) bool {
scrubbed := strings.ToLower(KeepAlphanumeric(text))
start := 0
end := len(scrubbed) - 1
for start < end {
if scrubbed[start] != scrubbed[end] {
return false
}
start++
end--
}
return true
}

View File

@@ -0,0 +1,14 @@
package valid_palindrome_test
import (
"testing"
"git.maximhutz.com/practice/pkg/valid_palindrome"
"github.com/stretchr/testify/assert"
)
func TestTwoSum(t *testing.T) {
assert.Equal(t, true, valid_palindrome.IsPalindrome("A man, a plan, a canal: Panama"))
assert.Equal(t, false, valid_palindrome.IsPalindrome("race a car"))
assert.Equal(t, true, valid_palindrome.IsPalindrome(" "))
}

View File

@@ -0,0 +1,46 @@
package valid_sudoku
type Multiset map[byte]bool
func (m Multiset) Add(b byte) bool {
if b == '.' {
return true
}
exists := m[b]
m[b] = true
return !exists
}
func IsValidSudoku(board [][]byte) bool {
for i := range 3 {
for j := range 3 {
box := Multiset{}
for x := range 3 {
for y := range 3 {
if !box.Add(board[3*j+y][3*i+x]) {
return false
}
}
}
}
}
for i := range 9 {
row := Multiset{}
column := Multiset{}
for j := range 9 {
if !row.Add(board[i][j]) {
return false
}
if !column.Add(board[j][i]) {
return false
}
}
}
return true
}

View File

@@ -0,0 +1,56 @@
package valid_sudoku_test
import (
"testing"
"git.maximhutz.com/practice/pkg/valid_sudoku"
"github.com/stretchr/testify/assert"
)
func Test1(t *testing.T) {
board := [][]byte{
{'5', '3', '.', '.', '7', '.', '.', '.', '.'},
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
{'.', '.', '.', '.', '8', '.', '.', '7', '9'},
}
assert.True(t, valid_sudoku.IsValidSudoku(board))
}
func Test2(t *testing.T) {
board := [][]byte{
{'8', '3', '.', '.', '7', '.', '.', '.', '.'},
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
{'.', '.', '.', '.', '8', '.', '.', '7', '9'},
}
assert.False(t, valid_sudoku.IsValidSudoku(board))
}
func Test3(t *testing.T) {
board := [][]byte{
{'.', '.', '.', '.', '5', '.', '.', '1', '.'},
{'.', '4', '.', '3', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '3', '.', '.', '1'},
{'8', '.', '.', '.', '.', '.', '.', '2', '.'},
{'.', '.', '2', '.', '7', '.', '.', '.', '.'},
{'.', '1', '5', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '2', '.', '.', '.'},
{'.', '2', '.', '9', '.', '.', '.', '.', '.'},
{'.', '.', '4', '.', '.', '.', '.', '.', '.'},
}
assert.False(t, valid_sudoku.IsValidSudoku(board))
}

View File

@@ -0,0 +1,47 @@
package wildcard_matching
var Cache = map[[2]string]bool{}
func MatchesCached(letters, pattern string) bool {
key := [2]string{letters, pattern}
if matches, exists := Cache[key]; exists {
return matches
}
result := Matches(letters, pattern)
Cache[key] = result
return result
}
func RemoveLeadingWildcards(pattern string) string {
for i := 0; i < len(pattern); i++ {
if pattern[i] != '*' {
return pattern[i:]
}
}
return ""
}
func Matches(letters string, pattern string) bool {
if len(letters) == 0 && len(pattern) == 0 {
return true
}
if len(pattern) != 0 && pattern[0] == '*' {
for i := 0; i <= len(letters); i++ {
if MatchesCached(letters[i:], RemoveLeadingWildcards(pattern)) {
return true
}
}
return false
}
if len(pattern) != 0 && len(letters) != 0 && (pattern[0] == '?' || pattern[0] == letters[0]) {
return MatchesCached(letters[1:], pattern[1:])
}
return false
}

View File

@@ -0,0 +1,32 @@
package wildcard_matching_test
import (
"testing"
"git.maximhutz.com/practice/pkg/wildcard_matching"
"github.com/stretchr/testify/assert"
)
func TestTwoSum1(t *testing.T) {
assert.Equal(t, false, wildcard_matching.Matches("aa", "a"))
}
func TestTwoSum2(t *testing.T) {
assert.Equal(t, true, wildcard_matching.Matches("aa", "*"))
}
func TestTwoSum3(t *testing.T) {
assert.Equal(t, false, wildcard_matching.Matches("cb", "?a"))
}
func TestTwoSum4(t *testing.T) {
assert.Equal(t, true, wildcard_matching.Matches("banana", "**********"))
}
func TestTwoSum5(t *testing.T) {
assert.Equal(t, true, wildcard_matching.Matches("", "******"))
}
func TestTwoSum6(t *testing.T) {
assert.Equal(t, false, wildcard_matching.Matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", "*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************a"))
}

View File

@@ -0,0 +1,116 @@
package wordsearchii
type State struct {
X, Y int
Previous *State
}
func (s *State) Visited(x, y int) bool {
if s == nil {
return false
}
if s.X == x && s.Y == y {
return true
}
return s.Previous.Visited(x, y)
}
type Trie struct {
Children map[byte]*Trie
States []*State
}
func NewTrie(board [][]byte) *Trie {
trie := &Trie{
Children: map[byte]*Trie{},
States: []*State{},
}
for x := range board {
for y := range board[x] {
if _, ok := trie.Children[board[x][y]]; !ok {
trie.Children[board[x][y]] = &Trie{
Children: map[byte]*Trie{},
States: []*State{},
}
}
trie.Children[board[x][y]].States = append(trie.Children[board[x][y]].States, &State{
X: x,
Y: y,
Previous: nil,
})
}
}
return trie
}
var CardinalDirections = [][2]int{
{-1, 0},
{1, 0},
{0, -1},
{0, 1},
}
func (t *Trie) Explore(board [][]byte, ch byte) *Trie {
child := &Trie{
Children: map[byte]*Trie{},
States: []*State{},
}
for _, state := range t.States {
for _, direction := range CardinalDirections {
xNew, yNew := state.X+direction[0], state.Y+direction[1]
if xNew < 0 || xNew >= len(board) || yNew < 0 || yNew >= len(board[xNew]) {
continue
}
if state.Visited(xNew, yNew) {
continue
}
if ch != board[xNew][yNew] {
continue
}
child.States = append(child.States, &State{
X: xNew,
Y: yNew,
Previous: state,
})
}
}
t.Children[ch] = child
return child
}
func (t *Trie) FindWord(board [][]byte, word string) bool {
if len(word) == 0 {
return len(t.States) > 0
}
child, ok := t.Children[word[0]]
if !ok {
child = t.Explore(board, word[0])
}
return child.FindWord(board, word[1:])
}
func FindWords(board [][]byte, words []string) []string {
trie := NewTrie(board)
found := []string{}
for _, word := range words {
if trie.FindWord(board, word) {
found = append(found, word)
}
}
return found
}