Go 算法和数据结构

刷题过程中会遇到一些通用数据结构的封装,在这里记录一下。注意因为是刷算法题用的,没有考虑 goroutine 安全, 不要直接用在并发环境,如果是在生产环境中使用请加锁改造。(没有泛型写起来挺繁琐的)

Stack

typeStackstruct{
st[]int
}
funcNewStack()*Stack{
return&Stack{st:make([]int,0)}
}
func(s*Stack)Push(xint){
s.st=append(s.st,x)
}
func(s*Stack)Peek()int{
returns.st[len(s.st)-1]
}
func(s*Stack)Pop()int{
n:=len(s.st)
top:=s.st[n-1]
s.st=s.st[:n-1]
returntop
}
func(s*Stack)Empty()bool{
returnlen(s.st)==0
}

Queue

typeItemstruct{
Numint
Orderint
}
typeQueuestruct{
items[]Item
}
funcNewQueue()*Queue{
return&Queue{
items:make([]Item,0),
}
}
func(q*Queue)Push(xItem){
q.items=append(q.items,x)
}
func(q*Queue)Pop()Item{
x:=q.items[0]
q.items=q.items[1:]
returnx
}
func(q*Queue)Front()Item{
returnq.items[0]
}
func(q*Queue)End()Item{
returnq.items[len(q.items)-1]
}
func(q*Queue)Empty()bool{
returnlen(q.items)==0
}

Deque 双端队列

import(
"container/list"
"fmt"
)
// 滑动窗口最大值
typeDequestruct{
ll*list.List
}
funcNewDeque()*Deque{
return&Deque{ll:list.New()}
}
func(dq*Deque)PushFront(xint){
dq.ll.PushFront(x)
}
func(dq*Deque)PushBack(xint){
dq.ll.PushBack(x)
}
func(dq*Deque)Pop(){// remove back
dq.ll.Remove(dq.ll.Back())
}
func(dq*Deque)PopFront(){// remove first
dq.ll.Remove(dq.ll.Front())
}
func(dq*Deque)Front()int{
returndq.ll.Front().Value.(int)
}
func(dq*Deque)Back()int{
returndq.ll.Back().Value.(int)
}
func(dq*Deque)Len()int{
returndq.ll.Len()
}

Linked List

packagemain
import"fmt"
// 测试链表。在 redigo 里边使用到了链表作为 pool 的实现
typeIntListstruct{
countint
// front,back 分别指向第一个和最后一个 node,或者是 nil。front.prev back.next 都是空
front,back*Node
}
// 链表节点
typeNodestruct{
next,prev*Node
}
func(l*IntList)Count()int{
returnl.count
}
func(l*IntList)pushFront(node*Node){
node.next=l.front
node.prev=nil
ifl.count==0{// note when list is empty
l.back=node
}else{
l.front.prev=node
}
l.front=node
l.count++
}
func(l*IntList)popFront(){
first:=l.front
l.count--
ifl.count==0{
l.front,l.back=nil,nil
}else{
first.next.prev=nil
l.front=first.next
}
first.next,first.prev=nil,nil// clear first
}
func(l*IntList)popBack(){
last:=l.back
l.count--
ifl.count==0{
l.front,l.back=nil,nil
}else{
last.prev.next=nil
l.back=last.prev
}
last.prev,last.next=nil,nil
}
func(l*IntList)Print(){
cur:=l.front
forcur!=l.back{
fmt.Println(cur)
cur=cur.next
}
ifl.back!=nil{
fmt.Println(l.back)
}
}

Trie 字典树

// Package main provides ...
packagemain
import"fmt"
// https://golangbyexample.com/trie-implementation-in-go/
const(
ALBHABET_SIZE=26
)
typenodestruct{
childrens[ALBHABET_SIZE]*node
isWordEndbool
}
typetriestruct{
root*node
}
funcnewTrie()*trie{
return&trie{
root:&node{},
}
}
func(t*trie)insert(wordstring){
wordLength:=len(word)
current:=t.root
fori:=0;i<wordLength;i++{
idx:=word[i]-'a'
ifcurrent.childrens[idx]==nil{
current.childrens[idx]=&node{}
}
current=current.childrens[idx]
}
current.isWordEnd=true
}
func(t*trie)find(wordstring)bool{
wordLength:=len(word)
current:=t.root
fori:=0;i<wordLength;i++{
idx:=word[i]-'a'
ifcurrent.childrens[idx]==nil{
returnfalse
}
current=current.childrens[idx]
}
ifcurrent.isWordEnd{
returntrue
}
returnfalse
}
funcmain(){
trie:=newTrie()
words:=[]string{"zhang","wang","li","zhao"}
fori:=0;i<len(words);i++{
trie.insert(words[i])
}
toFind:=[]string{"zhang","wang","li","zhao","gong"}
fori:=0;i<len(toFind);i++{
c:=toFind[i]
iftrie.find(c){
fmt.Printf("word[%s] found in trie.\n",c)
}else{
fmt.Printf("word[%s] not found in trie\n",c)
}
}
}

