双向链表/list
Posted miaoweiye
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双向链表/list相关的知识,希望对你有一定的参考价值。
-
-
第一个元素称为头(head)元素,前连接(前置指针域)为nil
-
最后一个元素称为尾(foot)元素,后连接(后置指针域)为nil
-
-
双向链表的优点:
-
在执行新增元素或删除元素时效率高,获取任意一个元素,可以方便的在这个元素前后插入元素
-
充分利用内存空间,实现内存灵活管理
-
可实现正序和逆序遍历
-
头元素和尾元素新增或删除时效率较高
-
-
双向链表的缺点
-
链表增加了元素的指针域,空间开销比较大
-
-
双向链表容器List
在Go语言标准库的container/list 包提供了双向链表List
-
List结构体定义如下
-
root表示根元素
-
len表示链表中有多少个元素
-
// List represents a doubly linked list. // The zero value for List is an empty list ready to use. 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 }
-
-
next表示下一个元素,使用Next()可以获取到
-
prev表示上一个元素,使用Prev()可以获取到
-
list表示元素属于哪个链表
-
-
// Element is an element of a linked list. type Element struct { // Next and previous pointers in the doubly-linked list of elements. // To simplify the implementation, internally a list l is implemented // as a ring, such that &l.root is both the next element of the last // list element (l.Back()) and the previous element of the first list // element (l.Front()). next, prev *Element // The list to which this element belongs. list *List // The value stored with this element. Value interface{} }
操作List
-
直接使用container/list包下的New()新建一个空的List
mylist := list.New() fmt.Println(mylist) //输出list中内容 fmt.Println(mylist.Len()) //查看链表中元素的个数 fmt.Printf("%p", mylist) //输出地址
- Go语言标准库中提供了很多向双向链表中添加元素的函数
//添加到最后,List["a"] mylist.PushBack("a") //添加到最前面,List["b","a"] mylist.PushFront("b") //向第一个元素后面添加元素,List["b","c","a"] mylist.InsertAfter("c", mylist.Front()) //向最后一个元素前面添加元素,List["b","c","d","a"] mylist.InsertBefore("d", mylist.Back())
- 取出链表中的元素
fmt.Println(mylist.Back().Value) //最后一个元素的值 fmt.Println(mylist.Front().Value) //第一个元素的值 //只能从头向后找,或从后往前找,获取元素内容 n := 5 var curr *list.Element if n > 0 && n <= mylist.Len() { if n == 1 { curr = mylist.Front() } else if n == mylist.Len() { curr = mylist.Back() } else { curr = mylist.Front() for i := 1; i < n; i++ { curr = curr.Next() } } } else { fmt.Println("n的数值不对") } //遍历所有值 for e := mylist.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) }
- 移动元素的顺序
mylist.MoveToBack(mylist.Front()) //把第一个移动到后面 mylist.MoveToFront(mylist.Back()) //把最后一个移动到前面 mylist.MoveAfter(mylist.Front(),mylist.Back())//把第一个参数元素,移动到第二个参数元素后面 mylist.MoveBefore(mylist.Front(),mylist.Back())//把第一个参数元素,移动到第二个参数元素前面
- 删除元素
mylist.Remove(mylist.Front())
双向循环链表
-
循环链表特点是没有节点的指针域为nil,通过任何一个元素都可以找到其他元素
-
-
在container/ring包下结构体Ring源码如下
-
官方明确说明了Ring是循环链表的元素,又是环形链表.
-
实际使用时Ring遍历就是环形链表第一个元素
-
// A Ring is an element of a circular list, or ring. // Rings do not have a beginning or end; a pointer to any ring element // serves as reference to the entire ring. Empty rings are represented // as nil Ring pointers. The zero value for a Ring is a one-element // ring with a nil Value. // type Ring struct { next, prev *Ring Value interface{} // for use by client; untouched by this library }
- Go语言标准库中对container/ring包提供的API如下
type Ring //实例化长度为n的环形链表 func New(n int) *Ring //长度 func (r *Ring) Len() int //下一个元素 func (r *Ring) Next() *Ring //上一个元素 func (r *Ring) Prev() *Ring //移动n次,支持负数 func (r *Ring) Move(n int) *Ring //合并s和r func (r *Ring) Link(s *Ring) *Ring //删除r后面n%r.Len()元素,删除多个,当前元素前面的不删除 func (r *Ring) Unlink(n int) *Ring //循环遍历,i是当前元素的值 func (r *Ring) Do(f func(interface{}))
代码演示
-
实例化、赋值、遍历
r := ring.New(3) for i := 0; i < r.Len(); i++ { r.Move(i).Value = i } r.Do(func(i interface{}) { fmt.Println(i) })
- 实例化后的r就是链表中第一个创建的元素.可以找到元素的前后元素
fmt.Println(r.Next().Value)//输出:1 fmt.Println(r.Next().Next().Value)//输出:2 fmt.Println(r.Next().Next().Next().Value)//输出:0 fmt.Println(r.Move(-1).Value)//输出:2 fmt.Println(r.Prev().Value)//输出:2
- 可以向环形链表添加或删除链表
s := ring.New(1) s.Value = 13 //r是哪个元素,就把新的链表添加到哪个元素后面 r.Link(s) r.Do(func(i interface{}) { fmt.Print(i, " ") }) fmt.Println("") //从r元素向后,n/r.Len()个元素被删除,当前元素和前面的保留 r.Unlink(1) r.Do(func(i interface{}) { fmt.Print(i, " ") })
以上是关于双向链表/list的主要内容,如果未能解决你的问题,请参考以下文章