STL:list,stack和queue

🎯 三、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)
相关推荐
杜子不疼.2 小时前
【LeetCode 153 & 173_二分查找】寻找旋转排序数组中的最小值 & 缺失的数字
算法·leetcode·职场和发展
CSDN_RTKLIB2 小时前
【LeetCode 热题 HOT 100】两数之和
算法·leetcode·职场和发展
Tisfy2 小时前
LeetCode 2054.两个最好的不重叠活动:二分查找
算法·leetcode·二分查找·题解
ozyzo2 小时前
局部变量的产生
c++
_OP_CHEN2 小时前
【C++数据结构进阶】吃透 LRU Cache缓存算法:O (1) 效率缓存设计全解析
数据结构·数据库·c++·缓存·线程安全·内存优化·lru
Looooking2 小时前
Python 之通过一个天平找出9个小球中唯一重量较轻的小球
python·算法
white-persist2 小时前
【攻防世界】reverse | tt3441810 详细题解 WP
java·c语言·开发语言·数据结构·c++·算法·安全
YGGP2 小时前
【Golang】LeetCode 70. 爬楼梯
算法·leetcode