Lru Cache (不是并发安全的,仅示例)

packagemain
import"container/list"
typeLRUCachestruct{
lis*list.List
mmap[int]*list.Element
capacityint
}
// 包装成一个 struct
typeKVstruct{
Keyint
Valint
}
funcConstructor(capacityint)LRUCache{
returnLRUCache{
lis:list.New(),
m:make(map[int]*list.Element),
capacity:capacity,
}
}
func(this*LRUCache)Get(keyint)int{
ifele,ok:=this.m[key];ok{
this.lis.MoveToFront(ele)
returnele.Value.(*KV).Val
}
return-1
}
func(this*LRUCache)Put(keyint,valueint){
ifele,ok:=this.m[key];ok{
ele.Value.(*KV).Val=value
this.lis.MoveToFront(ele)
return
}
ele:=this.lis.PushFront(&KV{key,value})
this.m[key]=ele// map 保存的是 节点信息
ifthis.lis.Len()>this.capacity{
back:=this.lis.Back()
delete(this.m,back.Value.(*KV).Key)
this.lis.Remove(back)
}
}

OrderedMap (类似 python collections.OrderedDict)

模拟 python collections.OrderedDict 写的,可以方便的实现 lru 等。注意这里的 order 指的是 key 插入的顺序,不是指 key 字典序。

packagemain
import(
"container/list"
"fmt"
)
// 按照 key 插入顺序遍历 map,类似 python collections.OrderedDict。注意不是 key 的字典序,而是插入顺序
typeOrderedMapstruct{
mmap[string]int
memap[string]*list.Element
ll*list.List// 记录 key order
}
funcNewOrderedMap()*OrderedMap{
return&OrderedMap{
m:make(map[string]int),
me:make(map[string]*list.Element),
ll:list.New(),
}
}
func(o*OrderedMap)Set(kstring,vint){
if_,found:=o.m[k];!found{
e:=o.ll.PushBack(k)
o.me[k]=e
}
o.m[k]=v
}
func(o*OrderedMap)Exist(kstring)bool{
_,found:=o.m[k]
returnfound
}
func(o*OrderedMap)Get(kstring)int{
returno.m[k]
}
func(o*OrderedMap)Delete(kstring){
delete(o.m,k)
node:=o.me[k]
o.ll.Remove(node)
delete(o.me,k)
}
func(o*OrderedMap)Len()int{
returnlen(o.m)
}
// 按照 key 进入顺序返回
func(o*OrderedMap)Keys()[]string{
keys:=make([]string,o.ll.Len())
i:=0
fore:=o.ll.Front();e!=nil;e=e.Next(){
keys[i]=e.Value.(string)
i++
}
returnkeys
}

Heap 堆

go 自带了一个 container/heap 模块可以用来实现堆。

// This example demonstrates an integer heap built using the heap interface.
// A heap is a tree with the property that each node is the minimum-valued node in its subtree.
// 可以用来实现优先级队列 priority queue
packagemain
import(
"container/heap"
"fmt"
)
// An IntHeap is a min-heap of ints.
typeIntHeap[]int
func(hIntHeap)Len()int{returnlen(h)}
func(hIntHeap)Less(i,jint)bool{returnh[i]<h[j]}
func(hIntHeap)Swap(i,jint){h[i],h[j]=h[j],h[i]}
// 最后追加一个元素
func(h*IntHeap)Push(xinterface{}){
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h=append(*h,x.(int))
}
// 移除并且返回最后一个元素
func(h*IntHeap)Pop()interface{}{
old:=*h
n:=len(old)
x:=old[n-1]
*h=old[0:n-1]
returnx
}
// This example inserts several ints into an IntHeap, checks the minimum,
// and removes them in order of priority.
funcmain(){
h:=&IntHeap{2,1,5}
heap.Init(h)
fmt.Println(h)
heap.Push(h,3)
fmt.Println(h)
fmt.Printf("minimum: %d\n",(*h)[0])// h[0] 最小的元素
forh.Len()>0{
fmt.Printf("%d ",heap.Pop(h))
}
}

参考: