链表与list
链表作为一种动态结构,能够根据需要的在运行时灵活的插入、删除数据元素,而且它对储存单元没有过高的要求,不一定要求核内存是连续的、整块单元。相对于数组、字符串,由于 链表存储不连续,因此不能实现对它的随机访问。在链表中查找、定位数据元素效率较低。无论如何,在很多问题中,链表还是能大展身手的,并高效的解决问题。
链表(Linked List)是一种线性数据结构,由一系列**节点(Node)**组成。每个节点包含两个部分:
数据域:存储实际的数据。
指针域:存储指向下一个节点(或上一个节点)的地址。
链表通过节点之间的指针连接起来,形成一个链式结构。
索引: 0 1 2 3 4
元素: A B C D E
地址: 0x1000 0x2000 0x3000 0x4000 0x5000
指针: •-----> •-----> •-----> •-----> None
地址:每个节点在内存中的地址。
指针:指向下一节点的地址
链表有多种类型,常见的有
单向链表(Singly Linked List)
每个节点只有一个指针,指向下一个节点。
只能从头节点开始单向遍历。
示例:
头节点 -> 节点1 -> 节点2 -> 节点3 -> nullptr
双向链表(Doubly Linked List)
每个节点有两个指针,分别指向前一个节点和后一个节点。
可以从头节点或尾节点开始双向遍历。
示例:
nullptr <-> 节点1 <-> 节点2 <-> 节点3 <-> nullptr
循环链表(Circular Linked List)
单向链表或双向链表的变种,尾节点的指针指向头节点,形成一个环。
示例:
头节点 -> 节点1 -> 节点2 -> 节点3 -> 头节点
STL中的list容器
什么是 list?
- list 是一个双向链表容器,每个元素包含指向前后元素的指针。
- 与 vector 和 deque 不同,list 不支持随机访问,但插入和删除操作的时间复杂度为 O(1)。
list 的特点:
- 动态内存分配,不需要连续的内存空间。
- 支持双向遍历(从头到尾或从尾到头)。
- 高效的插入和删除操作,尤其是在中间位置。
构造及赋值
构造和赋值list | 含义 |
---|---|
list< T > l | 默认构造空的l |
list< T > l2(l1) | 用l1复制构造l2 |
list< T > l1(beg,end) | 由区间[beg,end)的元素构造l1 |
l2 = l1 | 把l2赋值给l1 |
大小及存取
大小及存取 | 含义 |
---|---|
l.size() | 返回l中元素的个数 |
l.empty() | 判断l是否为空,l为空返回为true,用于此为目的时,比size更快 |
list< T >::iterator | 用于定义list< T >的迭代器类型 |
l.front() | 返回l的第一个元素,不检查链表是否为空 |
l.back() | 返回l的最后第一个元素,不检查链表是否为空 |
l.begin() | 返回l的第一个元素的位置 |
l.end() | 返回l的第一个最后元素的位置 |
插入与删除
插入 | 含义 |
---|---|
l.insert(pos,elem) | 在l的位置pos前将elem插入 |
l.insert(pos,n,elem) | 在l的位置pos前将n个elem插入 |
l.insert(begin,end) | 由区间[beg,end)的元素插入到l的pos位置前 |
l.push_back(elem) | 把元素elem追加到l的末端 |
l.push_front(elem) | 把元素elem追加到l的首端 |
删除 | 含义 |
---|---|
l.clear() | 删除l的所有元素 |
l.pop_front() | 删除l的第一个元素 |
l.pop_back() | 删除l的最后第一个元素 |
l.remove(val) | 删除l中所有值为val的元素 |
l.erase(pos) | 删除l中位置为pos的元素 |
l.erase(begin,end) | 删除l区间[beg,end)的元素 |
l.unique() | 删除l中重复的元素,保留第一次的出现 |
合并、逆转及排序
合并 | 含义 |
---|---|
l2.splice(pos,l1) | 将l1的所有元素转移到l2的位置pos前 |
l2.splice(pos,l1,pos1) | 将l1中位置为pos1元素转移到l2的位置pos前 |
l2.splice(begin,end) | 将l1中区间为[begin,end)的所有元素转移到l2的位置pos前 |
l1.merge(l2) | 把有序链表l2中的所有元素转移到l2的位置pos前 |
逆转和排序 | 含义 |
---|---|
l.reverse() | 将l中所有元素逆转 |
l.sort() | 将l中元素从小到大排序 |
值得注意的是,list不支持随机访问,因此不提供下标运算符和at()函数。list比较与vector的优势在与在任何位置插入和删除元素都很快,list的成员大多数执行的效率更高。
下面来举个例子帮助理解
将偶数和奇数装进两个不同的链表中,最后将两个链表排序后合并
#include <iostream>
#include <list>
int main() {
// 初始链表
std::list<int> originalList = {5, 2, 9, 1, 4, 6, 3, 8, 7};
// 用于存储偶数和奇数的链表
std::list<int> evenList; // 偶数链表
std::list<int> oddList; // 奇数链表
// 将偶数和奇数分别装入两个链表
for (int num : originalList) {
if (num % 2 == 0) {
evenList.push_back(num); // 偶数放入 evenList
} else {
oddList.push_back(num); // 奇数放入 oddList
}
}
// 对偶数链表和奇数链表进行排序
evenList.sort(); // 偶数链表排序
oddList.sort(); // 奇数链表排序
// 输出排序后的偶数链表和奇数链表
std::cout << "排序后的偶数链表: ";
for (int val : evenList) {
std::cout << val << " ";
}
std::cout << std::endl;
std::cout << "排序后的奇数链表: ";
for (int val : oddList) {
std::cout << val << " ";
}
std::cout << std::endl;
// 将偶数链表和奇数链表合并
evenList.merge(oddList); // 合并后 evenList 包含所有元素,oddList 为空
// 输出合并后的链表
std::cout << "合并后的链表: ";
for (int val : evenList) {
std::cout << val << " ";
}
std::cout << std::endl;
// 输出 oddList(应为空)
std::cout << "合并后的 oddList: ";
for (int val : oddList) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
排序后的偶数链表: 2 4 6 8
排序后的奇数链表:: 1 3 5 7 9
合并后的链表: 1 2 3 4 5 6 7 8 9
合并后的 oddList:
感谢您耐心阅读,希望这些关于list的知识能对您的编程学习有所助益!
若您有疑问之处请在评论区提出。