热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

死磕算法第二弹——栈、队列、链表(3)

本文整理来源《轻松学算法——互联网算法面试宝典》赵烨编著用栈实现队列由于栈和队列的特殊顺序存储结构,一些面试官会出一些题目,比如用栈实现队列和用队列实现栈。这样的题目在实际

本文整理来源 《轻松学算法——互联网算法面试宝典》/赵烨 编著

用栈实现队列

由于栈和队列的特殊顺序存储结构,一些面试官会出一些题目,比如用栈实现队列和用队列实现栈。

这样的题目在实际工作中并不具有实际应用意义,完全是为了考察大家的思考能力。

用两个栈实现队列

一般会用两个栈来实现队列。首先,我们将这两个栈分别定义为stack1和stack2

方案1
我们让入队操作在stack1中执行,而出队操作在stack2中执行。

  • 入队:直接向stack1入栈
  • 出队:将stack1中的所有元素出栈,依次入栈到stack2中,然后弹出stack2中的stack2中的栈顶元素,接着把stack2中的所有元素出现,依次压入stack1中。

来回入栈、出队比较繁琐,尤其是出队比较麻烦,需要先将元素从stack1倒入stack2里,然后stack2弹出元素之后又倒回stack1里。

方案2
入队都在stack1中进行,stack2用于出队,同时保证所有元素都在一个栈里,且遵循以下规则:

  • 入队:不管stack1是否为空栈,都将stack2中的元素压入stack1中。
  • 出队:若stack2不为空栈,则直接从stack2中弹出元素;若stack2为空栈,则把stack1中的元素倒入stack2中,再从stack2中弹出元素;若两个栈都是空的,则说明队列为空队,不能出队。

这种思路与方案1一样,只不过把倒回去的这个操作放在入队时执行,却使连续入队、出队的效率提高了。

方案3
入队都在stack1中操作,出队在stack2中操作,同时遵循以下规则:

  • 入队:直接把元素压入stack1中。
  • 出队:如果stack2不为空,则直接弹出stack2中的元素;如果stack2为空,则将stack1中的是所有元素带入stack2;如果stack2为空,则将stack1中的所有元素倒入stack2中,然后弹出stack2中的栈顶元素。同样若两个栈都是空栈,则队列为空队,无法出栈。

这个方案在入队时非常简单,而在出队时,多数情况下可以直接通过出队stack2实现,若需要把stack1中的元素倒入stack2中,则一般不用每次都进行操作。最坏的情况就是出队一个元素、入队一个元素这样循环操作,导致每次出队都要转移元素。

三个方案的操作时一样的。总体来说方案3是非常好的方案。

public class Stack2Queue {
    private Stack stack1;
    private Stack stack2;
    private int maxLength;

    public Stack2Queue(int capacity) {
        maxLength = capacity;
        stack1 = new Stack<>(capacity);
        stack2 = new Stack<>(capacity);
    }

    public boolean put(T item){
        if (stack1.isFull() || maxLength == size()){
            //满了
            return false;
        }
        stack1.push(item);
        return true;
    }

    public T poll(){
        if (!stack2.isEmpty()){
            return stack2.pop();
        }else {
            while (!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }
    }

    private int size() {
        return stack1.size() + stack2.size();
    }

}

测试代码:

public class Stack2QueueTest {

    @Test
    public void main(){
        Stack2Queue queue = new Stack2Queue<>(5);
        queue.put(1);
        queue.put(2);
        Assert.assertEquals(1,(int)queue.poll());
        queue.put(3);
        queue.put(4);
        Assert.assertEquals(2,(int)queue.poll());
        Assert.assertEquals(3,(int)queue.poll());
    }

}

用两个队列实现栈

栈的主要操作就是入栈和出栈,其特点就是后进先出。
我们先将两个队列分别定义为queue1与queue2。
方案1
入栈和出栈,都是queue1中完成,而queue2作为中转空间

