type
status
date
slug
summary
tags
category
icon
password
Property
链表
链表是一种用于存储数据的数据结构,通过如链条一般的指针来连接元素。它的特点是插入与删除数据十分方便,但寻找与读取数据的表现欠佳。
- 链表因其链状的结构,能方便地删除、插入数据,操作次数是。但也因为这样,寻找、读取数据的效率不如数组高,在随机访问数据中的操作次数是。
- 数组可以方便地寻找并读取数据,在随机访问中操作次数是。但删除、插入的操作次数是次。
优点:存储空间不必事先分配,在需要存储空间的时候可以临时申请,不会造成空间浪费;一些操作的时间效率远比数组高(插入、移动、删除等)
缺点:不仅数据元素本身的数据信息要占用存储空间,指针也需要占用存储空间,链表结构比数组结构的空间开销大。
单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。逻辑上相邻的数据元素在物理地址上可能相邻,可也能不相邻。其在物理地址上的表现是随机的。
双向链表(Doubly Linked List)中同样有数据域和指针域。不同之处在于,指针域有左右(或上一个、下一个)之分,用来连接上一个结点、当前结点、下一个结点。
循环链表(Circular linked list):最后一个链节点指向头节点,形成一个环。从循环链表的任何一个节点出发都能找到任何其他节点。
向链表中插入数据
单向链表
- 初始化待插入的数据
node
- 将
node
的next
指针指向p
的下一个结点
- 将
p
的next
指针指向node
单向循环链表
将链表的头尾连接起来,链表就变成了循环链表。由于链表首尾相连,在插入数据时需要判断原链表是否为空:为空则自身循环,不为空则正常插入数据。
- 初始化待插入的数据
node
,判断给定链表p
是否为空:
- 若为空,则将
node
的next
指针和p
都指向自己;否则,将node
的next
指针指向p
的下一个结点;
- 将
p
的next
指针指向node
双向循环链表
在向双向循环链表插入数据时,除了要判断给定链表是否为空外,还要同时修改左、右两个指针。
- 初始化待插入的数据
node
;判断给定链表p
是否为空;
- 若为空,则将
node
的left
和right
指针,以及p
都指向自己;否则,将node
的left
指针指向p
;
- 将
node
的right
指针指向p
的右结点;
- 将
p
右结点的left
指针指向node
;
- 将
p
的right
指针指向node
。
从链表中删除数据
单向(循环)链表
设待删除结点为
p
,从链表中删除它时,将 p
的下一个结点 p->next
的值覆盖给 p
即可,与此同时更新 p
的下下个结点。- 将
p
下一个结点的值赋给p
,以抹掉p->value
;
- 新建一个临时结点
t
存放p->next
的地址;
- 将
p
的next
指针指向p
的下下个结点,以抹掉p->next
;
- 删除
t
。此时虽然原结点p
的地址还在使用,删除的是原结点p->next
的地址,但p
的数据被p->next
覆盖,p
名存实亡。
双向循环链表
- 将
p
左结点的右指针指向p
的右节点;
- 将
p
右结点的左指针指向p
的左节点;
- 新建一个临时结点
t
存放p
的地址;
- 将
p
的右节点地址赋给p
,以避免p
变成悬垂指针;
- 删除
t
。
list
List
底层是双向链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息快(即实际存储的数据)、一个前驱指针和一个后驱指针。它无需分配指定的内存大小且可以任意伸缩,这是因为它存储在非连续的内存空间中,并且指针将有序的元素链接起来。由于其结构的原因,
list
随机检索的性能非常的不好,因为它不像vector
那样直接找到元素的地址,而是要从头一个一个的顺序查找,这样,目标元素越靠后,它的检索时间就越长。所以检索时间与目标元素的位置成正比。虽然随机检索的速度不够快,但是它可以迅速地在任何节点进行插入与删除操作。因为list
的每个节点保存着它在链表中的位置,插入或删除一个元素仅对最多三个元素有所影响,不像vector
会对操作点之后的所有元素的存储地址都有所影响。List的特点
- 不适用连续的内存空间,这样可以随意地进行动态操作
- 可以在内部任意位置快速地插入或删除,
- 不能进行内部的随机访问,即不支持
[]
操作符。如果要访问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
容器相同的特性,即擅长在序列的任何位置进行插入元素或删除元素的操作,但对于访问存储的元素,没有其它容器(如array
、vector
)的效率高。由于单链表没有双向链表那样灵活,因此相比
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() | 反转容器中元素的顺序。 |