双向链表/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
}
  • 其中Element结构体定义如下

    • next表示下一个元素,使用Next()可以获取到

    • prev表示上一个元素,使用Prev()可以获取到

    • list表示元素属于哪个链表

    • Value表示元素的值,interface{}在Go语言中表示任意类型

  // 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,通过任何一个元素都可以找到其他元素

  • 环形链表结构如下

技术图片 

  • 双向循环链表和双向链表区别

    • 双向循环链表没有严格意义上的头元素和尾元素

    • 没有元素的前连接和后连接为nil

    • 一个长度为n的双向循环链表,通过某个元素向某个方向移动,在查找最多n-1次后一定会找到另一个元素

Go语言中的双向循环链表

  • 在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的主要内容,如果未能解决你的问题,请参考以下文章

数据结构之带头结点的循环双向链表详细图片+文字讲解

双向链表

list双向链表容器

List双向链表容器

双向链表/list

List双向链表容器