  • 入栈:直接入queue1即可。
  • 出栈: 把queue1中除最后一个元素外的所有元素都移动到queue2中,再将queue1中的元素出队,此时即出栈;紧接着queue2中的所有元素移动到queue1中

这种操作过程与用栈实现队列的方案1一样,都是把第2个结构当做一个中转站,然后将数据来回倒。
方案2
从方案1中可以看到,在出栈时要把queue2中的元素移动到queue1中。在两个队列之间能否不用每次先出栈再把元素移动回去?

  • 入栈:两个队列哪个不为空,就把元素入队到哪个队列中;如果都为空,则任选一个队列入队,假设这个队列就是queue1.
  • 出栈:把不为空的队列中除最后一个元素外的所有元素移动到另一个队列中,然后出队最后一个元素。
public class Queue2Stack {
    private ArrayQueue queue1;
    private ArrayQueue queue2;
    private int maxLength;

    public Queue2Stack(int capacity){
        maxLength = capacity;
        queue1 = new ArrayQueue<>(capacity);
        queue2 = new ArrayQueue<>(capacity);
    }


    /**
     * 入栈
     * @param item 入栈元素
     * @return 入栈结果
     */
    public boolean push(T item){
        if(size() == maxLength){
            return false;
        }
        //如果中转空间为空,可以直接放入。当需要出栈时,放入元素即为队头
        if (queue2.isEmpty()){
            queue1.put(item);
        }else {
            queue2.put(item);
        }
        return true;
    }

    /**
     * 出栈
     * @return 出栈元素
     */
    public T pop(){
        if (size() == 0){
            throw new IndexOutOfBoundsException("栈里空了");
        }else {
            if (queue2.isEmpty()){
                while (queue1.size() > 1){
                    queue2.put(queue1.poll());
                }
                return queue1.poll();
            }else {
                while (queue2.size() > 1){
                    queue1.put(queue2.poll());
                }
                return queue2.poll();
            }
        }
    }

    private int size() {
        return queue1.size() + queue2.size();
    }
}

测试代码:

public class Queue2StackTest {

    @Test
    public void main() {
        Queue2Stack stack = new Queue2Stack<>(5);
        stack.push(1);
        stack.push(2);
        Assert.assertEquals(2, (int) stack.pop());
        stack.push(3);
        stack.push(4);
        Assert.assertEquals(4, (int) stack.pop());
        Assert.assertEquals(3, (int) stack.pop());
    }

}

推荐阅读
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • C++ STL复习(13)容器适配器
    STL提供了3种容器适配器,分别为stack栈适配器、queue队列适配器以及priority_queue优先权队列适配器。不同场景下,由于不同的序列式 ... [详细]
  • java线程池的实现原理源码分析
    这篇文章主要介绍“java线程池的实现原理源码分析”,在日常操作中,相信很多人在java线程池的实现原理源码分析问题上存在疑惑,小编查阅了各式资 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 广度优先遍历(BFS)算法的概述、代码实现和应用
    本文介绍了广度优先遍历(BFS)算法的概述、邻接矩阵和邻接表的代码实现,并讨论了BFS在求解最短路径或最短步数问题上的应用。以LeetCode中的934.最短的桥为例,详细阐述了BFS的具体思路和代码实现。最后,推荐了一些相关的BFS算法题目供大家练习。 ... [详细]
  • x86 linux的进程调度,x86体系结构下Linux2.6.26的进程调度和切换
    进程调度相关数据结构task_structtask_struct是进程在内核中对应的数据结构,它标识了进程的状态等各项信息。其中有一项thread_struct结构的 ... [详细]
  • 给出一群女孩的重量和颜值和她们的朋友关系现在有一个舞台ab是朋友bc是朋友ac就是朋友给出最大承重可以邀请这些女孩来玩对于每一个朋友团体全邀请or邀请一个or不邀请问能邀请的女孩的 ... [详细]
author-avatar
食品质量的安全
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有