C++STL容器:链表介绍与使用
目录
- 一、链表概念介绍
- C++中链表的实现与使用
- 1. 手写链表的定义(无方法)
- 2.STL库容器: list (双向链表)与 forward_list (单向链表)
- list
- list构造方法
- list增、删、查
- list的正向与逆向迭代器
- list 的特殊方法
一、链表概念介绍
链表是一种通过指针串连起来的数据结构,一个链表节点分为数据域(用来存储数据),和指针域(存储指向下一个(或上一个)结点的地址指针)。根据结构特点可以分为:单向链表、双向链表和循环链表三类:
- 单向链表
单向链表就是链表的结点仅含指向后继的指针(next),也就是说后一个数据的指针只能通过前一个数据找到。(单向链表尾部指向空指针)
- 双向链表
双向链表的结点含指向前驱(prev)和后继(next)的指针,适用于需要双向遍历的操作。(双向链表头节点的前驱以及尾节点的后继指向空指针) - 循环链表!
循环链表就是尾结点指针指向头结点的链表,形成闭环。
链表的优点:
- 动态内存分配:结点在运行时动态生成,无需预先指定大小,各个节点地址分散(数组是连续的)。
- 高效插入/删除:时间复杂度为** O(1)**(数组为O(n))(仅修改指针)。
链表的缺点:
- 低效随机访问:查找元素需遍历,时间复杂度 O(n)(数组为O(1))。
- 空间开销:需额外空间存储指针,内存利用率低于数组。
C++中链表的实现与使用
1. 手写链表的定义(无方法)
//单链表
struct ListNode {int val; //数据域ListNode* next; //指针域 ListNode(int val = 0, ListNode* next = nullptr) : val(val), next(next) {}
};//双链表
struct ListNode{int val; //数据域ListNode* prev;//指针域 ListNode* next;//指针域 ListNode(int val=0,ListNode* prev = nullptr,ListNode* next = nullptr):val(val),prev(prev),next(next){}
};
2.STL库容器: list (双向链表)与 forward_list (单向链表)
list
list是双向链表结构,每个节点包含前驱(prev)和后继(next)两个指针。对比之前STL容器vector,list少了不能按索引查找数据,多了可以轻松在头部插入数据,其它的方法也都比较类似,用于存储不需要经常访问的数据。
使用需要包含头文件#include<list>
并且`using namespace std;
list构造方法
由之前基础知识介绍可知,list对象的构造除了不支持初始化为n个空闲空间(链表是动态存储),其它与vector差不多。
//构造
//1. 构造空链表
list<T> L_name;//2. 初始化为 n个 value
list<T> L_name(n,value);//3. 拷贝初始化,L2为一个已有链表
list<T> L_name(L2_name);//4. 迭代器初始化,L1为其它容器
list<T> L_name(L1.begin(),L1.end());
list增、删、查
//1.在头部增加/删除元素
L_name.push_front(value); //增加
L_name.pop_front(); //删除//2. 在尾部增加/删除元素
L_name.push_back(value);//增加
L_name.pop_back();//删除//3. 在指定位置增加/删除元素
//增加 insert
list<int>::iterator pos = L_name.begin(); //pos一定要是迭代器对象
auto new_it = L_name.insert(pos,value); //在pos位置插入value,返回一个指向当前插入元素的指针,之前的pos仍然指向原来的值
L_name.insert(pos,n,value); //在pos位置插入n个value
//删除 erase
L_name.erase(pos); //删除指定位置的数据
L_name.erase(L_name.begin(),L_name.end()); //删除区间,这里L_name.begin(),L_name.end()仅作演示,表示所有
//删除 remove
L_name.remove(value); //删除等于value的所有元素//4.查询头部
L_name.front();//5.查询尾部
L_name.back();//6.查询其它位置
//链表不像数组那样有下标,因此查询其它位置的数据只能通过迭代的方式进行。
//第一种,通过遍历的方法查询:
for (auto it = lst.begin(); it != lst.end(); ++it) {//这里list的迭代器和vector不一样,不能直接 + 偏移量(链表内存地址不连续)if (*it == 30) {std::cout << "Found at position: " << std::distance(lst.begin(), it);break;}
}
//第二种,通过find函数查找:
#include <algorithm>
#include <list>std::list<int> lst = {10, 20, 30};
auto it = std::find(lst.begin(), lst.end(), 20); // 查找值为20的元素
if (it != lst.end()) {std::cout << "Found: " << *it << std::endl;
}
list的正向与逆向迭代器
list是双向链表,因此除了正向迭代器(begin(),end())外,还有逆向迭代器(rbegin(),rend())。逆向迭代器和正向迭代器的区别在于:
- begin()指向开头的元素,而rbegin()指向最后一个元素。
- 对于操作符++来说,begin()是向后移动,rbegin()是向前。即逆向迭代的
++
等于正向迭代的--
std::list<int> lst = {10, 20, 30};
for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) {std::cout << *rit << " "; // 输出:30 20 10
}
// 逆向迭代器 ++ 实际调用正向迭代器的 --
对于forward_list,没有逆向迭代器
list 的特殊方法
- sort()
std::list<int> lst = {30, 10, 20};
lst.sort(); // 默认升序排序,结果为 {10, 20, 30}
- splice
std::list的splice()方法是链表特有的高效节点转移操作,通过直接修改指针实现元素移动,无需拷贝或内存分配。splice(要插入的位置,要插入的链表,要插入链表的元素位置(默认为整体))
std::list<int> lst1{1, 2}, lst2{3, 4};
auto pos = lst1.end();// 整链转移
lst1.splice(pos, lst2); // lst1={1,2,3,4}, lst2为空// 单元素转移
lst2.splice(lst2.begin(), lst1, ++lst1.begin()); // lst1={1,3,4}, lst2={2}// 范围转移
lst1.splice(lst1.begin(), lst1, ++lst1.begin(), lst1.end()); // lst1={3,4,1}