feat: progress
This commit is contained in:
106
pkg/questions/design_twitter/main.go
Normal file
106
pkg/questions/design_twitter/main.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package design_twitter
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"maps"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type TweetHeap [][]Tweet
|
||||
|
||||
func (h TweetHeap) Len() int { return len(h) }
|
||||
func (h TweetHeap) Less(i, j int) bool {
|
||||
timeI, timeJ := -1, -1
|
||||
|
||||
if len(h[i]) > 0 {
|
||||
timeI = h[i][len(h[i])-1].Time
|
||||
}
|
||||
if len(h[j]) > 0 {
|
||||
timeJ = h[j][len(h[i])-1].Time
|
||||
}
|
||||
|
||||
return timeI > timeJ
|
||||
}
|
||||
func (h TweetHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
func (h *TweetHeap) Push(x any) {
|
||||
*h = append(*h, x.([]Tweet))
|
||||
}
|
||||
|
||||
func (h *TweetHeap) Pop() any {
|
||||
old := *h
|
||||
x := old[len(old)-1]
|
||||
*h = old[:len(old)-1]
|
||||
return x
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
|
||||
type Tweet struct {
|
||||
ID int
|
||||
Time int
|
||||
}
|
||||
|
||||
type Twitter struct {
|
||||
tweets map[int][]Tweet
|
||||
follows map[int]map[int]bool
|
||||
counter int
|
||||
}
|
||||
|
||||
func Constructor() Twitter {
|
||||
return Twitter{
|
||||
tweets: map[int][]Tweet{},
|
||||
follows: map[int]map[int]bool{},
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Twitter) PostTweet(userId int, tweetId int) {
|
||||
t.counter++
|
||||
|
||||
t.tweets[userId] = append(t.tweets[userId], Tweet{
|
||||
ID: tweetId,
|
||||
Time: t.counter,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Twitter) GetNewsFeed(userId int) []int {
|
||||
users := slices.AppendSeq([]int{userId}, maps.Keys(t.follows[userId]))
|
||||
|
||||
hp := TweetHeap{}
|
||||
for _, user := range users {
|
||||
hp = append(hp, t.tweets[user])
|
||||
}
|
||||
heap.Init(&hp)
|
||||
|
||||
feed := []int{}
|
||||
for range 10 {
|
||||
if len(hp[0]) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
top, rest := hp[0][len(hp[0])-1], hp[0][:len(hp[0])-1]
|
||||
feed = append(feed, top.ID)
|
||||
hp[0] = rest
|
||||
|
||||
heap.Fix(&hp, 0)
|
||||
}
|
||||
|
||||
return feed
|
||||
}
|
||||
|
||||
func (t *Twitter) Follow(followerId int, followeeId int) {
|
||||
if _, ok := t.follows[followerId]; !ok {
|
||||
t.follows[followerId] = map[int]bool{}
|
||||
}
|
||||
|
||||
t.follows[followerId][followeeId] = true
|
||||
}
|
||||
|
||||
func (t *Twitter) Unfollow(followerId int, followeeId int) {
|
||||
if _, ok := t.follows[followerId]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
delete(t.follows[followerId], followeeId)
|
||||
}
|
||||
51
pkg/questions/kth_largest_element_in_stream/main.go
Normal file
51
pkg/questions/kth_largest_element_in_stream/main.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package kthlargestelementinstream
|
||||
|
||||
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 KthLargest struct {
|
||||
k int
|
||||
largest IntHeap
|
||||
}
|
||||
|
||||
func Constructor(k int, nums []int) KthLargest {
|
||||
result := KthLargest{k, IntHeap{}}
|
||||
heap.Init(&result.largest)
|
||||
|
||||
for _, n := range nums {
|
||||
result.Add(n)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *KthLargest) Add(val int) int {
|
||||
heap.Push(&this.largest, val)
|
||||
if this.largest.Len() > this.k {
|
||||
heap.Pop(&this.largest)
|
||||
}
|
||||
|
||||
return this.largest.Peek()
|
||||
}
|
||||
@@ -6,6 +6,13 @@ import (
|
||||
|
||||
func MaxSlidingWindow(nums []int, k int) []int {
|
||||
h := heap.NewBy(heap.More, nums[:k]...)
|
||||
result := []int{h.Data[0]}
|
||||
result := []int{h.Top()}
|
||||
|
||||
for i := range nums[k:] {
|
||||
h.Drop(nums[i])
|
||||
h.Put(nums[i+k])
|
||||
result = append(result, h.Top())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
65
pkg/questions/maximum_sliding_window/main_test.go
Normal file
65
pkg/questions/maximum_sliding_window/main_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package maximum_sliding_window
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMaxSlidingWindow(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nums []int
|
||||
k int
|
||||
want []int
|
||||
}{
|
||||
{
|
||||
name: "example 1",
|
||||
nums: []int{1, 3, -1, -3, 5, 3, 6, 7},
|
||||
k: 3,
|
||||
want: []int{3, 3, 5, 5, 6, 7},
|
||||
},
|
||||
{
|
||||
name: "example 2",
|
||||
nums: []int{1},
|
||||
k: 1,
|
||||
want: []int{1},
|
||||
},
|
||||
{
|
||||
name: "k equals length",
|
||||
nums: []int{4, 2, 7, 1},
|
||||
k: 4,
|
||||
want: []int{7},
|
||||
},
|
||||
{
|
||||
name: "all same",
|
||||
nums: []int{3, 3, 3, 3},
|
||||
k: 2,
|
||||
want: []int{3, 3, 3},
|
||||
},
|
||||
{
|
||||
name: "descending",
|
||||
nums: []int{5, 4, 3, 2, 1},
|
||||
k: 3,
|
||||
want: []int{5, 4, 3},
|
||||
},
|
||||
{
|
||||
name: "ascending",
|
||||
nums: []int{1, 2, 3, 4, 5},
|
||||
k: 3,
|
||||
want: []int{3, 4, 5},
|
||||
},
|
||||
{
|
||||
name: "negatives",
|
||||
nums: []int{-5, -3, -1, -2, -4},
|
||||
k: 2,
|
||||
want: []int{-3, -1, -1, -2},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, MaxSlidingWindow(tt.nums, tt.k))
|
||||
})
|
||||
}
|
||||
}
|
||||
115
pkg/questions/multiply_strings/main.go
Normal file
115
pkg/questions/multiply_strings/main.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package multiplystrings
|
||||
|
||||
func addDigits(a, b byte) (byte, byte) {
|
||||
abInt := int(a-'0') + int(b-'0')
|
||||
return byte(abInt/10) + '0', byte(abInt%10) + '0'
|
||||
}
|
||||
|
||||
func addByDigit(dst *[]byte, src byte) {
|
||||
for i, b := range *dst {
|
||||
if src == '0' {
|
||||
return
|
||||
}
|
||||
|
||||
src, (*dst)[i] = addDigits(b, src)
|
||||
}
|
||||
|
||||
if src != '0' {
|
||||
*dst = append(*dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
func add(dst *[]byte, src []byte) {
|
||||
carry := byte('0')
|
||||
|
||||
for i := range max(len(*dst), len(src)) {
|
||||
d, s := byte('0'), byte('0')
|
||||
if len(*dst) > i {
|
||||
d = (*dst)[i]
|
||||
}
|
||||
if len(src) > i {
|
||||
s = src[i]
|
||||
}
|
||||
|
||||
hi, lo := addDigits(d, s)
|
||||
total := []byte{lo, hi}
|
||||
addByDigit(&total, carry)
|
||||
(*dst)[i], carry = total[0], total[1]
|
||||
}
|
||||
|
||||
if carry != '0' {
|
||||
*dst = append(*dst, carry)
|
||||
}
|
||||
}
|
||||
|
||||
func multiplyDigits(a, b byte) (byte, byte) {
|
||||
abInt := int(a-'0') * int(b-'0')
|
||||
return byte(abInt/10) + '0', byte(abInt%10) + '0'
|
||||
}
|
||||
|
||||
func multiplyByDigit(and []byte, ier byte) []byte {
|
||||
result, carry := []byte{}, byte('0')
|
||||
for _, b := range and {
|
||||
hi, lo := multiplyDigits(b, ier)
|
||||
|
||||
// Fold the incoming carry into the low digit; any overflow rolls
|
||||
// into hi, which is small enough that it never overflows again.
|
||||
c, lo := addDigits(lo, carry)
|
||||
_, carry = addDigits(hi, c)
|
||||
|
||||
result = append(result, lo)
|
||||
}
|
||||
|
||||
if carry != '0' {
|
||||
result = append(result, carry)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func reverse(s string) []byte {
|
||||
digits := make([]byte, len(s))
|
||||
for i := range s {
|
||||
digits[len(s)-1-i] = s[i]
|
||||
}
|
||||
|
||||
return digits
|
||||
}
|
||||
|
||||
func multiply(a, b string) string {
|
||||
if a == "0" || b == "0" {
|
||||
return "0"
|
||||
}
|
||||
|
||||
and := reverse(a)
|
||||
result := make([]byte, len(a)+len(b))
|
||||
for i := range result {
|
||||
result[i] = '0'
|
||||
}
|
||||
|
||||
for i := range len(b) {
|
||||
ier := b[len(b)-1-i]
|
||||
partial := multiplyByDigit(and, ier)
|
||||
|
||||
// Shift left by i places (little-endian: i leading zeros).
|
||||
shifted := make([]byte, i+len(partial))
|
||||
for j := range i {
|
||||
shifted[j] = '0'
|
||||
}
|
||||
copy(shifted[i:], partial)
|
||||
|
||||
add(&result, shifted)
|
||||
}
|
||||
|
||||
end := len(result)
|
||||
for end > 1 && result[end-1] == '0' {
|
||||
end--
|
||||
}
|
||||
|
||||
out := make([]byte, end)
|
||||
for i := range end {
|
||||
out[i] = result[end-1-i]
|
||||
}
|
||||
|
||||
return string(out)
|
||||
}
|
||||
167
pkg/questions/n_queens/main.go
Normal file
167
pkg/questions/n_queens/main.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package n_queens
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type board [][]bool
|
||||
|
||||
func newBoard(size int) board {
|
||||
b := make([][]bool, size)
|
||||
for i := range b {
|
||||
b[i] = make([]bool, size)
|
||||
}
|
||||
|
||||
return board(b)
|
||||
}
|
||||
|
||||
func (b board) size() (int, int) {
|
||||
return len(b[0]), len(b)
|
||||
}
|
||||
|
||||
func (b board) get(x, y int) bool {
|
||||
return b[y][x]
|
||||
}
|
||||
|
||||
func (b board) has(x, y int) bool {
|
||||
width, height := b.size()
|
||||
return x >= 0 && y >= 0 && x < width && y < height
|
||||
}
|
||||
|
||||
func (b board) set(x, y int, value bool) {
|
||||
b[y][x] = value
|
||||
}
|
||||
|
||||
type position struct{ x, y int }
|
||||
|
||||
type step struct {
|
||||
position int
|
||||
blocked []position
|
||||
}
|
||||
|
||||
type state struct {
|
||||
board board
|
||||
steps []step
|
||||
n int
|
||||
}
|
||||
|
||||
func newState(n int) *state {
|
||||
return &state{
|
||||
board: newBoard(n),
|
||||
n: n,
|
||||
steps: make([]step, 0, n),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *state) options() []int {
|
||||
y := len(s.steps)
|
||||
if y >= s.n {
|
||||
return []int{}
|
||||
}
|
||||
|
||||
results := []int{}
|
||||
for x := range s.n {
|
||||
if s.board.get(x, y) {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, x)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func (s *state) forward(x int) {
|
||||
blocked := []position{}
|
||||
y := len(s.steps)
|
||||
|
||||
// Vertical
|
||||
for i := range s.n {
|
||||
if !s.board.get(x, i) {
|
||||
s.board.set(x, i, true)
|
||||
blocked = append(blocked, position{x, i})
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal
|
||||
for i := range s.n {
|
||||
if !s.board.get(i, y) {
|
||||
s.board.set(i, y, true)
|
||||
blocked = append(blocked, position{i, y})
|
||||
}
|
||||
}
|
||||
|
||||
// Forward vertical
|
||||
for offset := range s.n {
|
||||
p := position{offset, y + x - offset}
|
||||
|
||||
if s.board.has(p.x, p.y) && !s.board.get(p.x, p.y) {
|
||||
s.board.set(p.x, p.y, true)
|
||||
blocked = append(blocked, p)
|
||||
}
|
||||
}
|
||||
|
||||
// Backward vertical
|
||||
for offset := range s.n {
|
||||
p := position{offset, y - x + offset}
|
||||
|
||||
if s.board.has(p.x, p.y) && !s.board.get(p.x, p.y) {
|
||||
s.board.set(p.x, p.y, true)
|
||||
blocked = append(blocked, p)
|
||||
}
|
||||
}
|
||||
|
||||
s.steps = append(s.steps, step{
|
||||
position: x,
|
||||
blocked: blocked,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *state) backward() {
|
||||
top, rest := s.steps[len(s.steps)-1], s.steps[:len(s.steps)-1]
|
||||
s.steps = rest
|
||||
|
||||
for _, p := range top.blocked {
|
||||
s.board.set(p.x, p.y, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *state) solution() ([]string, bool) {
|
||||
if len(s.steps) != s.n {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
blank := bytes.Repeat([]byte{'.'}, s.n)
|
||||
|
||||
solution := make([]string, s.n)
|
||||
for i := range solution {
|
||||
row := bytes.Clone(blank)
|
||||
row[s.steps[i].position] = 'Q'
|
||||
solution[i] = string(row)
|
||||
}
|
||||
|
||||
return solution, true
|
||||
}
|
||||
|
||||
func SolveNQueens(n int) [][]string {
|
||||
s := newState(n)
|
||||
solutions := [][]string{}
|
||||
|
||||
var dfs func()
|
||||
dfs = func() {
|
||||
if soln, ok := s.solution(); ok {
|
||||
solutions = append(solutions, soln)
|
||||
return
|
||||
}
|
||||
|
||||
for _, opt := range s.options() {
|
||||
s.forward(opt)
|
||||
dfs()
|
||||
s.backward()
|
||||
}
|
||||
}
|
||||
|
||||
dfs()
|
||||
|
||||
return solutions
|
||||
}
|
||||
60
pkg/questions/word_ladder/main.go
Normal file
60
pkg/questions/word_ladder/main.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package wordladder
|
||||
|
||||
import "iter"
|
||||
|
||||
type wordSet map[string][]string
|
||||
|
||||
func (w wordSet) add(word string) {
|
||||
buffer := []byte(word)
|
||||
for i := range word {
|
||||
buffer[i] = '*'
|
||||
w[string(buffer)] = append(w[string(buffer)], word)
|
||||
buffer[i] = word[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (w wordSet) neighborsOf(word string) iter.Seq[string] {
|
||||
return func(yield func(string) bool) {
|
||||
buffer := []byte(word)
|
||||
for i := range word {
|
||||
buffer[i] = '*'
|
||||
for _, neighbor := range w[string(buffer)] {
|
||||
yield(neighbor)
|
||||
}
|
||||
buffer[i] = word[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ladderLength(beginWord string, endWord string, wordList []string) int {
|
||||
ws := wordSet{}
|
||||
for _, word := range wordList {
|
||||
ws.add(word)
|
||||
}
|
||||
|
||||
idx, iteration, visited := 0, []string{beginWord}, map[string]bool{}
|
||||
visited[beginWord] = true
|
||||
|
||||
for {
|
||||
nextIteration := []string{}
|
||||
for _, it := range iteration {
|
||||
if it == endWord {
|
||||
return idx
|
||||
}
|
||||
|
||||
for neighbor := range ws.neighborsOf(it) {
|
||||
if visited[neighbor] {
|
||||
continue
|
||||
}
|
||||
|
||||
visited[neighbor] = true
|
||||
nextIteration = append(nextIteration, neighbor)
|
||||
}
|
||||
}
|
||||
|
||||
iteration = nextIteration
|
||||
idx++
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -3,10 +3,12 @@ package heap
|
||||
import (
|
||||
"cmp"
|
||||
"container/heap"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type Heap[T any] struct {
|
||||
type Heap[T comparable] struct {
|
||||
Data []T
|
||||
Index map[T]map[int]bool
|
||||
lessFunc func(a, b T) bool
|
||||
}
|
||||
|
||||
@@ -14,22 +16,98 @@ func More[T cmp.Ordered](x, y T) bool {
|
||||
return !cmp.Less(x, y) && x != y
|
||||
}
|
||||
|
||||
func NewBy[T any](lessFunc func(a, b T) bool, items ...T) *Heap[T] {
|
||||
h := &Heap[T]{items, lessFunc}
|
||||
func NewBy[T comparable](lessFunc func(a, b T) bool, items ...T) *Heap[T] {
|
||||
h := &Heap[T]{slices.Clone(items), map[T]map[int]bool{}, lessFunc}
|
||||
heap.Init(h)
|
||||
|
||||
for i, v := range h.Data {
|
||||
if h.Index[v] == nil {
|
||||
h.Index[v] = map[int]bool{}
|
||||
}
|
||||
|
||||
h.Index[v][i] = true
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func New[T cmp.Ordered](items ...T) *Heap[T] {
|
||||
return NewBy[T](func(a, b T) bool { return a < b }, items...)
|
||||
return NewBy(func(a, b T) bool { return a < b }, items...)
|
||||
}
|
||||
|
||||
func (h *Heap[T]) putIndex(item T, index int) {
|
||||
if h.Index == nil {
|
||||
h.Index = map[T]map[int]bool{}
|
||||
}
|
||||
|
||||
if h.Index[item] == nil {
|
||||
h.Index[item] = map[int]bool{}
|
||||
}
|
||||
|
||||
h.Index[item][index] = true
|
||||
}
|
||||
|
||||
func (h *Heap[T]) dropIndex(item T, index int) {
|
||||
if h.Index == nil {
|
||||
h.Index = map[T]map[int]bool{}
|
||||
}
|
||||
|
||||
if h.Index[item] == nil {
|
||||
h.Index[item] = map[int]bool{}
|
||||
}
|
||||
|
||||
delete(h.Index[item], index)
|
||||
}
|
||||
|
||||
func (h Heap[T]) Len() int { return len(h.Data) }
|
||||
func (h Heap[T]) Less(i, j int) bool { return h.lessFunc(h.Data[i], h.Data[j]) }
|
||||
func (h Heap[T]) Swap(i, j int) { h.Data[i], h.Data[j] = h.Data[j], h.Data[i] }
|
||||
func (h Heap[T]) Swap(i, j int) {
|
||||
h.dropIndex(h.Data[i], i)
|
||||
h.dropIndex(h.Data[j], j)
|
||||
|
||||
func (h *Heap[T]) Push(x any) { h.Data = append(h.Data, x.(T)) }
|
||||
func (h *Heap[T]) Pop() any {
|
||||
defer func() { h.Data = h.Data[:len(h.Data)-1] }()
|
||||
return h.Data[len(h.Data)-1]
|
||||
h.Data[i], h.Data[j] = h.Data[j], h.Data[i]
|
||||
|
||||
h.putIndex(h.Data[i], i)
|
||||
h.putIndex(h.Data[j], j)
|
||||
}
|
||||
|
||||
func (h *Heap[T]) Push(x any) {
|
||||
datum := x.(T)
|
||||
h.Data = append(h.Data, datum)
|
||||
h.putIndex(datum, len(h.Data)-1)
|
||||
}
|
||||
|
||||
func (h *Heap[T]) Pop() any {
|
||||
index := len(h.Data) - 1
|
||||
datum := h.Data[index]
|
||||
h.dropIndex(datum, index)
|
||||
|
||||
h.Data = h.Data[:index]
|
||||
return datum
|
||||
}
|
||||
|
||||
func (h *Heap[T]) GetIndex(item T) int {
|
||||
if h.Index[item] == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
var key int
|
||||
for k := range h.Index[item] {
|
||||
key = k
|
||||
break
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func (h *Heap[T]) Drop(item T) {
|
||||
heap.Remove(h, h.GetIndex(item))
|
||||
}
|
||||
|
||||
func (h *Heap[T]) Put(item T) {
|
||||
heap.Push(h, item)
|
||||
}
|
||||
|
||||
func (h *Heap[T]) Top() T {
|
||||
return h.Data[0]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user