数据结构之栈和队列

时间:2021-05-11 17:43:17

  第一篇:数据结构之链表

  

  这次,我们主要看栈和队列的相关题目。

  栈是“后进先出”的数据结构,队列是“先进先出”的数据结构,我们假设栈和队列中存储的都是整型数值,先来定义这两种数据结构(都采用数组的形式来存储信息,当达到数组边界时,对数组进行扩容处理)。

  栈主要包括Push、Pop和Peek和Count三个方法,如下:

数据结构之栈和队列数据结构之栈和队列栈的定义
 1 public class Stack {
2 private int[] arrValue = null;
3 private int m_Capacity;
4 private int m_Count;
5 private void set_Count(int m_Count) {
6 this.m_Count = m_Count;
7 }
8
9 public int get_Count() {
10 return m_Count;
11 }
12
13 public Stack(int capacity)
14 {
15 m_Capacity = capacity;
16 arrValue = new int[capacity];
17 set_Count(0);
18 }
19
20 public void push(int value)
21 {
22 if (get_Count() == m_Capacity)
23 {
24 System.out.println("Need to malloc more space for stack.");
25 int[] temp = new int[m_Capacity*2];
26 for(int i = 0; i < m_Capacity; i++)
27 {
28 temp[i] = arrValue[i];
29 }
30 arrValue = temp;
31 m_Capacity = m_Capacity*2;
32 System.out.println("The capacity of stack is " + m_Capacity);
33 }
34 set_Count(get_Count() + 1);
35 arrValue[get_Count() - 1] = value;
36
37 }
38
39 public int peek()
40 {
41 if (get_Count() == 0)
42 {
43 System.out.println("Stack is empty;");
44 return Integer.MIN_VALUE;
45 }
46 return arrValue[get_Count() - 1];
47 }
48
49 public int pop()
50 {
51 if (get_Count() == 0)
52 {
53 System.out.println("Stack is empty;");
54 return Integer.MIN_VALUE;
55 }
56 int result = arrValue[get_Count() - 1];
57 set_Count(get_Count() - 1);
58 return result;
59 }
60 }

  队列主要包括EnQueue和DeQueue两个方法,如下:

