链表 list
2023-2-17
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

 

链表

链表是一种用于存储数据的数据结构,通过如链条一般的指针来连接元素。它的特点是插入与删除数据十分方便,但寻找与读取数据的表现欠佳。
  • 链表因其链状的结构,能方便地删除、插入数据,操作次数是。但也因为这样,寻找、读取数据的效率不如数组高,在随机访问数据中的操作次数是
  • 数组可以方便地寻找并读取数据,在随机访问中操作次数是。但删除、插入的操作次数是次。
 
优点:存储空间不必事先分配,在需要存储空间的时候可以临时申请,不会造成空间浪费;一些操作的时间效率远比数组高(插入、移动、删除等)
缺点:不仅数据元素本身的数据信息要占用存储空间,指针也需要占用存储空间,链表结构比数组结构的空间开销大。
 
 
单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。逻辑上相邻的数据元素在物理地址上可能相邻,可也能不相邻。其在物理地址上的表现是随机的。
notion image
notion image
notion image
notion image
notion image
notion image
 
双向链表(Doubly Linked List)中同样有数据域和指针域。不同之处在于,指针域有左右(或上一个、下一个)之分,用来连接上一个结点、当前结点、下一个结点。
notion image
notion image
 
循环链表(Circular linked list):最后一个链节点指向头节点,形成一个环。从循环链表的任何一个节点出发都能找到任何其他节点。
notion image
 
 

向链表中插入数据

单向链表
  1. 初始化待插入的数据 node
    1. notion image
  1. 将 node 的 next 指针指向 p 的下一个结点
    1. notion image
  1. 将 p 的 next 指针指向 node
    1. notion image
 
 
单向循环链表
将链表的头尾连接起来,链表就变成了循环链表。由于链表首尾相连,在插入数据时需要判断原链表是否为空:为空则自身循环,不为空则正常插入数据。
  1. 初始化待插入的数据 node,判断给定链表 p 是否为空:
    1. notion image
  1. 若为空,则将 node 的 next 指针和 p 都指向自己;否则,将 node 的 next 指针指向 p 的下一个结点;
    1. notion image
  1. 将 pnext指针指向 node
    1. notion image
 
 
双向循环链表
在向双向循环链表插入数据时,除了要判断给定链表是否为空外,还要同时修改左、右两个指针。
  1. 初始化待插入的数据 node;判断给定链表 p 是否为空;
  1. 若为空,则将 node 的 left 和 right 指针,以及 p 都指向自己;否则,将 node 的 left 指针指向 p;
  1. 将 node 的 right 指针指向 p 的右结点;
  1. 将 p 右结点的 left 指针指向 node
  1. 将 p 的 right 指针指向 node
 

从链表中删除数据

单向(循环)链表
设待删除结点为 p,从链表中删除它时,将 p的下一个结点 p->next的值覆盖给 p即可,与此同时更新 p的下下个结点。
notion image
  1. 将 p 下一个结点的值赋给 p,以抹掉 p->value
    1. notion image
  1. 新建一个临时结点 t 存放 p->next 的地址;
  1. 将 p 的 next 指针指向 p 的下下个结点,以抹掉 p->next
    1. notion image
  1. 删除 t。此时虽然原结点 p 的地址还在使用,删除的是原结点 p->next 的地址,但 p 的数据被 p->next 覆盖,p 名存实亡。
 
双向循环链表
  1. 将 p 左结点的右指针指向 p 的右节点;
  1. 将 p 右结点的左指针指向 p 的左节点;
  1. 新建一个临时结点 t 存放 p 的地址;
  1. 将 p 的右节点地址赋给 p,以避免 p 变成悬垂指针;
  1. 删除 t
 

list

List底层是双向链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息快(即实际存储的数据)、一个前驱指针和一个后驱指针。它无需分配指定的内存大小且可以任意伸缩,这是因为它存储在非连续的内存空间中,并且指针将有序的元素链接起来。
由于其结构的原因,list随机检索的性能非常的不好,因为它不像vector那样直接找到元素的地址,而是要从头一个一个的顺序查找,这样,目标元素越靠后,它的检索时间就越长。所以检索时间与目标元素的位置成正比。虽然随机检索的速度不够快,但是它可以迅速地在任何节点进行插入与删除操作。因为list的每个节点保存着它在链表中的位置,插入或删除一个元素仅对最多三个元素有所影响,不像vector会对操作点之后的所有元素的存储地址都有所影响。
 
List的特点
  1. 不适用连续的内存空间,这样可以随意地进行动态操作
  1. 可以在内部任意位置快速地插入或删除,
  1. 不能进行内部的随机访问,即不支持 []操作符。如果要访问list中的第 6 个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销。不仅如此,list还需要一些额外的空间,以保存每个结点的 "相关联信息"。
 
 

