Go基础:list的介绍与使用
介绍
Go语言内置容器list是一个双向链表(实际是一个环)。位于包list当中。
结构体定义
list的核心结构体一共包含两个List和Element。
List
List的结构体如下:
type List struct {
root Element // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}
List的结构体包含两个部分:
root:类型为Element的结构体。len:用于记录List的长度(除去哨兵节点)。
List的第一个节点为哨兵节点,不存储任何信息。List的头从哨兵节点的下一个节点开始。
Element
List中Element的结构体如下:
type Element struct {
list *List
Value interface{}
next, prev *Element
}
从节点的结构体可以看出,list中的一个节点包含四个元素:
list:指向List的指针,用于标识当前节点属于哪一个LISTValue:用于存储元素的值。next,prev: 指向Element的指针,用于定位后一个节点与前一个节点。
为了简化实现,List的内部实现为一个环,这样l.root既是最后一个list元素(l.Back())的下一个元素,也是第一个list元素(l.Front())的上一个元素。
方法一览
下面将方法分为六个大类分别讲解:
- 初始化方法
- 基本方法
- 插入元素
- 删除元素
- 移动元素
- 合并链表
初始化
初始化的方法有两个:Init()和lazyInit()。
Init()方法可以用来初始化一个链表,或者是清空链表。
lazyInit()方法懒初始化一个零值的链表,从实现上可以看出,只有当哨兵节点的next节点为空时,才会初始化链表。
func (l *List) Init() *List {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
return l
}
func (l *List) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
基础方法
Next():返回当前节点的下一个节点或者nil。从实现可以看出,当list为空或者下一个节点为哨兵节点时,返回nil。Prev():返回当前节点的上一个节点或者nil,跟上面类似。New():返回一个初始化完成的链表。Len():回链表中的元素个数,时间复杂度为O(1)。Front():返回链表的第一个元素。当链表为空时,返回nil。Back():返回链表的最后一个元素。当链表为空时,返回nil。
func (e *Element) Next() *Element {
if p := e.next; e.list != nil && p != &e.list.root {
return p
}
return nil
}
func (e *Element) Prev() *Element {
if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
}
func New() *List { return new(List).Init() }
func (l *List) Len() int { return l.len }
func (l *List) Front() *Element {
if l.len == 0 {
return nil
}
return l.root.next
}
func (l *List) Back() *Element {
if l.len == 0 {
return nil
}
return l.root.prev
}
插入元素
insert():未导出函数。在元素at的位置后面插入一个元素,链表长度加一,返回插入的元素。insertValue():未导出函数。将Value包装成Element然后进行insert()。InsertBefore():调用insertValue()在元素mark前插入。要求元素mark属于调用的链表对象且不为nil。InsertAfter():与InsertBefore()类似,在元素mark后插入。PushFront():调用insertValue()方法在表头插入。返回插入的元素。PushBack():调用insertValue()方法在表尾插入。返回插入的元素。
func (l *List) insert(e, at *Element) *Element {
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
e.list = l
l.len++
return e
}
func (l *List) insertValue(v interface{}, at *Element) *Element {
return l.insert(&Element{Value: v}, at)
}
func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
if mark.list != l {
return nil
}
return l.insertValue(v, mark.prev)
}
func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
if mark.list != l {
return nil
}
return l.insertValue(v, mark)
}
func (l *List) PushFront(v interface{}) *Element {
l.lazyInit()
return l.insertValue(v, &l.root)
}
func (l *List) PushBack(v interface{}) *Element {
l.lazyInit()
return l.insertValue(v, l.root.prev)
}
删除元素
remove():未导出函数。从链表中删除元素e,链表长度减一,返回被删除的元素。Remove():当元素e属于被调用链表的元素时,调用remove()函数,返回e.Value。
func (l *List) remove(e *Element) *Element {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil // 避免内存泄漏
e.prev = nil // 避免内存泄漏
e.list = nil
l.len--
return e
}
func (l *List) Remove(e *Element) interface{} {
if e.list == l {
l.remove(e)
}
return e.Value
}
移动元素
move():未导出函数。将元素e移至元素at的后面,返回元素e。MoveToFront():将元素e移至表头。要求元素e属于被调用链表的元素。MoveToBack():将元素e移至表尾。要求元素e属于被调用链表的元素。MoveBefore():将元素e移至元素mark的前面。要求元素e属于被调用链表的元素且不为元素mark。MoveAfter():与MoveBefore()类似,移至其后面。
func (l *List) move(e, at *Element) *Element {
if e == at {
return e
}
e.prev.next = e.next
e.next.prev = e.prev
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
return e
}
func (l *List) MoveToFront(e *Element) {
if e.list != l || l.root.next == e {
return
}
l.move(e, &l.root)
}
func (l *List) MoveToBack(e *Element) {
if e.list != l || l.root.prev == e {
return
}
l.move(e, l.root.prev)
}
func (l *List) MoveBefore(e, mark *Element) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark.prev)
}
func (l *List) MoveAfter(e, mark *Element) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark)
}
合并链表
PushBackList():将另一个链表浅拷贝到当前链表的末尾。要求两个链表均不为nil。PushFronList():将另一个链表浅拷贝到当前链表的开头。要求两个链表均不为nil。
func (l *List) PushBackList(other *List) {
l.lazyInit()
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
l.insertValue(e.Value, l.root.prev)
}
}
func (l *List) PushFrontList(other *List) {
l.lazyInit()
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
l.insertValue(e.Value, &l.root)
}
}
使用
下面从三个方面来讲解list的使用:
- 基本操作
- 模拟栈
- 模拟队列
基本操作
下面演示一下链表的基本使用:
l := list.New()
e4 := l.PushBack(4)
e1 := l.PushFront(1)
l.InsertBefore(3, e4)
l.InsertAfter(2, e1)
// 遍历输出链表内容
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
输出结果如下:
1
2
3
4
栈
我们知道栈有三个基本操作:Push()、Pop()和Top(),分别是向栈顶压入,弹出,和查看栈顶操作。我们可以用list的方法来模拟这三个基本操作:s.PushBack()、s.Back().Value和s.Remove(s.Back())。
stack := list.New()
stack.PushBack(1)
stack.PushBack(2)
stack.PushBack(3)
stack.PushBack(4)
for stack.Len() > 0 {
fmt.Println(stack.Remove(stack.Back()))
}
输出如下:
4
3
2
1
队列
同样,我们还可以用q.PushBack()与q.Remove(q.Front())来模拟队列的进度与出队操作。
queue := list.New()
queue.PushBack(1)
queue.PushBack(2)
queue.PushBack(3)
queue.PushBack(4)
for l.Len() > 0 {
fmt.Println(l.Remove(l.Front()))
}
输出如下:
4
3
2
1