文章目录
- 数据结构与算法
- 链表
- 栈
数据结构与算法
链表
---手写单链表
type Node struct {
Info int
Next *Node
}
type List struct {
Head *Node
Len int
Tail *Node
}
func (list *List) Add(ele int) {
node := &Node{Info: ele, Next: nil}
if list.Len == 0 {
list.Head = node
list.Tail = node //尾指针
} else {
// head:=list.Head
// for i:=1;i<list.Len;i++{
// head = head.Next
// }
list.Tail.Next = node
list.Tail = node
}
list.Len += 1
}
func (list List) Travs() {
if list.Len == 0 {
return
}
head := list.Head
fmt.Printf("%d ", head.Info)
for i := 1; i < list.Len; i++ {
head = head.Next
fmt.Printf("%d ", head.Info)
}
}
链表的一个应用案例。LRU(Least Recently Used, 最近最少使用)缓存淘汰的总体思路:缓存的key放到链表中,头部的元素表示最近刚使用。
-
如果命中缓存,从链表中找到对应的key,移到链表头部。
-
如果没命中缓存:
-
如果缓存容量没超,放入缓存,并把key放到链表头部。
-
如果超出缓存容量,删除链表尾部元素,再把key放到链表头部。
-
基于手写双链表粗略实现LRU算法
---基于双链表实现LRU算法(TODO:往链表中添加元素时的判断) type Node struct { Info int Next *Node Prev *Node } type List struct { Head *Node Len int Tail *Node } func (list *List) Add(ele int) { node := &Node{Info: ele, Next: nil, Prev: nil} if list.Len == 0 { list.Head = node list.Tail = node //尾指针 } else { list.Tail.Next = node node.Prev = list.Tail list.Tail = node } list.Len += 1 } func (list List) Travs() { if list.Len == 0 { return } head := list.Head fmt.Printf("%d ", head.Info) for i := 1; i < list.Len; i++ { head = head.Next fmt.Printf("%d ", head.Info) } fmt.Println() } func (list *List) Visit(ele int) { if list.Len == 0 { return } head := list.Head for { if head == nil { break } if head.Info != ele { head = head.Next } else { break } } if head == nil { return } else { // 1-->8-->7-->5 改为7-->1-->8-->5 post := head.Next //5 pre := head.Prev //8 pre.Next = post //8-->5 post.Prev = pre //5-->8 head.Next = list.Head // 7-->1 list.Head.Prev = head // 1-->7 list.Head = head //7成链表的头部 } }
-
go list的使用
type Element
type Element struct {
// 元素保管的值
Value interface{}
// 内含隐藏或非导出字段
}
Element类型代表是双向链表的一个元素。
func (*Element) Next
func (e *Element) Next() *Element
Next返回链表的后一个元素或者nil。
func (*Element) Prev
func (e *Element) Prev() *Element
Prev返回链表的前一个元素或者nil。
type List
type List struct {
// 内含隐藏或非导出字段
}
List代表一个双向链表。List零值为一个空的、可用的链表。
func New
func New() *List
New创建一个链表。
func (*List) Init
func (l *List) Init() *List
Init清空链表。
func (*List) Len
func (l *List) Len() int
Len返回链表中元素的个数,复杂度O(1)。
func (*List) Front
func (l *List) Front() *Element
Front返回链表第一个元素或nil。
func (*List) Back
func (l *List) Back() *Element
Back返回链表最后一个元素或nil。
func (*List) PushFront
func (l *List) PushFront(v interface{}) *Element
PushBack将一个值为v的新元素插入链表的第一个位置,返回生成的新元素。
func (*List) PushFrontList
func (l *List) PushFrontList(other *List)
PushFrontList创建链表other的拷贝,并将拷贝的最后一个位置连接到链表l的第一个位置。
func (*List) PushBack
func (l *List) PushBack(v interface{}) *Element
PushBack将一个值为v的新元素插入链表的最后一个位置,返回生成的新元素。
func (*List) PushBackList
func (l *List) PushBackList(other *List)
PushBack创建链表other的拷贝,并将链表l的最后一个位置连接到拷贝的第一个位置。
func (*List) InsertBefore
func (l *List) InsertBefore(v interface{}, mark *Element) *Element
InsertBefore将一个值为v的新元素插入到mark前面,并返回生成的新元素。如果mark不是l的元素,l不会被修改。
func (*List) InsertAfter
func (l *List) InsertAfter(v interface{}, mark *Element) *Element
InsertAfter将一个值为v的新元素插入到mark后面,并返回新生成的元素。如果mark不是l的元素,l不会被修改。
func (*List) MoveToFront
func (l *List) MoveToFront(e *Element)
MoveToFront将元素e移动到链表的第一个位置,如果e不是l的元素,l不会被修改。
func (*List) MoveToBack
func (l *List) MoveToBack(e *Element)
MoveToBack将元素e移动到链表的最后一个位置,如果e不是l的元素,l不会被修改。
func (*List) MoveBefore
func (l *List) MoveBefore(e, mark *Element)
MoveBefore将元素e移动到mark的前面。如果e或mark不是l的元素,或者e==mark,l不会被修改。
func (*List) MoveAfter
func (l *List) MoveAfter(e, mark *Element)
MoveAfter将元素e移动到mark的后面。如果e或mark不是l的元素,或者e==mark,l不会被修改。
func (*List) Remove
func (l *List) Remove(e *Element) interface{}
Remove删除链表中的元素e,并返回e.Value。
lst := list.New() // 创建一个空的双向链表
lst.PushBack(4)
lst.PushBack(6)
lst.PushBack(2)
lst.PushFront(9)//往链表头部添加
TravsList(lst)// 9 4 6 2
func TravsList(lst *list.List) {
head := lst.Front()
for head.Next() != nil {
fmt.Printf("%v ", head.Value)
head = head.Next()
}
fmt.Printf("%v \n", head.Value)
}
ring的应用:基于滑动窗口的统计。比如最近100次接口调用的平均耗时、最近10笔订单的平均值、最近30个交易日股票的最高点。ring的容量即为滑动窗口的大小,把待观察变量按时间顺序不停地写入ring即可。
package main
import (
"container/ring"
"fmt"
)
func TraverseRing(ring *ring.Ring) {
ring.Do(func(i interface{}) { //通过Do()来遍历ring,内部实际上调用了Next()而非Prev()
fmt.Printf("%v ", i)
})
fmt.Println()
}
func main() {
ring := ring.New(5) //必须指定长度,各元素被初始化为nil
ring2 := ring.Prev()
for i := 0; i < 3; i++ {
ring.Value = i
ring = ring.Next()
}
for i := 0; i < 3; i++ {
ring2.Value = i
ring2 = ring2.Prev()
}
TraverseRing(ring)
TraverseRing(ring2) //ring和ring2当前所在的指针位置不同,所以遍历出来的顺序也不同
}
栈
栈是一种先进后出的数据结构,push把元素压入栈底,pop弹出栈顶的元素。编程语言的编译系统也用到了栈的思想。
go自带的List已经包含了栈的功能,这里实现一个线程安全的栈。
type (
node struct {
value interface{}
prev *node
}
MyStack struct {
top *node
length int
lock *sync.RWMutex
}
)
func NewMyStack() *MyStack {
return &MyStack{nil, 0, &sync.RWMutex{}}
}
func (stack *MyStack) Push(value interface{}) {
stack.lock.Lock()
defer stack.lock.Unlock()
n := &node{value, stack.top}
stack.top = n
stack.length++
}
func (stack *MyStack) Pop() interface{} {
stack.lock.Lock()
defer stack.lock.Unlock()
if stack.length == 0 {
return nil
}
n := stack.top
stack.top = n.prev
stack.length--
return n.value
}
func (stack *MyStack) Peak() interface{} {
stack.lock.RLock()
defer stack.lock.RUnlock()
if stack.length == 0 {
return nil
}
return stack.top.value
}
func (stack *MyStack) Len() int {
return stack.length
}
func (stack *MyStack) Empty() bool {
return stack.Len() == 0
}