Compare commits
24 Commits
2a6e1677d6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
c47e2e4cb2
|
|||
|
5f33c164d9
|
|||
|
63e7b31184
|
|||
|
a2492aad21
|
|||
|
364fd261e9
|
|||
|
afa3b30e98
|
|||
|
15160f6451
|
|||
|
80c4ee3bf2
|
|||
|
e53fbf0ec6
|
|||
|
0c5444e63a
|
|||
|
1d47afa321
|
|||
|
b629395a1a
|
|||
|
5fb803129f
|
|||
|
f3abe4ff5b
|
|||
|
32173210d6
|
|||
|
c3f6b6758f
|
|||
|
3722df466f
|
|||
|
20629eab22
|
|||
|
2fc909f9c7
|
|||
|
6db53a1fb9
|
|||
|
647b79a050
|
|||
|
1e2700a2f2
|
|||
|
7fdf6a520f
|
|||
|
da48abc0d1
|
11
pkg/best_time_to_buy_and_sell_stock/main.go
Normal file
11
pkg/best_time_to_buy_and_sell_stock/main.go
Normal 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
|
||||
}
|
||||
105
pkg/car_fleet/main.go
Normal file
105
pkg/car_fleet/main.go
Normal 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)
|
||||
}
|
||||
44
pkg/car_fleet/main_test.go
Normal file
44
pkg/car_fleet/main_test.go
Normal 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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
36
pkg/container_with_most_water/main.go
Normal file
36
pkg/container_with_most_water/main.go
Normal 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))
|
||||
}
|
||||
13
pkg/find_minimum_in_rotated_sorted_array/main.go
Normal file
13
pkg/find_minimum_in_rotated_sorted_array/main.go
Normal 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]
|
||||
}
|
||||
}
|
||||
41
pkg/group_anagrams/main.go
Normal file
41
pkg/group_anagrams/main.go
Normal 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
|
||||
}
|
||||
14
pkg/group_anagrams/main_test.go
Normal file
14
pkg/group_anagrams/main_test.go
Normal 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"}))
|
||||
// }
|
||||
60
pkg/largest_rectangle_in_histogram/main.go
Normal file
60
pkg/largest_rectangle_in_histogram/main.go
Normal 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
|
||||
}
|
||||
29
pkg/largest_rectangle_in_histogram/main_test.go
Normal file
29
pkg/largest_rectangle_in_histogram/main_test.go
Normal file
File diff suppressed because one or more lines are too long
28
pkg/linked_list_cycle/main.go
Normal file
28
pkg/linked_list_cycle/main.go
Normal 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
|
||||
}
|
||||
86
pkg/longest_consecutive_sequence/main.go
Normal file
86
pkg/longest_consecutive_sequence/main.go
Normal 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
|
||||
}
|
||||
32
pkg/longest_consecutive_sequence/main_test.go
Normal file
32
pkg/longest_consecutive_sequence/main_test.go
Normal 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}))
|
||||
}
|
||||
63
pkg/merge_k_sorted_lists/main.go
Normal file
63
pkg/merge_k_sorted_lists/main.go
Normal 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
|
||||
}
|
||||
33
pkg/merge_two_sorted_lists/main.go
Normal file
33
pkg/merge_two_sorted_lists/main.go
Normal 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
|
||||
}
|
||||
95
pkg/minimum_window_substring/main.go
Normal file
95
pkg/minimum_window_substring/main.go
Normal 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
|
||||
}
|
||||
25
pkg/product_of_array_except_self/main.go
Normal file
25
pkg/product_of_array_except_self/main.go
Normal 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
|
||||
}
|
||||
16
pkg/product_of_array_except_self/main_test.go
Normal file
16
pkg/product_of_array_except_self/main_test.go
Normal 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}))
|
||||
}
|
||||
27
pkg/remove_nth_node_from_end_of_list/main.go
Normal file
27
pkg/remove_nth_node_from_end_of_list/main.go
Normal 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]
|
||||
}
|
||||
34
pkg/reorder_list/main.go
Normal file
34
pkg/reorder_list/main.go
Normal 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--
|
||||
}
|
||||
}
|
||||
19
pkg/reverse_linked_list/main.go
Normal file
19
pkg/reverse_linked_list/main.go
Normal 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
|
||||
}
|
||||
30
pkg/search_in_rotated_sorted_array/main.go
Normal file
30
pkg/search_in_rotated_sorted_array/main.go
Normal 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)
|
||||
}
|
||||
41
pkg/three_sum/main.go
Normal file
41
pkg/three_sum/main.go
Normal 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
|
||||
}
|
||||
26
pkg/three_sum/main_test.go
Normal file
26
pkg/three_sum/main_test.go
Normal 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}))
|
||||
}
|
||||
89
pkg/top_k_frequent_elements/main.go
Normal file
89
pkg/top_k_frequent_elements/main.go
Normal 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
|
||||
}
|
||||
20
pkg/top_k_frequent_elements/main_test.go
Normal file
20
pkg/top_k_frequent_elements/main_test.go
Normal 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))
|
||||
}
|
||||
31
pkg/trapping_rain_water/main.go
Normal file
31
pkg/trapping_rain_water/main.go
Normal 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
|
||||
}
|
||||
31
pkg/trapping_rain_water/main_test.go
Normal file
31
pkg/trapping_rain_water/main_test.go
Normal 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)
|
||||
}
|
||||
30
pkg/valid_palindrome/main.go
Normal file
30
pkg/valid_palindrome/main.go
Normal 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
|
||||
}
|
||||
14
pkg/valid_palindrome/main_test.go
Normal file
14
pkg/valid_palindrome/main_test.go
Normal 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(" "))
|
||||
}
|
||||
46
pkg/valid_sudoku/main.go
Normal file
46
pkg/valid_sudoku/main.go
Normal 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
|
||||
}
|
||||
56
pkg/valid_sudoku/main_test.go
Normal file
56
pkg/valid_sudoku/main_test.go
Normal 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))
|
||||
}
|
||||
Reference in New Issue
Block a user