【数据结构与算法】栈与队列:从基础到实战,代码案例+应用场景全解析!

开篇互动:你的代码中是否还在手动管理数据顺序?

“是否还在为数据先进先出、后进先出的顺序而烦恼?”
栈(Stack)和队列(Queue)是计算机科学中最基础且最重要的数据结构之一。无论是浏览器的前进后退、打印机的任务处理,还是括号匹配、迷宫求解,栈和队列的身影无处不在。

这篇文章将从栈和队列的基础概念出发,结合代码案例经典问题实际应用场景,手把手教你掌握这两个数据结构的核心知识!文末还有常见问题解答实战技巧分享哦~✨

【数据结构与算法】栈与队列:从基础到实战,代码案例+应用场景全解析!_第1张图片

 

一、栈:先进后出(FILO)

1.1 栈的定义

栈是一种只能在一端进行插入或删除操作的线性数据结构,遵循“先进后出”(First In Last Out, FILO)的原则。想象一下洗碗池的水槽,每次只能从最上面拿走盘子。


1.2 栈的核心操作

1.2.1 入栈(Push)

将元素压入栈顶。

public void push(int value) {
    if (top == size - 1) {
        System.out.println(" 栈已满,无法入栈!");
        return;
    }
    top++;
    arr[top] = value;
}
1.2.2 出栈(Pop)

从栈顶取出元素。

public int pop() {
    if (top == -1) {
        System.out.println(" 栈为空,无法出栈!");
        return -1;
    }
    int poppedValue = arr[top];
    top--;
    return poppedValue;
}
1.2.3 查看栈顶元素(Peek)

查看栈顶元素而不移除它。

public int peek() {
    if (top == -1) {
        System.out.println(" 栈为空!");
        return -1;
    }
    return arr[top];
}

1.3 栈的应用场景

  1. 浏览器历史记录:前进和后退功能。
  2. 括号匹配:验证字符串中的括号是否正确匹配。
  3. 递归实现:递归的本质就是利用栈结构。

二、队列:先进先出(FIFO)

2.1 队列的定义

队列是一种只能在一端插入而在另一端删除的线性数据结构,遵循“先进先出”(First In First Out, FIFO)的原则。想象一下银行排队系统,先来的人先办理业务。


2.2 队列的核心操作

2.2.1 入队(Enqueue)

将元素添加到队尾。

public void enqueue(int value) {
    if (rear == size - 1) {
        System.out.println(" 队列已满,无法入队!");
        return;
    }
    rear++;
    arr[rear] = value;
}
2.2.2 出队(Dequeue)

从队头取出元素。

public int dequeue() {
    if (front > rear) {
        System.out.println(" 队列为空,无法出队!");
        return -1;
    }
    int dequeuedValue = arr[front];
    front++;
    return dequeuedValue;
}
2.2.3 查看队头元素

查看队头元素而不移除它。

public int peekFront() {
    if (front > rear) {
        System.out.println(" 队列为空!");
        return -1;
    }
    return arr[front];
}

2.3 队列的应用场景

  1. 打印机任务处理:多个打印任务按照提交顺序处理。
  2. 多线程任务调度:任务按照提交顺序执行。
  3. 广度优先搜索(BFS):用于图的遍历算法。

三、栈与队列的经典问题

3.1 括号匹配问题

验证字符串中的括号是否正确匹配是一个经典的栈应用问题。

