• 双向链表结构如下

  • 双向链表结构中元素在内存中不是紧邻空间,而是每个元素中存放上一个元素和后一个元素的地址

    • 第一个元素称为头(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 :=
var curr *list.Element
if n > && n <= mylist.Len() {
if n == {
curr = mylist.Front()
} else if n == mylist.Len() {
curr = mylist.Back()
} else {
curr = mylist.Front()
for i := ; 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()
for i := ; 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(-).Value)//输出:2
fmt.Println(r.Prev().Value)//输出:2
  • 可以向环形链表添加或删除链表
    s := ring.New()
s.Value =
//r是哪个元素,就把新的链表添加到哪个元素后面
r.Link(s)
r.Do(func(i interface{}) {
fmt.Print(i, " ")
})
fmt.Println("")
//从r元素向后,n/r.Len()个元素被删除,当前元素和前面的保留
r.Unlink()
r.Do(func(i interface{}) {
fmt.Print(i, " ")
})

双向链表/list的更多相关文章

  1. 学习Redis你必须了解的数据结构——双向链表(JavaScript实现)

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/ 下午分享了JavaScript实现单向链表,晚上就来补充下双向链表吧.对链表 ...

  2. 双向链表、双向循环链表的JS实现

    关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法:  单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...

  3. 剑指Offer面试题:25.二叉搜索树与双向链表

    一.题目:二叉搜索树与双向链表 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向.比如输入下图中左边的二叉搜索树,则输出转换之后的 ...

  4. Linux 内核数据结构:Linux 双向链表

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...

  5. Linux 内核数据结构:双向链表

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...

  6. 线性表-双向链表(LinkedList)

    双向链表:如图1-3 所示,会把当前header拆分开,重新插入一个Entry<E>. LinkedList源码 0.首先这个类中的两个变量 private transient Entry ...

  7. Shuffling Machine和双向链表

    1. 双向链表 https://github.com/BodhiXing/Data_Structure 2. Shuffling Machine https://pta.patest.cn/pta/t ...

  8. MS - 1 - 把二元查找树转变成排序的双向链表

    ## 1. 把二元查找树转变成排序的双向链表 ## ### 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表. ### 要求不能创建任何新的结点,只调整指针的指向. 10       ...

  9. javascript中的链表结构—双向链表

    1.概念 上一个文章里我们已经了解到链表结构,链表的特点是长度不固定,不用担心插入新元素的时候新增位置的问题.插入一个元素的时候,只要找到插入点就可以了,不需要整体移动整个结构. 这里我们了解一下双向 ...

  10. Java自己实现双向链表LinkList

    /** * <p> * Node 双向链表实体类 * <p> * * @author <a href="mailto:yangkj@corp.21cn.com& ...

随机推荐

  1. UITableViewCell点击不能push解决方法

    一般情况下不能push是因为当前控制器没有导航控制器,造成不能push的情况. 解决方法如下: - (void)tableView:(UITableView *)tableView didSelect ...

  2. jinja2渲染使用

    说明:通过jinja2渲染后只能打印出来效果,目前无法保存 例1:渲染 .j2 文件 1.安装jinja2模块 pip3 install jinja2 2.定义模板 说明:变量必须是小写,大写有的情况 ...

  3. 重学Python - Day 07 - python基础 -> linux命令行学习 -- 常用命令 一

    常用命令和使用方法如下: man man 命令 #可以查询命令的用法 cat 和 tac cat是正序显示文件内容 tac是倒叙显示文件内容 sort 对文件内容排序 uniq 忽略文件中重复行 hi ...

  4. WINDOWS mysql 5.7.15 安装配置方法图文教程

    因本人需要需要安装Mysql,现将安装过程记录如下,在自己记录的同时,希望对有疑问的人有所帮助. 一.下载软件 1. 进入mysql官网,登陆自己的oracle账号(没有账号的自己注册一个),下载My ...

  5. Star all over again.

    0x00前言 经过了一上午的折腾之后,博客的界面勉强可观,今天下午将之前的所有博客全部删除,重新开始写属于自己的博客,而不是只把它当作一个收藏夹,转载其他人的文章. 0x01近来感想 有感而发,随便写 ...

  6. ASP.NET Session详解(转)

    ASP.NET Session详解 本文章来自:http://blog.163.com/adam601@126/blog/static/22506317200932824210996/ 当用户在 We ...

  7. 卸载mysql后再安装提示The service already exists!问题解决方法

    卸载mysql后再安装输入mysqld --install 回车后提示The service already exists! 原因:卸载的时候没有卸载干净 方法: 一.重新以管理员身份打开cmd 二. ...

  8. VMware Workstation改动存储位置之后,软件变成全英文,修改成中文的方法

    今天想改动一下VMware Workstation的位置  改动之后打开软件,本来的中文改成了英文,整了很长时候,最后发现是因为改动一下位置之后,虽然zh_CN语言包还在,但是Vmware找不到本来的 ...

  9. postman 获取登录成功后存在在header中cookies,并在下一个接口中使用。

    1.首先登录成功 2.在tests中添加脚本 3.添加环境变量 4.配置环境变量和引用值 5.请求第二个接口成功

  10. EM 算法(二)-KMeans

    KMeans 算法太过简单,不再赘述 本文尝试用 EM 算法解释 KMeans,而事实上 KMeans 算是 EM 的一个特例 EM 算法是包含隐变量的参数估计模型,那对应到 KMeans 上,隐变量 ...