好好学数据结构-栈与队列

638 阅读3分钟

这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

栈与队列

熟练掌握数据结构的基本原理,栈与队列问题处理起来要容易得多。当然,有些问题也可能相当棘手。部分问题不过是对基本数据结构略作调整,其他问题则要难得多。

实现一个栈

栈这种数据结构正如其名:存放数据之处。在某些特定的问题中,栈比数组更加合适。

栈采用后进先出(LIFO)的顺序。换言之,像一堆盘子那样,最后入栈的元素最先出栈。

栈有如下基本操作。

  • pop():移除栈顶元素。
  • push(item):在栈顶加入一个元素。
  • peek():返回栈顶元素。
  • isEmpty():当且仅当栈为空时返回true

与数组不同的是,栈无法在常数时间复杂度内访问第i个元素。但是,因为栈不需要在添加和删除操作时移动元素,所以可以在常数时间复杂度内完成此类操作。

下面给出了栈的简单实现代码。注意,如果只从链表的一端添加和删除元素,栈也可以用链表实现。

1.   public class MyStack<T> {
2.     private static class StackNode<T> {
3.       private T data;
4.       private StackNode<T> next;
5.
6.       public StackNode(T data) {
7.         this.data = data;
8.       }
9.     }
10.
11.    private StackNode<T> top;
12.
13.    public T pop() {
14.      if (top == null) throw new EmptyStackException();
15.      T item = top.data;
16.      top = top.next;
17.      return item;
18.    }
19.
20.    public void push(T item) {
21.      StackNode<T> t = new StackNode<T>(item);
22.      t.next = top;
23.      top = t;
24.    }
25.
26.    public T peek() {
27.      if (top == null) throw new EmptyStackException();
28.      return top.data;
29.    }
30.
31.    public boolean isEmpty() {
32.      return top == null;
33.    }
34.  }

对于某些递归算法,栈通常大有用处。有时,你需要在递归时把临时数据加入到栈中,在回溯时(例如,在递归判断失败时)再删除该数据。栈是实现这类算法的一种直观方法。

当使用迭代法实现递归算法时,栈也可派上用场。(这是一个很好的练习项目。选择一个简单的递归算法并用迭代法实现该算法。)

实现一个队列

队列采用先进先出(FIFO)的顺序。就像一支排队购票的队伍那样,最早入列的元素也是最先出列的。

队列有如下基本操作。

  • add():在队列尾部加入一个元素。
  • remove():移除队列第一个元素。
  • peek():返回队列顶部元素。
  • isEmpty():当且仅当队列为空时返回true

队列也可以用链表实现。事实上,只要元素是从链表的相反的两端添加和删除的,链表和队列本质上就是一样的。

更新队列当中第一个和最后一个节点很容易出错,请务必再三确认。

队列常用于广度优先搜索或缓存的实现中。

例如,在广度优先搜索中,我们使用队列来存储需要被处理的节点。每处理一个节点时,就把其相邻节点加入到队列的尾端。这使得我们可以按照发现节点的顺序处理各个节点。