数据结构之栈和队列数据结构之栈和队列队列的定义
 1 public class Queue {
2 private int m_Capacity;
3 private int m_Count;
4 private int[] m_arrValue;
5
6 public Queue(int capacity)
7 {
8 m_Capacity = capacity;
9 m_arrValue = new int[capacity];
10 set_Count(0);
11 }
12
13 public void enQueue(int value)
14 {
15 if (get_Count() == m_Capacity)
16 {
17 System.out.println("Need to malloc more space for queue.");
18 int[] temp = new int[m_Capacity*2];
19 for(int i = 0; i < m_Capacity; i++)
20 {
21 temp[i] = m_arrValue[i];
22 }
23 m_arrValue = temp;
24 m_Capacity = m_Capacity*2;
25 System.out.println("The capacity of queue is " + m_Capacity);
26 }
27 set_Count(get_Count() + 1);
28 m_arrValue[get_Count() - 1] = value;
29 }
30
31 public int deQueue()
32 {
33 if(get_Count() == 0)
34 {
35 System.out.println("The queue is empty.");
36 return Integer.MIN_VALUE;
37 }
38 int result = m_arrValue[0];
39 for (int i = 1; i < get_Count(); i++)
40 {
41 m_arrValue[ i - 1] = m_arrValue[i];
42 }
43 set_Count(get_Count() - 1);
44 return result;
45 }
46
47 private void set_Count(int m_Count) {
48 this.m_Count = m_Count;
49 }
50
51 public int get_Count() {
52 return m_Count;
53 }
54 }

  下面,我们来看和这两种数据类型相关的题目。

  • 设计含有min函数的栈,要求min、push和pop的时间复杂度是O(1)。
    思路:使用一个栈来实现上述要求,在栈顶保存min()值,每次Push或者Pop,向栈操作两次,第一次针对min,第二次是实际的值,这样我们可以使用Peek方法来取得min值。
    数据结构之栈和队列数据结构之栈和队列含有min的栈
     1 public class NewStack {
    2 private int[] m_arrValue = null;
    3 private int m_Capacity;
    4 private int m_Count;
    5
    6 public NewStack(int capacity)
    7 {
    8 m_arrValue = new int[2*capacity];
    9 m_Capacity = 2*capacity;
    10 m_Count = 0;
    11 }
    12
    13 public void push(int value)
    14 {
    15 if (m_Count * 2 + 2 > m_Capacity)
    16 {
    17 System.out.println("Need to malloc more space for stack.");
    18 int[] temp = new int[m_Capacity*2];
    19 for(int i = 0; i < m_Capacity; i++)
    20 {
    21 temp[i] = m_arrValue[i];
    22 }
    23 m_arrValue = temp;
    24 m_Capacity = m_Capacity*2;
    25 System.out.println("The capacity of stack is " + m_Capacity/2);
    26 }
    27 int min = Integer.MAX_VALUE;
    28 if (m_Count > 0)
    29 {
    30 min = m_arrValue[2*m_Count - 1];
    31 }
    32 m_Count++;
    33 m_arrValue[2*m_Count -2] = value;
    34 m_arrValue[2*m_Count - 1] = min < value ? min : value;
    35 }
    36
    37 public int peek()
    38 {
    39 if (m_Count == 0)
    40 {
    41 System.out.println("stack is empty.");
    42 return Integer.MIN_VALUE;
    43 }
    44 return m_arrValue[2*m_Count - 2];
    45 }
    46
    47 public int min()
    48 {
    49 if (m_Count == 0)
    50 {
    51 System.out.println("stack is empty.");
    52 return Integer.MIN_VALUE;
    53 }
    54 return m_arrValue[2*m_Count - 1];
    55 }
    56
    57 public int pop()
    58 {
    59 if (m_Count == 0)
    60 {
    61 System.out.println("stack is empty.");
    62 return Integer.MIN_VALUE;
    63 }
    64 int result = m_arrValue[2*m_Count - 2];
    65 m_Count--;
    66 return result;
    67 }
    68
    69 public void set_Count(int m_Count) {
    70 this.m_Count = m_Count;
    71 }
    72
    73 public int get_Count() {
    74 return m_Count;
    75 }
    76 }

    上述代码是从头开始实现的栈,包括对存储结构的处理细节,我们可以看上面关于Stack的定义,其实已经包括了这些细节,因此我们可以直接拿来用,代码会更清晰。

    数据结构之栈和队列数据结构之栈和队列含有min的栈
     1 public class NewStack2 {
    2 private Stack stack;
    3
    4 public NewStack2(int capacity)
    5 {
    6 stack = new Stack(capacity*2);
    7 }
    8
    9 public void push(int value)
    10 {
    11 if(stack.get_Count() == 0)
    12 {
    13 stack.push(value);
    14 stack.push(value);
    15 }
    16 else
    17 {
    18 int min = stack.peek();
    19 stack.push(value);
    20 stack.push(value > min ? min : value);
    21 }
    22 }
    23
    24 public int min()
    25 {
    26 return stack.peek();
    27 }
    28
    29 public int pop()
    30 {
    31 stack.pop();
    32 return stack.pop();
    33 }
    34
    35 public int get_Count()
    36 {
    37 return stack.get_Count()/2;
    38 }
    39 }
  • 用两个栈实现一个队列
    思路:两个栈一个作为入值栈,一个作为出值栈,当执行Push操作时,将值压入入值栈,当执行Pop或者Peek操作时,取出值栈的栈顶元素,当出值栈为空时,需要将入值栈的元素依次压入出值栈。
    数据结构之栈和队列数据结构之栈和队列两个栈表示一个队列
     1 public class NewQueue {
    2 private Stack inStack;
    3 private Stack outStack;
    4
    5 public NewQueue(int capacity)
    6 {
    7 inStack = new Stack(capacity);
    8 outStack = new Stack(capacity);
    9 }
    10
    11 public void enQueue(int value)
    12 {
    13 inStack.push(value);
    14 }
    15
    16 public int deQueue()
    17 {
    18 if(inStack.get_Count() == 0 && outStack.get_Count() == 0)
    19 {
    20 System.out.println("The queue is empty.");
    21 return Integer.MIN_VALUE;
    22 }
    23 if (outStack.get_Count() == 0)
    24 {
    25 while(inStack.get_Count() > 0)
    26 {
    27 outStack.push(inStack.pop());
    28 }
    29 }
    30 return outStack.pop();
    31 }
    32
    33 public int get_Count()
    34 {
    35 return inStack.get_Count() + outStack.get_Count();
    36 }
    37 }
  • 用两个队列实现一个栈
    思路:根据“后进先出”和“先进后出”的特点,在运行过程中,其中一个队列应该一直为空,对于push操作,将值放到非空队列中,当进行Push或者Pop操作时,先将非空队列全部导入空队列,然后此时的非空队列的头元素就是我们期望的元素。
    数据结构之栈和队列数据结构之栈和队列两个队列表示一个栈
     1 public class NewStack3 {
    2
    3 private Queue queue1;
    4 private Queue queue2;
    5
    6 public NewStack3(int capacity)
    7 {
    8 queue1 = new Queue(capacity);
    9 queue2 = new Queue(capacity);
    10 }
    11
    12 public void push(int value)
    13 {
    14 if (queue1.get_Count() == 0)
    15 {
    16 queue2.enQueue(value);
    17 }
    18 else
    19 {
    20 queue1.enQueue(value);
    21 }
    22 }
    23
    24 public int peek()
    25 {
    26 if (queue1.get_Count() == 0 && queue2.get_Count() == 0)
    27 {
    28 System.out.println("The stack is empty.");
    29 return Integer.MIN_VALUE;
    30 }
    31 int result = Integer.MIN_VALUE;
    32 if (queue1.get_Count() > 0 && queue2.get_Count() == 0)
    33 {
    34 while(queue1.get_Count() > 1)
    35 {
    36 queue2.enQueue(queue1.deQueue());
    37 }
    38 result = queue1.deQueue();
    39 queue2.enQueue(result);
    40 }
    41 else if (queue1.get_Count() == 0 && queue2.get_Count() > 0)
    42 {
    43 while(queue2.get_Count() > 1)
    44 {
    45 queue1.enQueue(queue2.deQueue());
    46 }
    47 result = queue2.deQueue();
    48 queue1.enQueue(result);
    49 }
    50
    51 return result;
    52 }
    53
    54 public int pop()
    55 {
    56 if (queue1.get_Count() == 0 && queue2.get_Count() == 0)
    57 {
    58 System.out.println("The stack is empty.");
    59 return Integer.MIN_VALUE;
    60 }
    61 int result = Integer.MIN_VALUE;
    62 if (queue1.get_Count() > 0 && queue2.get_Count() == 0)
    63 {
    64 while(queue1.get_Count() > 1)
    65 {
    66 queue2.enQueue(queue1.deQueue());
    67 }
    68 result = queue1.deQueue();
    69 }
    70 else if (queue1.get_Count() == 0 && queue2.get_Count() > 0)
    71 {
    72 while(queue2.get_Count() > 1)
    73 {
    74 queue1.enQueue(queue2.deQueue());
    75 }
    76 result = queue2.deQueue();
    77 }
    78
    79 return result;
    80 }
    81
    82 public int get_Count()
    83 {
    84 return queue1.get_Count() + queue2.get_Count();
    85 }
    86 }
  • 递归反转一个栈,要求不能重建一个新栈,空间复杂度为O(1)。
    思路:参考汉诺塔的递归思路。
    数据结构之栈和队列数据结构之栈和队列反转一个栈
     1 public static void Reverse(Stack stack)
    2 {
    3 if (stack.get_Count() == 0)
    4 {
    5 return;
    6 }
    7 int value1 = stack.pop();
    8 Reverse(stack);
    9 if (stack.get_Count() == 0)
    10 {
    11 stack.push(value1);
    12 return;
    13 }
    14 int value2 = stack.pop();
    15 Reverse(stack);
    16 stack.push(value1);
    17 Reverse(stack);
    18 stack.push(value2);
    19 }
  • 对栈进行排序
    思路:依旧采取递归的套路
    数据结构之栈和队列数据结构之栈和队列对栈内元素进行排序
     1 public static void sort(Stack stack)
    2 {
    3 if (stack.get_Count() == 0)
    4 {
    5 return;
    6 }
    7 int value1 = stack.pop();
    8 sort(stack);
    9 if (stack.get_Count() == 0)
    10 {
    11 stack.push(value1);
    12 return;
    13 }
    14 int value2 = stack.pop();
    15 if (value1 > value2)
    16 {
    17 stack.push(value1);
    18 sort(stack);
    19 stack.push(value2);
    20 }
    21 else
    22 {
    23 stack.push(value2);
    24 sort(stack);
    25 stack.push(value1);
    26 }
    27 }
  • 判断栈的push、pop序列是否一致
    思路:创建一个新栈,依次将push队列中的值压入,在压入过程中,判断pop队列,如果找到相同的值,执行stack.pop操作,以此类推,最终判断stack是否为空。
    数据结构之栈和队列数据结构之栈和队列View Code
     1 public static boolean isMatch(int[] arrPush, int[] arrPop)
    2 {
    3 if (arrPush == null || arrPop == null || arrPush.length != arrPop.length)
    4 {
    5 return false;
    6 }
    7 Stack stack = new Stack(arrPush.length);
    8 for(int i = 0, j = 0; i < arrPush.length; i++)
    9 {
    10 stack.push(arrPush[i]);
    11 while(j < arrPop.length)
    12 {
    13 if (stack.peek() == arrPop[j])
    14 {
    15 stack.pop();
    16 j++;
    17 }
    18 else
    19 {
    20 break;
    21 }
    22 }
    23 }
    24
    25 return stack.get_Count() == 0;
    26 }
  • 如何用一个数组实现两个栈
    思路:用0、2、4、6……表示第一个栈的元素,用1、3、5、7表示第二个栈的元素
    数据结构之栈和队列数据结构之栈和队列一个数组表示两个栈
      1 public class NewStack4 {
    2 private int[] arrValue;
    3 private int capacity;
    4 private int m_Count1;
    5 private int m_Count2;
    6
    7 public NewStack4(int capacity)
    8 {
    9 arrValue = new int[capacity];
    10 this.capacity = capacity;
    11 m_Count1 = 0;
    12 m_Count2 = 0;
    13 }
    14
    15 public void push(int value, int type)
    16 {
    17 boolean bExtend = false;
    18 if (type == 1)
    19 {
    20 if (m_Count1 * 2 + 1 >= capacity)
    21 {
    22 bExtend = true;
    23 }
    24 }
    25 if (type == 2)
    26 {
    27 if (m_Count2 * 2 + 2 >= capacity)
    28 {
    29 bExtend=true;
    30 }
    31 }
    32 if (bExtend)
    33 {
    34 int[] temp = new int[capacity*2];
    35 capacity = capacity*2;
    36 for(int i = 0; i < arrValue.length; i++)
    37 {
    38 temp[i] = arrValue[i];
    39 }
    40 arrValue = temp;
    41 }
    42
    43 if (type == 1)
    44 {
    45 m_Count1++;
    46 arrValue[m_Count1*2 - 1] = value;
    47 }
    48
    49 if (type == 2)
    50 {
    51 m_Count2++;
    52 arrValue[m_Count2*2] = value;
    53 }
    54 }
    55
    56 public int peek(int type)
    57 {
    58 if (type == 1)
    59 {
    60 if (m_Count1 == 0)
    61 {
    62 System.out.println("Stack 1 is empty.");
    63 return Integer.MIN_VALUE;
    64 }
    65 else
    66 {
    67 return arrValue[m_Count1*2 - 1];
    68 }
    69 }
    70 if (type == 2)
    71 {
    72 if (m_Count2 == 0)
    73 {
    74 System.out.println("Stack 1 is empty.");
    75 return Integer.MIN_VALUE;
    76 }
    77 else
    78 {
    79 return arrValue[m_Count2*2];
    80 }
    81 }
    82
    83 return Integer.MIN_VALUE;
    84 }
    85
    86 public int pop(int type)
    87 {
    88 if (type == 1)
    89 {
    90 if (m_Count1 == 0)
    91 {
    92 System.out.println("Stack 1 is empty.");
    93 return Integer.MIN_VALUE;
    94 }
    95 else
    96 {
    97 int result = arrValue[m_Count1*2 - 1];
    98 m_Count1--;
    99 return result;
    100 }
    101 }
    102 if (type == 2)
    103 {
    104 if (m_Count2 == 0)
    105 {
    106 System.out.println("Stack 1 is empty.");
    107 return Integer.MIN_VALUE;
    108 }
    109 else
    110 {
    111 int result =arrValue[m_Count2*2];
    112 m_Count2--;
    113 return result;
    114 }
    115 }
    116
    117 return Integer.MIN_VALUE;
    118 }
    119
    120 public int get_Count(int type)
    121 {
    122 if (type == 1)
    123 {
    124 return m_Count1;
    125 }
    126 else if (type == 2)
    127 {
    128 return m_Count2;
    129 }
    130 return Integer.MIN_VALUE;
    131 }
    132
    133 }

    这种方式在两个栈的容量接近时比较好,如果两个栈的容量相差很大, 那么会浪费很多存储空间。针对这个问题的解决方法是用0、1、2、3……表示第一个栈,用n、n-1、n-2、n-3……表示第二个栈,代码实现类似,这种情况下也可以对数组进行扩容。

      

  最后,欢迎大家提出更多相关的面试题目,我们可以一起讨论。