栈和队列的相互实现

浅浅的花香味﹌ 2022-06-07 13:23 263阅读 0赞

前言

栈和队列作为两种典型的线性表,有着非常鲜明甚至可以说是相互对立的特点;栈先进后出(后进先出),队列先进先出(后进后出)。因此,对相同的输入,两者会产生恰好截然相反的输出。例如,对于给定的序列”ABCDE”,如果按照字母顺序将这个5个元素依次入栈,然后再依次出栈,那么得到的输出将是”EDCBA”,而如果将5个元素意思压入队列,然后依次弹出,那么得到的输出将是”ABCDE”。

正是因为这种截然相对的输出,使得他们彼此之间有了更多的联系;使得他们之间可以相互实现对方。就是说我们可以用栈模拟出队列的输出,同样也可以用队列模拟出栈的输出。下面就来看看。

栈实现队列

先说容易理解也是大家最容易想到实现方式的:用两个栈实现一个队列。

现在有栈Stack1和栈Stack2,假设现在输入序列”ABCDE”已经依次压入到栈Stack1,A处于栈底,E处于栈顶,那么怎样才可以得到输出序列也为“ABCDE”呢,我们很容易想到,把栈Stack1倒过来就可以了,那么怎样倒过来呢?这时候就要借助Stack2,我们把Stack1的内容依次弹出,然后再依次压入到Stack2不就相当于把Stack1 倒过来了吗?这时候Stack2 依次弹出,输出序列就是队列形式了。总结一下:

  • 入栈只进栈Stack1
  • 出栈时,如果Stack2 不为空,则直接从Stack2弹出;如果Stack2 为空,则把Stack1的内容依次弹出,并压入Stack2,然后从Stack2弹出栈顶元素。

原理很简单,实现起来也不难:

  1. /**
  2. * Created by engineer on 2017/10/22.
  3. * <p>
  4. * 用栈实现队列
  5. */
  6. public class Stack2Queue {
  7. private static class SQueue<E> {
  8. //只负责进栈元素
  9. private Stack<E> mTStackA;
  10. //负责中转
  11. private Stack<E> mTStackB;
  12. public SQueue() {
  13. mTStackA = new Stack<>();
  14. mTStackB = new Stack<>();
  15. }
  16. public int getSize() {
  17. return mTStackA.size() + mTStackB.size();
  18. }
  19. private boolean enqueue(E e) {
  20. return mTStackA.add(e);
  21. }
  22. private E dequeue() {
  23. //两个栈都为空时,则队列也为空
  24. if (mTStackA.isEmpty() && mTStackB.isEmpty()) {
  25. return null;
  26. }
  27. if (mTStackB.isEmpty()) {
  28. while (!mTStackA.isEmpty()) {
  29. mTStackB.push(mTStackA.pop());
  30. }
  31. }
  32. return mTStackB.pop();
  33. }
  34. }
  35. //测试类
  36. public static void main(String[] args) {
  37. SQueue<String> mSQueue = new SQueue<>();
  38. mSQueue.enqueue("A");
  39. mSQueue.enqueue("b");
  40. System.out.println("出对列:"+mSQueue.dequeue());
  41. mSQueue.enqueue("B");
  42. mSQueue.enqueue("C");
  43. System.out.println("出队列:"+mSQueue.dequeue());
  44. mSQueue.enqueue("D");
  45. int size = mSQueue.getSize();
  46. System.out.printf("%d 个元素出队:\n", size);
  47. for (int i = 1; i <= size; i++) {
  48. System.out.println(mSQueue.dequeue());
  49. }
  50. }
  51. }

得到输出:

  1. 出对列:A
  2. 出队列:b
  3. 3 个元素出队:
  4. B
  5. C
  6. D

用队列实现栈

有了上面的经验,我们可以再想想怎样用两个队列实现栈呢?其实,思路或者说是原理,都是一样,就是利用两个容器,实现数据的翻转,假设现有队列DequeA和DequeB;刚开始两个队列都为空,现有输入序列”ABCDE”,有元素A要入队,那么这个时候,可以随机一个队列使用,假设我们选队列DequeB,元素A进入队列DequeB,接着元素B,C,D进入队列DequeB,这个时候,如果要求有元素输出,如果直接从队列DequeB头部输出元素,那么就不符合栈后进先出的原则,此时,需要输出的元素是D,而他此时在队列DequeB的尾部,因此为了输出他,必须把他前面的ABC**拿走**,拿走的元素放在哪里呢?队列DequeA恰好是空的,放进去就好了。此时,队列DequeB中只有一个B,让他出队列就好了,最后队列DequeB空了。接着E要入队,此时他应该放在哪里呢?应该放入队列DequeA中。同样,此时需要输出了,再次按照刚才的思路,把队列DequeA 中除了E之外的所有元素放入队列DequeB中,这样以此类推,就实现了栈的输出。总结一下:

  • 当两个队列都为空时,有元素需要插入时,任选一个插入即可。
  • 当需要元素出栈时,从非空队列中,除了最后一个处于队尾的元素之外,其余元素都压入到另一个空队列中,并从队列中弹出最后一个元素
  • 每次入栈、出栈操作完成后,总有一个队列是完全空的