list的构造

  • list():创建一个空list
  • list(int nSize):创建一个vector,元素个数为nSize
  • list(int nSize,const t& t):创建一个list,元素个数为nSize,且值均为t
  • list(const vector&):复制构造函数
  • list(begin,end):复制[begin,end)区间内元素到list
 

list迭代器使用

  • iterator :正向迭代器
  • reverse_iterator:反向迭代器
  • const_iterator:常量迭代器
  • const_reverse_iterator:常量反向迭代器
  • begin()返回指向容器中第一个元素的迭代器
    • end()返回指向容器最后一个元素所在位置后一个位置的迭代器
  • cbegin():在begin()基础上,增加了const属性,不能用于修改元素
    • cend():在end()基础上,增加const属性,不能用于修改元素
  • rbegin():反向,返回指向最后一个元素的迭代器
    • rend() :反向,返回指向第一个元素所在位置前一个位置的迭代器
  • crbegin() :在rbegin()基础上,增加了const属性,不能用于修改元素
    • crend() :在rend()基础上,增加了const属性,不能用于修改元素
 

list容量操作

函数成员
函数功能
size()
返回实际元素个数
max_size()
返回元素个数的最大值。这通常是一个很大的值,很少会用到
resize()
改变实际元素的个数
empty()
判断容器中是否有元素,若无元素,则返回 true
 

list增删查改

函数成员
函数功能
front()
返回第一个元素的引用
back()
返回最后一个元素的引用
assign()
用新元素替换原有内容
push_back()
尾插
pop_back()
尾删
push_front()
头插
pop_front()
头删
insert()
在指定的位置插入一个或多个元素。
erase()
移出一个元素或一段元素。
remove()
只需要给一个元素的值,它就可以自己找自己删
clear()
移出所有的元素,容器大小变为 0。
swap()
交换两个容器的所有元素。
emplace()
在指定的位置直接生成一个元素。
emplace_back()
在序列尾部生成一个元素。
emplace_front()
在序列头部生成一个元素。
 

其他操作

函数成员
函数功能
reverse()
逆置 list 中的元素
sort()
排序
unique()
去重,去重之前一定要先排序
splice()
把一个链表转移到另一个链表里去
 

迭代器失效

插入元素不会导致迭代器失效;删除元素时,只会导致当前迭代器失效,其它迭代器不受影响
 
 

list实现

 
 

forward_list

forward_list容器被实现为单链表,具有和list容器相同的特性,即擅长在序列的任何位置进行插入元素或删除元素的操作,但对于访问存储的元素,没有其它容器(如arrayvector)的效率高。
由于单链表没有双向链表那样灵活,因此相比list容器,forward_list 容器的功能受到了很多限制。比如,由于单链表只能从前向后遍历,而不支持反向遍历,因此forward_list只提供前向迭代器,而不是双向迭代器。这意味着,forward_list容器不具有 rbegin()rend()之类的成员函数。
存储相同个数的同类型元素,单链表耗用的内存空间更少,空间利用率更高,并且对于实现某些操作单链表的执行效率也更高。效率高是选用 forward_list而弃用list容器最主要的原因。
成员函数
功能
before_begin()
返回一个前向迭代器,其指向容器中第一个元素之前的位置。
begin()
返回一个前向迭代器,其指向容器中第一个元素的位置。
end()
返回一个前向迭代器,其指向容器中最后一个元素之后的位置。
cbefore_begin()
和 before_begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cbegin()
和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend()
和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
empty()
判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
max_size()
返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。
front()
返回第一个元素的引用。
assign()
用新元素替换容器中原有内容。
push_front()
在容器头部插入一个元素。
emplace_front()
在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。
pop_front()
删除容器头部的一个元素。
emplace_after()
在指定位置之后插入一个新元素,并返回一个指向新元素的迭代器。和 insert_after() 的功能相同,但效率更高。
insert_after()
在指定位置之后插入一个新元素,并返回一个指向新元素的迭代器。
erase_after()
删除容器中某个指定位置或区域内的所有元素。
swap()
交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。
resize()
调整容器的大小。
clear()
删除容器存储的所有元素。
splice_after()
将某个 forward_list 容器中指定位置或区域内的元素插入到另一个容器的指定位置之后。
remove(val)
删除容器中所有等于 val 的元素。
remove_if()
删除容器中满足条件的元素。
unique()
删除容器中相邻的重复元素,只保留一个。
merge()
合并两个事先已排好序的 forward_list 容器,并且合并之后的 forward_list 容器依然是有序的。
sort()
通过更改容器中元素的位置,将它们进行排序。
reverse()
反转容器中元素的顺序。
 
  • 计算机基础
  • 数据结构与算法
  • vector题目list题目
    目录