3.1.1 实现思路
  • 遇到左括号(( 或 { 或 [)时入栈。
  • 遇到右括号时,检查栈顶是否匹配的左括号。
  • 最终判断栈是否为空。
3.1.2 代码实现
public boolean isValid(String s) {
    Stack stack = new Stack<>();
    for (char c : s.toCharArray())  {
        if (c == '(' || c == '{' || c == '[') {
            stack.push(c); 
        } else {
            if (stack.isEmpty())  {
                return false;
            }
            char top = stack.pop(); 
            if ((c == ')' && top != '(') ||
                (c == '}' && top != '{') ||
                (c == ']' && top != '[')) {
                return false;
            }
        }
    }
    return stack.isEmpty(); 
}

3.2 迷宫求解问题

迷宫求解可以通过栈实现深度优先搜索(DFS)。

3.2.1 实现思路
  • 使用栈记录路径。
  • 每次从当前位置尝试四个方向(上下左右)。
  • 如果找到出口则返回路径;如果无法前进则回溯。
3.2.2 代码实现
class Point {
    int x;
    int y;
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
 
public List solveMaze(int[][] maze, Point start, Point end) {
    Stack stack = new Stack<>();
    stack.push(start); 
    maze[start.x][start.y] = 1; // 标记已访问 
 
    while (!stack.isEmpty())  {
        Point current = stack.peek(); 
        if (current.x == end.x && current.y == end.y) {
            return stack;
        }
        // 尝试四个方向 
        boolean canMove = false;
        // 上 
        if (current.x > 0 && maze[current.x - 1][current.y] == 0) {
            stack.push(new  Point(current.x - 1, current.y));
            maze[current.x - 1][current.y] = 1;
            canMove = true;
            continue;
        }
        // 下 
        if (current.x < maze.length  - 1 && maze[current.x + 1][current.y] == 0) {
            stack.push(new  Point(current.x + 1, current.y));
            maze[current.x + 1][current.y] = 1;
            canMove = true;
            continue;
        }
        // 左 
        if (current.y > 0 && maze[current.x][current.y - 1] == 0) {
            stack.push(new  Point(current.x, current.y - 1));
            maze[current.x][current.y - 1] = 1;
            canMove = true;
            continue;
        }
        // 右 
        if (current.y < maze[0].length - 1 && maze[current.x][current.y + 1] == 0) {
            stack.push(new  Point(current.x, current.y + 1));
            maze[current.x][current.y + 1] = 1;
            canMove = true;
            continue;
        }
        if (!canMove) {
            stack.pop(); 
        }
    }
    return null; // 无解 
}

3.3 约瑟夫环问题

约瑟夫环问题是一个经典的队列应用问题。

3.3.1 实现思路
  • 将所有人加入队列。
  • 按照步长逐个移出并重新入队。
  • 最后剩下的即为幸存者。
3.3.2 代码实现
public int josephusProblem(int n, int k) {
    Queue queue = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        queue.add(i); 
    }
    while (queue.size()  > 1) {
        for (int i = 0; i < k - 1; i++) {
            int front = queue.poll(); 
            queue.add(front); 
        }
        queue.poll();  // 移除第k个人 
    }
    return queue.poll(); 
}

四、栈与队列的不同实现方式

4.1 数组实现

4.1.1 栈的数组实现
public class ArrayStack {
    private int[] arr;
    private int top;
    private int size;
 
    public ArrayStack(int size) {
        this.size  = size;
        arr = new int[size];
        top = -1;
    }
 
    public void push(int value) { /* 实现同上 */ }
    public int pop() { /* 实现同上 */ }
    public int peek() { /* 实现同上 */ }
}
4.1.2 队列的数组实现
public class ArrayQueue {
    private int[] arr;
    private int front;
    private int rear;
    private int size;
 
    public ArrayQueue(int size) {
        this.size  = size;
        arr = new int[size];
        front = -1;
        rear = -1;
    }
 
    public void enqueue(int value) { /* 实现同上 */ }
    public int dequeue() { /* 实现同上 */ }
}

4.2 链表实现

4.2.1 栈的链表实现
class Node {
    int value;
    Node next;
 
    Node(int value) {
        this.value  = value;
        this.next  = null;
    }
}
 
public class LinkedListStack {
    private Node top;
    private int size;
 
    public LinkedListStack() {
        top = null;
        size = 0;
    }
 
    public void push(int value) {
        Node newNode = new Node(value);
        if (top == null) {
            top = newNode;
        } else {
            newNode.next  = top;
            top = newNode;
        }
        size++;
    }
 
    public int pop() {
        if (top == null) {
            System.out.println(" 栈为空!");
            return -1;
        }
        int poppedValue = top.value; 
        top = top.next; 
        size--;
        return poppedValue;
    }
}
4.2.2 队列的链表实现
class Node {
    int value;
    Node next;
 
    Node(int value) {
        this.value  = value;
        this.next  = null;
    }
}
 
public class LinkedListQueue {
    private Node front;
    private Node rear;
    private int size;
 
    public LinkedListQueue() {
        front = null;
        rear = null;
        size = 0;
    }
 
    public void enqueue(int value) {
        Node newNode = new Node(value);
        if (front == null) {
            front = newNode;
            rear = newNode;
        } else {
            rear.next  = newNode;
            rear = newNode;
        }
        size++;
    }
 
    public int dequeue() {
        if (front == null) {
            System.out.println(" 队列为空!");
            return -1;
        }
        int dequeuedValue = front.value; 
        front = front.next; 
        size--;
        return dequeuedValue;
    }
}

五、栈与队列的优缺点对比

特性 栈(Stack) 队列(Queue)
数据访问顺序 后进先出(FILO) 先进先出(FIFO)
应用场景 递归、回溯、括号匹配等 排队系统、任务调度等
插入/删除效率 O(1) O(1)
空间复杂度 O(n) O(n)

六、实际应用场景案例

6.1 浏览器前进后退功能

使用两个栈实现浏览器的前进后退功能。

6.1.1 实现思路
  • 使用一个主栈保存浏览历史。
  • 使用一个辅助栈保存待前进的历史。
6.1.2 代码实现
public class BrowserHistory {
    Stack historyStack = new Stack<>();
    Stack forwardStack = new Stack<>();
 
    public void visit(String url) {
        historyStack.push(url); 
        forwardStack.clear(); 
    }
 
    public String back() {
        if (!historyStack.isEmpty())  {
            String backUrl = historyStack.pop(); 
            forwardStack.push(backUrl); 
            return backUrl;
        }
        return null;
    }
 
    public String forward() {
        if (!forwardStack.isEmpty())  {
            String forwardUrl = forwardStack.pop(); 
            historyStack.push(forwardUrl); 
            return forwardUrl;
        }
        return null;
    }
}

6.2 打印机任务处理

使用队列实现打印机的任务处理。

6.2.1 实现思路
  • 将打印任务加入队列。
  • 按照顺序处理任务。
6.2.2 代码实现
public class PrinterQueue {
    Queue taskQueue = new LinkedList<>();
 
    public void addTask(String task) {
        taskQueue.add(task); 
        System.out.println(" 任务已添加:" + task);
    }
 
    public void processTasks() {
        while (!taskQueue.isEmpty())  {
            String task = taskQueue.poll(); 
            System.out.println(" 正在打印:" + task);
            try {
                Thread.sleep(1000);  // 模拟打印时间 
            } catch (InterruptedException e) {
                e.printStackTrace(); 
            }
            System.out.println(" 打印完成:" + task);
        }
    }
}

七、结语与互动

通过这篇文章,你已经掌握了栈和队列的核心知识及其在实际中的应用。如果你有任何疑问或想看到更多实战案例,请在评论区留言!我会逐一回复并分享更多技巧。

P.S. 如果你有任何技术问题或学习困惑,欢迎随时在评论区留言!我会尽力帮助你!

你可能感兴趣的:(java,开发语言,算法,数据结构)