按照上面的思路:

  1. /**
  2. * Created by engineer on 2017/10/22.
  3. * <p>
  4. * 队列实现栈
  5. */
  6. public class Queue2Stack {
  7. private static class QStack<E> {
  8. private Deque<E> mEQueueA;
  9. private Deque<E> mEQueueB;
  10. private QStack() {
  11. mEQueueA = new LinkedList<E>();
  12. mEQueueB = new LinkedList<E>();
  13. }
  14. private int getSize() {
  15. return mEQueueA.size() + mEQueueB.size();
  16. }
  17. private void push(E e) {
  18. if (mEQueueA.isEmpty()) {
  19. mEQueueB.addLast(e);
  20. } else {
  21. mEQueueA.addLast(e);
  22. }
  23. }
  24. private E pop() throws Exception {
  25. if (mEQueueA.isEmpty() && mEQueueB.isEmpty()) {
  26. return null;
  27. }
  28. if (mEQueueA.isEmpty() && !mEQueueB.isEmpty()) {
  29. return swapDeque(mEQueueA, mEQueueB);
  30. } else if (mEQueueB.isEmpty() && !mEQueueA.isEmpty()) {
  31. return swapDeque(mEQueueB, mEQueueA);
  32. } else {
  33. //This should never happen
  34. throw new RuntimeException("At least One of Deque must be empty");
  35. }
  36. }
  37. private E swapDeque(Deque<E> A, Deque<E> B) {
  38. while (B.size() != 1) {
  39. //队列B从队头出队,压入队列A的尾部
  40. A.addLast(B.removeFirst());
  41. }
  42. //从队列B队头返回最后的一个元素
  43. return B.removeFirst();
  44. }
  45. }
  46. public static void main(String[] args) throws Exception {
  47. QStack<String> mQStack = new QStack<>();
  48. mQStack.push("A");
  49. mQStack.push("b");
  50. System.out.println("栈顶元素pop:"+mQStack.pop());
  51. mQStack.push("B");
  52. mQStack.push("C");
  53. mQStack.push("D");
  54. System.out.println("栈顶元素pop:"+mQStack.pop());
  55. mQStack.push("E");
  56. int size = mQStack.getSize();
  57. System.out.printf("%d 个元素出栈:\n", size);
  58. for (int i = 1; i <= size; i++) {
  59. System.out.printf("第 %d 个出栈元素:%s\n", i, mQStack.pop());
  60. }
  61. }
  62. }

得到输出:

  1. 栈顶元素pop:b
  2. 栈顶元素pop:D
  3. 4 个元素出栈:
  4. 1 个出栈元素:E
  5. 2 个出栈元素:C
  6. 3 个出栈元素:B
  7. 4 个出栈元素:A

好了,这就是栈和队列的相互实现。

发表评论

表情:
评论列表 (有 0 条评论,263人围观)

还没有评论,来说两句吧...

相关阅读

    相关 理解

    一.栈 1.1 概念 栈 :一种 特殊的线性表 ,其 只允许在固定的一端进行插入和删除元素操作 。 进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。 栈中的数据元

    相关 堆、区别

    1、堆和栈 1)堆(完全二叉树,可以看成一棵树的数组对象)是指程序运行时申请的动态内存,而栈只是指一种使用堆的方法(即先进后出); 2)堆是在程序运行时,而不是在程序编译时

    相关 相互实现

    前言 栈和队列作为两种典型的线性表,有着非常鲜明甚至可以说是相互对立的特点;栈先进后出(后进先出),队列先进先出(后进后出)。因此,对相同的输入,两者会产生恰好截然相反的

    相关 区别

    1.队列先进先出,栈先进后出。 对插入和删除操作的"限定"。 栈是限定只能在表的一端进行插入和删除操作的线性表。 队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性

    相关 实际应用

    1.将一个非负的十进制整数N转换成其他D进制数。 解决思路,连续取模%和整除/。D进制各数位的产生顺序是从低位到高位,而输出顺序却是从高位到低位,刚好相反,可以考虑使用栈进行