《C++栈和队列》详解:程序员的“单行道”与“流水线”

一、开篇:程序员的“食堂哲学”

假设你是一个程序员,正坐在公司食堂排队打饭。此时,食堂阿姨的窗口只有一个,队伍必须先进先出(FIFO)——这就是队列的精髓。而当你吃完饭想把盘子放回洗碗池时,洗碗阿姨只会从最上面拿走一个盘子——这便是的“后进先出”(LIFO)哲学。

栈和队列,这两个看似简单的数据结构,却是程序员世界里最基础的“交通规则”。它们像两条平行的单行道:栈是“只进不出”的单行道(只能从顶部操作),而队列是“只进只出”的双向道(队尾进,队头出)。


二、原理篇:LIFO vs FIFO

1. 栈(Stack)

LIFO(Last In, First Out):就像你叠盘子,最后一个放上去的盘子,必须第一个被拿走。

  • 核心操作
    • push:往栈顶压入元素(叠盘子)。
    • pop:弹出栈顶元素(拿走最上面的盘子)。
    • top:查看栈顶元素(看看最上面是什么盘子)。

应用场景

  • 函数调用栈(递归调用时的“记忆点”)。
  • 括号匹配(比如 {[()]} 的匹配问题)。
  • 撤销/重做功能(比如 Word 的 Ctrl+Z)。

2. 队列(Queue)

FIFO(First In, First Out):就像食堂排队,先来的先打饭。

  • 核心操作
    • push:在队尾添加元素(排队的人站在队尾)。
    • pop:移除队头元素(打饭的人从队头出)。
    • front:查看队头元素(看谁该打饭了)。

应用场景

  • 任务调度(比如打印队列)。
  • 广度优先搜索(BFS)。
  • 网络请求处理(比如限流队列)。

三、实现篇:C++代码大乱斗

1. 栈的实现(数组 vs 链表)

数组实现(简单粗暴)
#include <iostream>  
using namespace std;  

class MyStack {  
private:  
    int* arr;  
    int top;  // 栈顶指针  
    int capacity;  // 容量  

public:  
    MyStack(int size) {  
        arr = new int[size];  
        capacity = size;  
        top = 0;  // 初始栈顶为0  
    }  

    void push(int x) {  
        if (top >= capacity) {  
            cout << "栈满啦!别压了,洗碗阿姨会崩溃的!" << endl;  
            return;  
        }  
        arr[top++] = x;  
    }  

    int pop() {  
        if (top <= 0) {  
            cout << "栈空啦!盘子都洗完了!" << endl;  
            return -1;  
        }  
        return arr[--top];  
    }  

    int topValue() {  
        if (top <= 0) {  
            cout << "栈空啦!啥都没有!" << endl;  
            return -1;  
        }  
        return arr[top - 1];  
    }  
};  
int main() {  
    MyStack s(5);  
    s.push(1);  // 压入1号盘子  
    s.push(2);  // 压入2号盘子  
    s.push(3);  // 压入3号盘子  
    cout << "栈顶元素: " << s.topValue() << endl;  // 输出3  
    s.pop();    // 弹出3号盘子  
    cout << "新栈顶元素: " << s.topValue() << endl;  // 输出2  
    return 0;  
}  
链表实现(灵活多变)
struct Node {  
    int data;  
    Node* next;  
    Node(int x) : data(x), next(nullptr) {}  
};  

class MyStackLinkedList {  
private:  
    Node* top;  // 栈顶指针  

public:  
    MyStackLinkedList() { top = nullptr; }  

    void push(int x) {  
        Node* newNode = new Node(x);  
        newNode->next = top;  
        top = newNode;  
    }  

    int pop() {  
        if (top == nullptr) {  
            cout << "链表栈空啦!别弹了!" << endl;  
            return -1;  
        }  
        Node* temp = top;  
        int val = temp->data;  
        top = top->next;  
        delete temp;  
        return val;  
    }  
};  

2. 队列的实现(环形数组 vs 链表)

环形数组实现(高效省空间)
class MyQueue {  
private:  
    int* arr;  
    int front;  // 队头指针  
    int rear;   // 队尾指针  
    int capacity;  

public:  
    MyQueue(int size) {  
        capacity = size;  
        arr = new int[capacity];  
        front = 0;  
        rear = 0;  // 初始队列为空  
    }  

    void push(int x) {  
        if ((rear + 1) % capacity == front) {  
            cout << "队列满啦!食堂窗口堵住了!" << endl;  
            return;  
        }  
        arr[rear] = x;  
        rear = (rear + 1) % capacity;  
    }  

    int pop() {  
        if (front == rear) {  
            cout << "队列空啦!没人排队了!" << endl;  
            return -1;  
        }  
        int val = arr[front];  
        front = (front + 1) % capacity;  
        return val;  
    }  
};  
链表实现(动态扩展)
struct QueueNode {  
    int data;  
    QueueNode* next;  
    QueueNode(int x) : data(x), next(nullptr) {}  
};  

class MyQueueLinkedList {  
private:  
    QueueNode* front;  // 队头  
    QueueNode* rear;   // 队尾  

public:  
    MyQueueLinkedList() { front = rear = nullptr; }  

    void push(int x) {  
        QueueNode* newNode = new QueueNode(x);  
        if (rear == nullptr) {  // 空队列  
            front = rear = newNode;  
        } else {  
            rear->next = newNode;  
            rear = newNode;  
        }  
    }  

    int pop() {  
        if (front == nullptr) {  
            cout << "链表队列空啦!没饭吃了!" << endl;  
            return -1;  
        }  
        int val = front->data;  
        QueueNode* temp = front;  
        front = front->next;  
        delete temp;  
        return val;  
    }  
};  

四、实战篇:栈和队列的“江湖对决”

1. 用栈实现队列(双栈设计哲学)

场景:你有两个栈,一个负责“接收订单”(输入栈),另一个负责“出货”(输出栈)。

class MyQueueUsingStack {  
private:  
    stack<int> inStack, outStack;  

    void transfer() {  // 元素转移  
        while (!inStack.empty()) {  
            outStack.push(inStack.top());  
            inStack.pop();  
        }  
    }  

public:  
    void push(int x) {  
        inStack.push(x);  // 订单进入输入栈  
    }  

    int pop() {  
        if (outStack.empty()) transfer();  // 输出栈空则转移  
        int val = outStack.top();  
        outStack.pop();  
        return val;  
    }  
};  

总结

输入栈就像“食堂窗口”,输出栈是“打饭阿姨的托盘”。每次打饭前,阿姨都要把窗口的盘子倒进托盘,才能按顺序发饭。

2. 用队列实现栈(双队列设计哲学)

场景:你有两个队列,每次压栈时,将元素放入非空队列,然后将原队列的其他元素转移到另一个队列。

class MyStackUsingQueue {  
private:  
    queue<int> q1, q2;  

public:  
    void push(int x) {  
        if (q1.empty()) {  
            q2.push(x);  
            while (!q1.empty()) {  
                q2.push(q1.front());  
                q1.pop();  
            }  
        } else {  
            q1.push(x);  
            while (!q2.empty()) {  
                q1.push(q2.front());  
                q2.pop();  
            }  
        }  
    }  

    int pop() {  
        if (q1.empty()) return q2.front();  
        else return q1.front();  
    }  
};  

总结

这就像两个食堂窗口,每次有人插队时,都要把前面的人转移到另一个窗口,才能保证“最后来的先打饭”。


五、高阶应用:栈和队列的“跨界合作”

1. 括号匹配问题(栈的经典应用)

bool isValid(string s) {  
    stack<char> st;  
    for (char c : s) {  
        if (c == '(' || c == '[' || c == '{') {  
            st.push(c);  // 遇到左括号压栈  
        } else {  
            if (st.empty()) return false;  
            char top = st.top();  
            st.pop();  
            if ((c == ')' && top != '(') ||  
                (c == ']' && top != '[') ||  
                (c == '}' && top != '{')) {  
                return false;  
            }  
        }  
    }  
    return st.empty();  
}  

测试

如果你输入 ([)],程序会像食堂阿姨一样怒斥:“括号不匹配,重新来!”

2. 广度优先搜索(BFS)(队列的典型场景)

void BFS(Node* root) {  
    queue<Node*> q;  
    q.push(root);  
    while (!q.empty()) {  
        Node* current = q.front();  
        q.pop();  
        // 处理当前节点  
        for (auto child : current->children) {  
            q.push(child);  // 将子节点入队  
        }  
    }  
}  

总结

BFS就像食堂的“传菜链”,每一道菜都会被“出队”处理,然后把下一道菜“入队”。


六、结语:程序员的“单行道”人生

栈和队列,是程序员世界中最基础的“交通规则”。它们看似简单,却能构建出复杂的算法和系统。正如食堂阿姨的窗口和洗碗池,程序员的生活也充满了这些“单行道”与“流水线”。

最后送大家一句话

“人生如栈,越努力越要‘出栈’;人生如队,越耐心越能‘出队’!”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值