Go 算法和数据结构¶
刷题过程中会遇到一些通用数据结构的封装,在这里记录一下。注意因为是刷算法题用的,没有考虑 goroutine 安全, 不要直接用在并发环境,如果是在生产环境中使用请加锁改造。(没有泛型写起来挺繁琐的)
Stack¶
type Stack struct {
st []int
}
func NewStack() *Stack {
return &Stack{st:make([]int,0)}
}
func (s *Stack) Push(x int) {
s.st = append(s.st, x)
}
func (s *Stack) Peek() int{
return s.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]
return top
}
func (s *Stack) Empty() bool{
return len(s.st) == 0
}
Queue¶
type Item struct {
Num int
Order int
}
type Queue struct {
items []Item
}
func NewQueue() *Queue {
return &Queue{
items: make([]Item, 0),
}
}
func (q *Queue) Push(x Item) {
q.items = append(q.items, x)
}
func (q *Queue) Pop() Item {
x := q.items[0]
q.items = q.items[1:]
return x
}
func (q *Queue) Front() Item {
return q.items[0]
}
func (q *Queue) End() Item {
return q.items[len(q.items)-1]
}
func (q *Queue) Empty() bool {
return len(q.items) == 0
}
Deque 双端队列¶
import (
"container/list"
"fmt"
)
// 滑动窗口最大值
type Deque struct {
ll *list.List
}
func NewDeque() *Deque {
return &Deque{ll: list.New()}
}
func (dq *Deque) PushFront(x int) {
dq.ll.PushFront(x)
}
func (dq *Deque) PushBack(x int) {
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 {
return dq.ll.Front().Value.(int)
}
func (dq *Deque) Back() int {
return dq.ll.Back().Value.(int)
}
func (dq *Deque) Len() int {
return dq.ll.Len()
}
Linked List¶
package main
import "fmt"
// 测试链表。在 redigo 里边使用到了链表作为 pool 的实现
type IntList struct {
count int
// front,back 分别指向第一个和最后一个 node,或者是 nil。front.prev back.next 都是空
front, back *Node
}
// 链表节点
type Node struct {
next, prev *Node
}
func (l *IntList) Count() int {
return l.count
}
func (l *IntList) pushFront(node *Node) {
node.next = l.front
node.prev = nil
if l.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--
if l.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--
if l.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
for cur != l.back {
fmt.Println(cur)
cur = cur.next
}
if l.back != nil {
fmt.Println(l.back)
}
}
Trie 字典树¶
// Package main provides ...
package main
import "fmt"
// https://golangbyexample.com/trie-implementation-in-go/
const (
ALBHABET_SIZE = 26
)
type node struct {
childrens [ALBHABET_SIZE]*node
isWordEnd bool
}
type trie struct {
root *node
}
func newTrie() *trie {
return &trie{
root: &node{},
}
}
func (t *trie) insert(word string) {
wordLength := len(word)
current := t.root
for i := 0; i < wordLength; i++ {
idx := word[i] - 'a'
if current.childrens[idx] == nil {
current.childrens[idx] = &node{}
}
current = current.childrens[idx]
}
current.isWordEnd = true
}
func (t *trie) find(word string) bool {
wordLength := len(word)
current := t.root
for i := 0; i < wordLength; i++ {
idx := word[i] - 'a'
if current.childrens[idx] == nil {
return false
}
current = current.childrens[idx]
}
if current.isWordEnd {
return true
}
return false
}
func main() {
trie := newTrie()
words := []string{"zhang", "wang", "li", "zhao"}
for i := 0; i < len(words); i++ {
trie.insert(words[i])
}
toFind := []string{"zhang", "wang", "li", "zhao", "gong"}
for i := 0; i < len(toFind); i++ {
c := toFind[i]
if trie.find(c) {
fmt.Printf("word[%s] found in trie.\n", c)
} else {
fmt.Printf("word[%s] not found in trie\n", c)
}
}
}
Lru Cache (不是并发安全的,仅示例)¶
package main
import "container/list"
type LRUCache struct {
lis *list.List
m map[int]*list.Element
capacity int
}
// 包装成一个 struct
type KV struct {
Key int
Val int
}
func Constructor(capacity int) LRUCache {
return LRUCache{
lis: list.New(),
m: make(map[int]*list.Element),
capacity: capacity,
}
}
func (this *LRUCache) Get(key int) int {
if ele, ok := this.m[key]; ok {
this.lis.MoveToFront(ele)
return ele.Value.(*KV).Val
}
return -1
}
func (this *LRUCache) Put(key int, value int) {
if ele, 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 保存的是 节点信息
if this.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 字典序。
package main
import (
"container/list"
"fmt"
)
// 按照 key 插入顺序遍历 map,类似 python collections.OrderedDict。注意不是 key 的字典序,而是插入顺序
type OrderedMap struct {
m map[string]int
me map[string]*list.Element
ll *list.List // 记录 key order
}
func NewOrderedMap() *OrderedMap {
return &OrderedMap{
m: make(map[string]int),
me: make(map[string]*list.Element),
ll: list.New(),
}
}
func (o *OrderedMap) Set(k string, v int) {
if _, found := o.m[k]; !found {
e := o.ll.PushBack(k)
o.me[k] = e
}
o.m[k] = v
}
func (o *OrderedMap) Exist(k string) bool {
_, found := o.m[k]
return found
}
func (o *OrderedMap) Get(k string) int {
return o.m[k]
}
func (o *OrderedMap) Delete(k string) {
delete(o.m, k)
node := o.me[k]
o.ll.Remove(node)
delete(o.me, k)
}
func (o *OrderedMap) Len() int {
return len(o.m)
}
// 按照 key 进入顺序返回
func (o *OrderedMap) Keys() []string {
keys := make([]string, o.ll.Len())
i := 0
for e := o.ll.Front(); e != nil; e = e.Next() {
keys[i] = e.Value.(string)
i++
}
return keys
}
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
package main
import (
"container/heap"
"fmt"
)
// An IntHeap is a min-heap of ints.
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 interface{}) {
// 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]
return x
}
// This example inserts several ints into an IntHeap, checks the minimum,
// and removes them in order of priority.
func main() {
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] 最小的元素
for h.Len() > 0 {
fmt.Printf("%d ", heap.Pop(h))
}
}
参考: