栈和队列基础训练(一)

题目一:

  请设计一个栈,除pop与push方法,还支持min方法,可返回栈元素中的最小值。push、pop和min三个方法的时间复杂度必须为O(1)。

  思路:题目要求时间复杂度为O(1),所以肯定不能用循环遍历的方式去解决,于是我们想到用空间换时间的方式去解决,如果我们已经维护好一个已经排好序的栈的话,那么min方法直接返回当前的栈顶元素就好了。于是我们可以定义另一个栈,然后用push方法和pop方法去维护这个栈。另一个栈的栈顶始终存的是在当前状态下最小的元素,如果再来一个数据小于另一个栈的栈顶,那么两个栈同时压入这个数。否则只是原栈压入,而另一个栈还是重复压入当前最小的数据。另外pop的时候两个栈需要同时弹出。

  注意:代码中找不到的类前面的博客都有介绍,这里就没有重复写了。

 1 import java.util.EmptyStackException;
 2 
 3 public class StackWithMin extends DoubleLinkedList<Integer> implements IStack<Integer> {
 4     @Override
 5     public void push(Integer e) {
 6         super.add(e);
 7         if (brother.empty()) {
 8             brother.push(e);
 9         } else {
10             Integer peek = brother.peek();
11             if (e < peek) {
12                 brother.push(e);
13             } else {
14                 brother.push(peek);
15             }
16         }
17     }
18 
19     @Override
20     public Integer pop() {
21         if (size <= 0)
22             throw new EmptyStackException();
23         ListNode<Integer> the = super.last.getPre();
24         Integer res = the.getData();
25 
26         the.getPre().setNext(last);
27         last.setPre(the.getPre());
28         the.setNext(null);
29         the.setPre(null);
30         size--;
31         brother.pop();
32         return res;
33     }
34 
35     @Override
36     public boolean empty() {
37         return getSize() == 0;
38     }
39 
40     @Override
41     public int getSize() {
42         return super.getSize();
43     }
44 
45     @Override
46     public Integer peek() {
47         return last.getPre().getData();
48     }
49 
50     private MyStack<Integer> brother = new MyStack<>();
51 
52     public int min() throws Exception {
53         // ListNode<T> h = first.getNext();
54         // int min = -1;
55         // while(h!=last){
56         // if ((int)(h.getData())<min){
57         // min = (int)(h.getData());
58         // }
59         // }
60         // return min;
61         if (!brother.empty())
62             return brother.peek();
63         else
64             throw new Exception("没有元素了");
65     }
66 
67     public static void main(String[] args) throws Exception {
68         StackWithMin stack = new StackWithMin();
69         stack.push(2);
70         stack.push(9);
71         stack.push(3);
72         stack.push(1);
73         stack.push(5);
74 
75         System.out.println(stack.min());  // 输出1
76     }
77 }
View Code

  我们可以发现另一个栈里面重复的元素太多,也是可以继续优化的,就是在上面的基础上,做些简单的判断即可,就是如果再来的数据如果大于另一个栈的栈顶元素,那么另一个栈就不重复压入当前最小的数据了。而这里pop的时候两个栈的栈顶就不需要同时弹出了,需要做大小判断然后根据大小弹出哪个栈的栈顶元素。

题目二:  

  请实现一种数据结构SetOfStacks,由多个栈组成,其中每个栈的大小为size,当前一个栈填满时,新建一个栈。该数据结构应支持与普通栈相同的push和pop操作。

  给定一个操作序列int[][2] ope,每个操作的第一个数代表操作类型,若为1,则为push操作,后一个数为应push的数字;若为2,则为pop操作,后一个数无意义。请返回一个int[][],为完成所有操作后的SetOfStacks,顺序应为从下到上,默认初始的SetOfStacks为空。保证数据合法。这道题目是作为面试题目,所以只有思路,没有结果。

  思路:这道题目很简单,但是这里有一个知识点需要注意,这道题目本身不知道size大小,所以必须使用ArrayList去实现,所以这里需要掌握的点就是二维动态数组的建立以及使用动态数组模拟栈的行为。

 1 import java.util.ArrayList;
 2 
 3 public class SetOfStacks {
 4     public ArrayList<ArrayList<Integer>> setOfStacks(int[][] ope, int size) {
 5         // 动态的二维数组
 6         ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
 7         ArrayList<Integer> currStack = new ArrayList<Integer>(size);// 正在操作的栈
 8         res.add(currStack);
 9         for (int[] opAndValue : ope) {
10             int op = opAndValue[0];
11             int value = opAndValue[1];
12 
13             if (op == 1) {
14                 if (currStack.size() == size) { // 当前满
15                     currStack = new ArrayList<Integer>(size);// 创建一个新的栈
16                     res.add(currStack);
17                 }
18                 currStack.add(value);
19             } else {// 出栈
20                 if (currStack.size() == 0) {
21                     res.remove(currStack);// 栈的列表中移除
22                     currStack = res.get(res.size() - 1);// 被操作的栈是列表中的上一个栈
23                 }
24                 currStack.remove(currStack.size() - 1);
25             }
26         }
27         return res;
28     }
29 }
View Code

题目三:

  实现一个MyQueue类,该类用两个栈来实现一个队列。

  思路:该题最原始的想法呢就是把另一个栈作为缓冲区,在入栈的时候直接压入堆栈即可,在弹出堆栈的时候把第一个堆栈的元素全部压入到第二个堆栈中然后弹出第二个堆栈的栈顶元素这样就完成了队列先进先出的要求,然后再把第二个堆栈的元素重新移到第一个堆栈中。但是这个思路如果重复出栈或者入栈的话就会导致频繁的移动元素操作,增加了不必要的开销。下面给出优化过后的代码。

 1 import java.util.Stack;
 2 
 3 public class QueueByTwoStack {
 4     Stack<Integer> stack1 = new Stack<Integer>();
 5     Stack<Integer> stack2 = new Stack<Integer>();
 6 
 7     public void enqueue(int node) {
 8         if (stack1.isEmpty()) {
 9             move(stack2, stack1);
10         }
11         stack1.push(node);
12     }
13 
14     public int dequeue() {
15         if (stack2.isEmpty())
16             move(stack1, stack2);
17         int result = stack2.pop();
18 
19         return result;
20     }
21 
22     private void move(Stack source, Stack target) {
23         while (!source.empty()) {
24             target.push(source.pop());
25         }
26     }
27 
28     public static void main(String[] args) {
29         QueueByTwoStack obj = new QueueByTwoStack();
30         obj.enqueue(1);
31         obj.enqueue(2);
32         System.out.println(obj.dequeue());// 1
33         obj.enqueue(3);
34 
35         System.out.println(obj.dequeue());// 2
36         obj.enqueue(4);
37         System.out.println(obj.dequeue());// 3
38         System.out.println(obj.dequeue());// 4
39         obj.enqueue(5);
40         System.out.println(obj.dequeue());// 5
41 
42     }
43 }
View Code
原文地址:https://www.cnblogs.com/xiaoyh/p/10387982.html