🎯 三、list容器:双向链表全面解析
3.1 list基本特性
list是C++标准库中的双向循环链表容器,支持在任意位置O(1)时间插入删除,但不支持随机访问。
3.2 list核心接口
3.2.1 构造与初始化
cpp
#include <list>
#include <iostream>
using namespace std;
void TestListConstruct() {
// 1. 默认构造
list<int> l1;
// 2. 指定大小和值
list<int> l2(5, 10); // 5个10
// 3. 范围构造
int arr[] = {1, 2, 3, 4, 5};
list<int> l3(arr, arr + 5);
// 4. 拷贝构造
list<int> l4(l3);
// 5. 移动构造(C++11)
list<int> l5(move(l4));
// 6. 列表初始化(C++11)
list<int> l6 = {1, 2, 3, 4, 5};
}
3.2.2 访问与遍历
cpp
void TestListAccess() {
list<int> lst = {1, 2, 3, 4, 5};
// list不支持[]随机访问
// cout << lst[2] << endl; // ERROR!
// 1. 迭代器访问
for (auto it = lst.begin(); it != lst.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 2. 范围for
for (int num : lst) {
cout << num << " ";
}
cout << endl;
// 3. 访问首尾元素
cout << "front: " << lst.front() << endl;
cout << "back: " << lst.back() << endl;
// 4. 反向迭代器
for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) {
cout << *rit << " ";
}
cout << endl;
}
3.2.3 修改操作
cpp
void TestListModify() {
list<int> lst = {1, 2, 3};
// 1. 头尾插入
lst.push_front(0); // {0, 1, 2, 3}
lst.push_back(4); // {0, 1, 2, 3, 4}
// 2. 头尾删除
lst.pop_front(); // {1, 2, 3, 4}
lst.pop_back(); // {1, 2, 3}
// 3. 插入
auto it = lst.begin();
++it; // 指向2
lst.insert(it, 99); // {1, 99, 2, 3}
// 4. 删除
it = lst.begin();
++it; // 指向99
lst.erase(it); // {1, 2, 3}
// 5. 拼接(splice) - list特有
list<int> lst2 = {4, 5, 6};
lst.splice(lst.end(), lst2); // {1, 2, 3, 4, 5, 6}
// 6. 排序 - list有自己专门的sort方法
list<int> lst3 = {3, 1, 4, 2, 5};
lst3.sort(); // {1, 2, 3, 4, 5}
// 7. 去重(需要先排序)
lst3.unique();
}
3.2.4 list特有操作
cpp
void TestListSpecial() {
list<int> lst = {1, 3, 5, 7, 9};
list<int> lst2 = {2, 4, 6, 8, 10};
// 1. 合并(两个有序列表)
lst.sort();
lst2.sort();
lst.merge(lst2); // lst变为有序合并列表
// 2. 反转
lst.reverse();
// 3. 移除特定元素
lst.remove(5); // 移除所有值为5的元素
// 4. 移除满足条件的元素
lst.remove_if([](int x) { return x % 2 == 0; });
}
3.3 迭代器失效问题
list的迭代器失效情况比vector简单:
插入操作不会使任何迭代器失效
删除操作只会使指向被删除元素的迭代器失效
cpp
void TestListIterator() {
list<int> lst = {1, 2, 3, 4, 5};
// 正确:删除偶数元素
auto it = lst.begin();
while (it != lst.end()) {
if (*it % 2 == 0) {
it = lst.erase(it); // erase返回下一个有效迭代器
} else {
++it;
}
}
}
3.4 list模拟实现
3.4.1 节点结构
cpp
template<class T>
struct ListNode {
T _data;
ListNode<T>* _prev;
ListNode<T>* _next;
ListNode(const T& val = T())
: _data(val), _prev(nullptr), _next(nullptr) {}
};
3.4.2 迭代器设计
list迭代器不是原生指针,而是对节点指针的封装:
cpp
template<class T, class Ref, class Ptr>
struct ListIterator {
typedef ListNode<T> Node;
typedef ListIterator<T, Ref, Ptr> Self;
Node* _node;
ListIterator(Node* node) : _node(node) {}
// 重载操作符
Ref operator*() { return _node->_data; }
Ptr operator->() { return &_node->_data; }
Self& operator++() {
_node = _node->_next;
return *this;
}
Self operator++(int) {
Self temp(*this);
_node = _node->_next;
return temp;
}
Self& operator--() {
_node = _node->_prev;
return *this;
}
Self operator--(int) {
Self temp(*this);
_node = _node->_prev;
return temp;
}
bool operator!=(const Self& it) { return _node != it._node; }
bool operator==(const Self& it) { return _node == it._node; }
};
3.4.3 list类框架
cpp
template<class T>
class List {
typedef ListNode<T> Node;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
// 构造函数
List() {
_head = new Node();
_head->_prev = _head;
_head->_next = _head;
}
// 析构函数
~List() {
clear();
delete _head;
_head = nullptr;
}
// 迭代器
iterator begin() { return iterator(_head->_next); }
iterator end() { return iterator(_head); }
// 插入删除
void push_back(const T& val) {
insert(end(), val);
}
iterator insert(iterator pos, const T& val) {
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newNode = new Node(val);
newNode->_prev = prev;
newNode->_next = cur;
prev->_next = newNode;
cur->_prev = newNode;
return iterator(newNode);
}
iterator erase(iterator pos) {
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}
private:
Node* _head; // 头结点(哨兵节点)
};
3.5 list与vector对比
| 特性 | vector | list |
|---|---|---|
| 底层结构 | 动态数组 | 双向循环链表 |
| 内存分配 | 连续空间 | 非连续节点 |
| 随机访问 | O(1) | O(n) |
| 插入删除 | 尾部O(1),中间O(n) | 任意位置O(1) |
| 迭代器 | 原生指针 | 封装指针 |
| 迭代器失效 | 插入/删除可能使所有迭代器失效 | 仅删除使当前迭代器失效 |
| 缓存友好 | 是(空间局部性) | 否 |
| 内存开销 | 小(只有数据) | 大(每个节点有额外指针) |
🎯 四、stack与queue:容器适配器详解
4.1 stack:后进先出(LIFO)
4.1.1 stack基本概念
stack是一种容器适配器,遵循后进先出原则,底层可用vector、list或deque实现。
cpp
#include <stack>
#include <iostream>
using namespace std;
void TestStack() {
stack<int> s;
// 入栈
s.push(1);
s.push(2);
s.push(3);
// 访问栈顶
cout << "top: " << s.top() << endl; // 3
// 出栈
s.pop();
cout << "after pop, top: " << s.top() << endl; // 2
// 大小
cout << "size: " << s.size() << endl;
cout << "empty: " << s.empty() << endl;
}
4.1.2 stack模拟实现
cpp
template<class T, class Container = deque<T>>
class Stack {
public:
void push(const T& val) {
_con.push_back(val);
}
void pop() {
_con.pop_back();
}
T& top() {
return _con.back();
}
const T& top() const {
return _con.back();
}
size_t size() const {
return _con.size();
}
bool empty() const {
return _con.empty();
}
private:
Container _con;
};
4.1.3 stack经典应用
最小栈
cpp
class MinStack {
public:
void push(int val) {
dataStack.push(val);
// 如果最小栈为空或新值更小,同时压入最小栈
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
void pop() {
if (dataStack.top() == minStack.top()) {
minStack.pop();
}
dataStack.pop();
}
int top() {
return dataStack.top();
}
int getMin() {
return minStack.top();
}
private:
stack<int> dataStack;
stack<int> minStack;
};
有效的括号
cpp
class Solution {
public:
bool isValid(string s) {
stack<char> st;
for (char ch : s) {
if (ch == '(' || ch == '[' || ch == '{') {
st.push(ch);
} else {
if (st.empty()) return false;
char top = st.top();
if ((ch == ')' && top == '(') ||
(ch == ']' && top == '[') ||
(ch == '}' && top == '{')) {
st.pop();
} else {
return false;
}
}
}
return st.empty();
}
};
4.2 queue:先进先出(FIFO)
4.2.1 queue基本概念
queue是队列容器适配器,遵循先进先出原则。
cpp
#include <queue>
#include <iostream>
using namespace std;
void TestQueue() {
queue<int> q;
// 入队
q.push(1);
q.push(2);
q.push(3);
// 访问队首队尾
cout << "front: " << q.front() << endl; // 1
cout << "back: " << q.back() << endl; // 3
// 出队
q.pop();
cout << "after pop, front: " << q.front() << endl; // 2
// 大小
cout << "size: " << q.size() << endl;
}
4.2.2 queue模拟实现
cpp
template<class T, class Container = deque<T>>
class Queue {
public:
void push(const T& val) {
_con.push_back(val);
}
void pop() {
_con.pop_front();
}
T& front() {
return _con.front();
}
const T& front() const {
return _con.front();
}
T& back() {
return _con.back();
}
const T& back() const {
return _con.back();
}
size_t size() const {
return _con.size();
}
bool empty() const {
return _con.empty();
}
private:
Container _con;
};
4.2.3 queue经典应用
用栈实现队列
cpp
class MyQueue {
public:
void push(int x) {
// 总是压入输入栈
inStack.push(x);
}
int pop() {
// 如果输出栈为空,将输入栈所有元素转移
if (outStack.empty()) {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
int result = outStack.top();
outStack.pop();
return result;
}
int peek() {
// 复用pop逻辑但不删除
if (outStack.empty()) {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
return outStack.top();
}
bool empty() {
return inStack.empty() && outStack.empty();
}
private:
stack<int> inStack; // 输入栈
stack<int> outStack; // 输出栈
};
二叉树的层序遍历
cpp
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
};
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size();
vector<int> level;
for (int i = 0; i < levelSize; ++i) {
TreeNode* node = q.front();
q.pop();
level.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
result.push_back(level);
}
return result;
}
4.3 priority_queue:优先级队列
4.3.1 priority_queue基本概念
priority_queue底层是堆,默认是大顶堆(最大元素在顶部)。
cpp
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
void TestPriorityQueue() {
// 默认大顶堆
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
pq.push(5);
cout << "top: " << pq.top() << endl; // 5
// 小顶堆
priority_queue<int, vector<int>, greater<int>> minHeap;
minHeap.push(3);
minHeap.push(1);
minHeap.push(4);
cout << "minHeap top: " << minHeap.top() << endl; // 1
}
4.3.2 自定义类型优先级队列
cpp
struct Person {
string name;
int age;
// 重载<运算符(用于大顶堆)
bool operator<(const Person& other) const {
return age < other.age; // 年龄大的优先级高
}
// 重载>运算符(用于小顶堆)
bool operator>(const Person& other) const {
return age > other.age;
}
};
void TestCustomPriorityQueue() {
// 大顶堆:年龄大的在前
priority_queue<Person> pq;
pq.push({"Alice", 25});
pq.push({"Bob", 30});
pq.push({"Charlie", 20});
cout << pq.top().name << endl; // Bob
// 小顶堆:年龄小的在前
priority_queue<Person, vector<Person>, greater<Person>> minPq;
minPq.push({"Alice", 25});
minPq.push({"Bob", 30});
minPq.push({"Charlie", 20});
cout << minPq.top().name << endl; // Charlie
}
4.3.3 priority_queue模拟实现
cpp
template<class T, class Container = vector<T>, class Compare = less<T>>
class PriorityQueue {
public:
void push(const T& val) {
_con.push_back(val);
push_heap(_con.begin(), _con.end(), _comp);
}
void pop() {
pop_heap(_con.begin(), _con.end(), _comp);
_con.pop_back();
}
const T& top() const {
return _con.front();
}
size_t size() const {
return _con.size();
}
bool empty() const {
return _con.empty();
}
private:
Container _con;
Compare _comp;
};
4.3.4 经典应用:Top K问题
cpp
// 求数组中第K大的元素
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 维护一个大小为K的小顶堆
priority_queue<int, vector<int>, greater<int>> pq;
for (int num : nums) {
pq.push(num);
if (pq.size() > k) {
pq.pop(); // 保持堆的大小为K
}
}
return pq.top(); // 堆顶就是第K大的元素
}
};
4.4 容器适配器总结
| 特性 | stack | queue | priority_queue |
|---|---|---|---|
| 访问方式 | 仅栈顶 | 队首队尾 | 仅堆顶 |
| 底层容器 | vector/list/deque | list/deque | vector/deque |
| 默认容器 | deque | deque | vector |
| 主要操作 | push/pop/top | push/pop/front/back | push/pop/top |
| 时间复杂度 | O(1) | O(1) | push O(log n), pop O(log n) |
