包含min函数的栈、队列

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈/队列的最小元素的min函数。在该栈/队列中,调用min、入栈(入队列)及出栈(出队列)函数的时间复杂度都是O(1)。

1. 包含min函数的栈

  看到这个问题,第一反应是创建一个成员变量保存栈中当前的最小元素。每次压入一个新元素进栈时,如果该元素比当前最小的元素还要小,则更新最小元素。采用这种思路,元素入栈的时候没有问题,但出栈时如果最小的元素被弹出栈了,就无法得到下一个最小的元素。如图,元素入栈出栈过程如下,当元素3出栈时,无法确定栈中当前最小元素4。

 

元素入栈出栈过程分析

  分析到这里,我们发现仅仅添加一个成员变量存放最小元素时不够的,也就是当最小元素被弹出栈的时候,我们希望能够得到次小元素。因此,在压入这个最小元素之前,需要把次小元素保存起来,于是采用一个“辅助栈”保存历史最小元素。还是以上面的入栈出栈为例,多了一个辅助栈,当元素3出栈时,依然可以的到栈的当前最小元素。

元素入栈出栈过程分析

算法的代码实现如下:

 1 import java.util.LinkedList;
 2 import java.util.Stack;
 3 
 4 public class d30_MinInStack {
 5     public static void main(String[] args) {
 6         // 测试包含min函数的栈
 7         StackWithMin<Integer> stack = new StackWithMin<>();
 8         stack.push(3);
 9         stack.push(4);
10         stack.push(2);
11         stack.pop(); // 当前最小元素出栈,则minStack最小元素也要出栈
12         stack.push(0);
13         stack.push(-1);
14         System.out.println(stack.dataStack); // [3, 4, 0, -1]
15         System.out.println(stack.minStack); // [3, 0, -1]
16         System.out.println(stack.min()); // -1
17     }
18 
19     /*
20      * 包含min函数的栈
21      */
22     static class StackWithMin<T extends Comparable<T>> {
23         private Stack<T> dataStack; // 记录实际元素的栈
24         private Stack<T> minStack; // 记录最小元素的栈
25 
26         public StackWithMin() {
27             this.dataStack = new Stack<>();
28             this.minStack = new Stack<>();
29         }
30 
31         /*
32          * 元素入栈
33          */
34         public void push(T data) {
35             if (data == null) {
36                 throw new RuntimeException("cannot push null");
37             }
38             if (dataStack.isEmpty()) {
39                 dataStack.push(data);
40                 minStack.push(data);
41             } else {
42                 dataStack.push(data);
43                 // 记录最小栈的当前最小元素
44                 T curMin = minStack.peek();
45                 // 新入栈函数小于curMin
46                 if (data.compareTo(curMin) < 0) {
47                     minStack.push(dataStack.peek());
48                 }
49             }
50         }
51 
52         /*
53          * 元素出栈
54          */
55         public T pop() {
56             if (dataStack.isEmpty()) {
57                 throw new RuntimeException("dataStack is empty!");
58             }
59             // 如果出栈元素为dataStack栈的当前最小元素,minStack的栈顶元素出栈
60             if (dataStack.peek() == minStack.peek()) {
61                 minStack.pop();
62             }
63             return dataStack.pop();
64         }
65 
66         /*
67          * 取栈中最小元素
68          */
69         public T min() {
70             if (minStack.isEmpty()) {
71                 throw new RuntimeException("minStack is empty!");
72             }
73             return minStack.peek();
74         }
75     }
76 }

2. 包含min函数的队列

  有了上面对栈的分析作为基础,实现包含min函数的队列就比较容易了。实现思路还是创建“辅助队列”,但与“辅助栈”不同的是,“辅助队列”并不是保存队列中历史最小元素。如果不是最小元素入队列,则“辅助队列”以递减的方式保存元素;如果是最小元素入队列,则“辅助队列”将其他元素出队,只保存该最小元素。具体的过程如下图所示:

元素入队出队过程分析

算法实现代码如下:

 1 import java.util.LinkedList;
 2 import java.util.Stack;
 3 
 4 public class d30_MinInStack {
 5 
 6     public static void main(String[] args) {
 7         // 测试包含min函数的队列
 8         QueueWithMin<Integer> queue = new QueueWithMin<>();
 9         queue.enqueue(3);
10         queue.enqueue(4);
11         queue.enqueue(7);
12         System.out.println(queue.min());
13         queue.enqueue(5);
14         System.out.println();
15         System.out.println(queue.min());
16     }
17 
18     /*
19      * 包含min函数的队列
20      */
21     public static class QueueWithMin<T extends Comparable<T>> {
22         private LinkedList<T> dataQueue;
23         private LinkedList<T> minQueue;
24 
25         public QueueWithMin() {
26             dataQueue = new LinkedList<>();
27             minQueue = new LinkedList<>();
28         }
29 
30         // 元素入队
31         public void enqueue(T data) {
32             if (data == null) {
33                 throw new RuntimeException("cannot enqueue null");
34             }
35             dataQueue.offer(data);
36             // 保证minQueue队列的元素递减
37             // 若data < 队头元素,则minQueue队头元素出队
38             while (!minQueue.isEmpty() && (data.compareTo(minQueue.peek()) < 0)) {
39                 minQueue.poll();
40             }
41             // 若data < 队尾元素,则minQueue队尾元素出队
42            while(!minQueue.isEmpty() && (data.compareTo(minQueue.peekLast()) < 0)) {
43                 minQueue.poll();
44             }
45             minQueue.offer(data);
46         }
47 
48         // 元素出队
49         public T dequeue() {
50             if (dataQueue.isEmpty()) {
51                 throw new RuntimeException("dataQueue is empty!");
52             }
53             // dataQueue出队元素与minQueue队头元素相等,则minQueue队头元素出队
54             if (dataQueue.peek() == minQueue.peek()) {
55                 minQueue.poll();
56             }
57             return dataQueue.poll();
58         }
59 
60         // 取队列最小元素
61         public T min() {
62             if (minQueue.isEmpty()) {
63                 throw new RuntimeException("minQueue is empty!");
64             }
65             return minQueue.peek();
66         }
67     }
69 }
原文地址:https://www.cnblogs.com/lemonu/p/8821903.html