1.list基本使用
Go 语言的链表实现在其标准库的 container/list代码包中,这个包包含了两个公开的程序实体:List和Element,前者实现了一个双向链表,而后者则代表了链表中元素的结构。
package main
import (
"container/list"
"fmt"
)
func main() {
//initialize a list
l := list.New()
fmt.Println(l)
//add items to the list
l.PushFront(10)
fmt.Println("the front element: ", l.Front())
l.PushBack(11)
fmt.Println("the back element: ", l.Back())
l.PushBack(12)
l.PushBack(13)
fmt.Println("the back element: ", l.Back())
//traverse the list
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
fmt.Println("Traverse the list in reverse order")
for e := l.Back(); e != nil; e = e.Prev() {
fmt.Println(e.Value)
}
//remove items from the list
v := l.Back()
l.Remove(v)
fmt.Println("the back element: ", l.Back())
//insert item
v1 := l.Front()
v2 := l.InsertBefore(8, v1)
fmt.Println("the front element: ", v2.Value)
v3 := l.InsertAfter(9, v2)
fmt.Println("the second element: ", v3.Value)
fmt.Println("the length of the list: ", l.Len())
//move the item
fmt.Println("before move items")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
back := l.Back()
l.MoveAfter(v2, back)
l.MoveBefore(v3, back)
fmt.Println("after move item")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
l.MoveToFront(v2)
l.MoveToBack(v3)
fmt.Println("after move item")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
l1 := list.New()
l1.PushFront(1)
l1.PushBack(2)
//append another list
l.PushBackList(l1)
fmt.Println("apppend")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
l.PushFrontList(l1)
fmt.Println("apppend")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
}
2.ring基本使用
container/ring包中的Ring类型实现的是一个循环链表,也就是我们俗称的环。其实List 在内部就是一个循环链表。它的根元素永远不会持有任何实际的元素值,而该元素的存在,就是为了连接这个循环链表的首尾两端。下面图中对ring的描述源于源码:
package main
import (
"container/ring"
"fmt"
)
func main() {
//initialize a ring
r := ring.New(5)
len := r.Len()
fmt.Println("the length of ring is: ", len)
//initialize the ring
for i := 0; i < len; i++ {
r.Value = i
r = r.Next()
}
fmt.Println("ring r before move")
//iterate through the ring and print its contents
r.Do(func(i interface{}) {
fmt.Println(i.(int))
})
//move the pointer forward by one step
r = r.Move(1)
fmt.Println("ring r after move")
r.Do(func(i interface{}) {
fmt.Println(i.(int))
})
//move the pointer back by two steps
r = r.Move(-2)
fmt.Println("ring r after move")
r.Do(func(i interface{}) {
fmt.Println(i.(int))
})
//unlink two elements from r, starting from r.Next()
r.Unlink(2)
fmt.Println("ring r after unlink")
r.Do(func(i interface{}) {
fmt.Println(i.(int))
})
s := ring.New(2)
len1 := s.Len()
for i := 0; i < len1; i++ {
s.Value = i
s = s.Next()
}
fmt.Println("ring: rs")
//link ring r and ring s
rs := r.Link(s)
rs.Do(func(i interface{}) {
fmt.Println(i.(int))
})
}
3.Ring和List的区别
- Ring类型的数据结构仅由它自身即可代表,而List类型则需要由它以及Element类型联 合表示。这是表示方式上的不同,也是结构复杂度上的不同。
- 一个Ring类型的值严格来讲,只代表了其所属的循环链表中的一个元素,而一个List类型 的值则代表了一个完整的链表。这是表示维度上的不同。
- 在创建并初始化一个Ring值的时候,我们可以指定它包含的元素的数量,但是对于一个 List值来说,却不能这样做(也没有必要这样做)。循环链表一旦被创建,其长度是不可 变的。这是两个代码包中的New函数在功能上的不同,也是两个类型在初始化值方面的第一 个不同。
- 仅通过var r ring.Ring语句声明的r将会是一个长度为1的循环链表,而List类型的零 值则是一个长度为0的链表。别忘了List中的根元素不会持有实际元素值,因此计算长度时 不会包含它。这是两个类型在初始化值方面的第二个不同。
- Ring值的Len方法的算法复杂度是 O(N) 的,而List值的Len方法的算法复杂度则是 O(1) 的。这是两者在性能方面最显而易见的差别。
4.heap
heap示例源于官网
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
}
func main() {
h := &IntHeap{2, 1, 5}
heap.Init(h)
heap.Push(h, 3)
fmt.Printf("minimum: %d\n", (*h)[0])
for h.Len() > 0 {
fmt.Printf("%d ", heap.Pop(h))
}
}
package main
import (
"container/heap"
"fmt"
)
// An Item is something we manage in a priority queue.
type Item struct {
value string // The value of the item; arbitrary.
priority int // The priority of the item in the queue.
// The index is needed by update and is maintained by the heap.Interface methods.
index int // The index of the item in the heap.
}
// A PriorityQueue implements heap.Interface and holds Items.
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
return pq[i].priority > pq[j].priority
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
old[n-1] = nil // avoid memory leak
item.index = -1 // for safety
*pq = old[0 : n-1]
return item
}
// update modifies the priority and value of an Item in the queue.
func (pq *PriorityQueue) update(item *Item, value string, priority int) {
item.value = value
item.priority = priority
heap.Fix(pq, item.index)
}
// This example creates a PriorityQueue with some items, adds and manipulates an item,
// and then removes the items in priority order.
func main() {
// Some items and their priorities.
items := map[string]int{
"banana": 3, "apple": 2, "pear": 4,
}
// Create a priority queue, put the items in it, and
// establish the priority queue (heap) invariants.
pq := make(PriorityQueue, len(items))
i := 0
for value, priority := range items {
pq[i] = &Item{
value: value,
priority: priority,
index: i,
}
i++
}
heap.Init(&pq)
// Insert a new item and then modify its priority.
item := &Item{
value: "orange",
priority: 1,
}
heap.Push(&pq, item)
pq.update(item, item.value, 5)
// Take the items out; they arrive in decreasing priority order.
for pq.Len() > 0 {
item := heap.Pop(&pq).(*Item)
fmt.Printf("%.2d:%s ", item.priority, item.value)
}
}
本节示例代码的地址:demo GitHub