双向链表/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 :=
- 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的更多相关文章
- 学习Redis你必须了解的数据结构——双向链表(JavaScript实现)
本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/ 下午分享了JavaScript实现单向链表,晚上就来补充下双向链表吧.对链表 ...
- 双向链表、双向循环链表的JS实现
关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法: 单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...
- 剑指Offer面试题:25.二叉搜索树与双向链表
一.题目:二叉搜索树与双向链表 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向.比如输入下图中左边的二叉搜索树,则输出转换之后的 ...
- Linux 内核数据结构:Linux 双向链表
Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...
- Linux 内核数据结构:双向链表
Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...
- 线性表-双向链表(LinkedList)
双向链表:如图1-3 所示,会把当前header拆分开,重新插入一个Entry<E>. LinkedList源码 0.首先这个类中的两个变量 private transient Entry ...
- Shuffling Machine和双向链表
1. 双向链表 https://github.com/BodhiXing/Data_Structure 2. Shuffling Machine https://pta.patest.cn/pta/t ...
- MS - 1 - 把二元查找树转变成排序的双向链表
## 1. 把二元查找树转变成排序的双向链表 ## ### 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表. ### 要求不能创建任何新的结点,只调整指针的指向. 10 ...
- javascript中的链表结构—双向链表
1.概念 上一个文章里我们已经了解到链表结构,链表的特点是长度不固定,不用担心插入新元素的时候新增位置的问题.插入一个元素的时候,只要找到插入点就可以了,不需要整体移动整个结构. 这里我们了解一下双向 ...
- Java自己实现双向链表LinkList
/** * <p> * Node 双向链表实体类 * <p> * * @author <a href="mailto:yangkj@corp.21cn.com& ...
随机推荐
- 059. Spiral Matrix II
题目链接:https://leetcode.com/problems/spiral-matrix-ii/description/ Given a positive integer n, generat ...
- 转-Uptime与数据中心等级认证
1 数据中心等级认证 随着数据中心的蓬勃发展,越来越多的标准被制定出具.其中,Uptime Tier认证在业内是认同度最高的标准.以前,Uptime在中国的宣传很少,很多人对Uptime及其认证体系不 ...
- windows中service.msc与regedit
Services.msc是Windows2000/XP/2003/Vista/7/2008/8/8.1/10系统中用来启动.终止并设置 Windows 服务的管理策略. 作用:控制系统服务. 性质:系 ...
- eve-ng
eve-ng eve-ng采用的是和web-iou一样的B/S模式,进步的地方是它只需拖动鼠标就可以搭建拓朴,比web-iou要直观. 此虚拟机已经集成了IOL的L2.L3以及vIOS的L ...
- 应用安全 - 工具 - Jmeter - 漏洞 - 汇总
CVE-2018-1297 Date 类型 rmi 反序列化导致远程代码执行 影响范围
- 【Linux】linux设备驱动归纳总结
前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...
- BUUOJ reverse SimpleRev (爆破)
SimpleRev SimpleRev(flag需加上flag{}再提交) 注意:得到的 flag 请包上 flag{} 提交 拖到ida 找到关键函数: unsigned __int64 Decry ...
- 自然语言处理工具hanlp定制用户词条
作者:baiziyu 关于hanlp的文章已经分享过很多,似乎好像大部分以理论性的居多.最近有在整理一些hanlp应用项目中的文章,待整理完成后会陆续分享出来.本篇分享的依然是由baiziyu 分享的 ...
- split、paste命令
一.split分割文件 语法 split [OPTION] ... [INPUT [PREFIX]] 描述 将固定大小的INPUT输出到PREFIXaa,PREFIXab,.. ...
- Zabbix 监控常见服务
监控Apache性能 1.客户端编译安装Apache服务,并在编译选项中开启监控页面功能. [root@localhost ~]# yum install -y gcc openssl openssl ...