• 双向链表/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, " ")
        })

     

  • 相关阅读:
    C# AtomicInt
    Ubuntu16.04或18.04上安装QQ微信迅雷
    Git强制拉取覆盖本地 Pull force
    ulimit限制打开的文件数量
    centos 7.x设置守护进程的文件数量限制
    Apache Doris通过supervisor进行进程管理
    CentOS7 安装supervisor守护进程管理器
    fdisk 分区
    linux i2c tools
    ubuntu12.04 登录黑屏
  • 原文地址:https://www.cnblogs.com/miaoweiye/p/12092704.html
Copyright © 2020-2023  润新知