• 包含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 }
  • 相关阅读:
    C# 随机生成姓名的方法
    Task 异步编程测试案例及基础应用说明
    C# 多线程 Parallel.For 和 For 谁的效率高?那么 Parallel.ForEach 和 ForEach 呢?
    C# SignalR 即时通信
    C#中out和ref之间的区别
    LInq之Take Skip TakeWhile SkipWhile Reverse Union Concat 用法
    C# LINQ 详解 From Where Select Group Into OrderBy Let Join
    JS 数组去重的几个方法
    attachEvent和addEventListener区别
    Event事件跨浏览器封装
  • 原文地址:https://www.cnblogs.com/lemonu/p/8821903.html
Copyright © 2020-2023  润新知