zoukankan      html  css  js  c++  java
  • 栈与队列问题(主要是栈的使用)

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6857537.html 

    一:问题概述

        栈与队列的相关算法题,一般都是基于对栈、队列基本性质的熟练掌握的前提下,如何巧妙地组合、包装,以达到某种原来数据类型所没有的性质。比如:设计出一种能getMin()获取栈中最小值的栈、利用栈实现队列等等。问题的本意为考察栈、队列的基本性质与基本操作的灵活使用,所以解题思路也是从这边出发:怎么利用现有的数据结构以及其基本性质、操作,去组合、包装,实现题目要求?

    二:栈、队的基本性质与操作

        栈:后进先出。Java中Stack类实现了栈,基本操作有:empty()判断是否为空、peek()查看栈顶、pop()弹出栈顶、push()入栈。

        队列:先进先出。Java中用LinkedList实现了队列(LinkedList也实现了List接口和deque接口,所以LinkedList中包含了list、单向队列、双向队列的操作),单向队列的基本操作有:peek()查看队首、poll()弹出队首、offer()加入队尾、size()返回大小。

               

    三:设计一个可查询最值的栈

        我们知道,基本的栈是“后进先出”的,栈中的最值没有固定的位置,所以想要靠位置记录来查找是行不通的;其次,栈中元素随着push/pop操作,最值是会变化的,怎么随着push/pop操作动态修改最值呢?

        下面是例题:

        定义一种栈的数据结构,在该类型中实现一个能够得到栈最小元素的min函数。

    public class Solution {
        //两个栈,一个记录数据,一个记录栈中每层数据对应着的当前栈中最小值
        Stack data=new Stack();
        Stack min=new Stack();
        int curr_min=Integer.MAX_VALUE;
        
        //入栈
        public void push(int node) {
            data.push(node);
            //min栈记录当前入栈元素对应的当前栈中最小值是谁
            if(node<=curr_min){
                min.push(node);
                curr_min=node;//更新最小值
            }else{
                min.push(curr_min);
            }
        }
        //弹出元素,两个栈同步弹出
        public void pop() {
            data.pop();
            min.pop();
        }
        
        public int top() {
            return (Integer)data.peek();
        }
        
        public int min() {
            return (Integer)min.peek();
        }
    }

    四:用两个栈,实现队列

        栈是“后进先出”的,所以一般用于实现“倒序”问题。而队列是“先进先出”的,用栈实现队列,由“负负得正”即可知道,需要用两个栈。第一个栈接收数据,第二个栈负责把第一个栈的数据顺序调整回来,使先进第一个栈的,先从第二个栈弹出。题目唯一要注意的是:不是单纯地把第一个栈的数据转移到第二个栈,转移的时机很重要——第二个栈为空时,才把第一个栈的元素全部转移过来。

     public int[] twoStack(int[] ope, int n) {
            Stack in=new Stack();
            Stack out=new Stack();
            ArrayList<Integer> pops=new ArrayList<Integer>();
            
            for(int i:ope){
                if(i>0){
                    in.push(i);
                }else{
                    //双栈实现队列的唯一重点:有出队命令时,首先应该检查out栈有无元素,有的话直接out栈弹出即可;
                    //若out栈为空,才把in栈中元素全部弹出入out栈,调换了位置,然后弹出out栈栈顶
                    if(out.empty()){
                        while(!in.empty()){
                            int val=(Integer)in.pop();
                            out.push(val);
                        }
                    }
                    int valout=(Integer)out.pop();
                    pops.add(valout);
                    
                }
            }
            int[] res=new int[pops.size()];
            for(int i=0;i<pops.size();++i){
                res[i]=pops.get(i);
            }
            return res;
        }

    五:实现栈的逆序

        在不借助额外的数据结构的前提下,把当前栈中的存储的数据逆序存放。即:原先位于栈顶的,放到栈底......

        思路:

        这道题,如果可以借助额外的数据结构,则很容易实现,比如:使用第二个栈依次接收第一个栈中弹出的数据即可。但是题目要求不能借助额外的数据结构,这时我们就不得不找替代品——除了栈之外,还有什么拥有“后进先出”的特性呢?没错,那就是递归!其实递归函数的执行,归根结底还是使用了栈的,那就是函数栈。所以,这里我们就在递归函数中操作原有栈,利用递归函数的return顺序实现数据倒序。

       解题偶得:递归通常在参数中传递被各层操作的数据、也在参数中传递控制递归深度的变量。递归返回数据有两种方法:递归函数的返回值、通过操作参数中的数组来携带数据。

      递归思路整理:

    recur(){
    
       递归下层前代码:按递归顺序执行,同时,也是递归终点返回数据的地方;
      
       recur();//递归指令:从这里进入下层执行,等待下层执行完毕并返回
    
       递归后代码:获取下层执行完毕并返回的数据,继续完成该层的所有操作。按递归顺序的逆序执行
    
    }

    代码:

       //对每一层:先取出栈中底部元素,并且不改变上面的元素
       public int getBottom(Stack<Integer> data){
          int curr=(Integer)data.pop();
           if(data.empty()){//如果取出后,栈空了,说明这是栈底元素,返回上层
               return curr;
           }
           int bottom=getBottom(data);//获取下层返回的栈底元素
           data.push(curr);//把当前层元素重新入栈
           return bottom;//把获取到的栈底元素往上传递
       }
        //实现逆序
        public void recur(Stack<Integer> data,int n){
            if(n==0){
                return;//控制递归终点
            }
            int bottom=getBottom(data);//按递归顺序获取栈底
            recur(data,n-1);
            data.push(bottom);//按递归逆序把每一层获取到的栈底入栈,从而实现原先的栈底最后入栈,成为栈顶
        }

    六:栈排序问题

        给定一个栈,栈中有数据。要求最多只能额外借助一个栈,不能用其他数据结构复制数据。实现对栈中数据排序。

       思路:使用一个辅助栈来协助排序,其实就是一个不断弹出、比较、放回、入栈的过程。(类比汉诺塔)

        public ArrayList<Integer> twoStacksSort(int[] numbers) {
          //由于Stack的peek()没元素则抛出异常,而LinkedList中的peekFirst()无元素则返回Null。所以用LinkedList模拟栈
          LinkedList<Integer> A=new LinkedList<Integer>();
          LinkedList<Integer> B=new LinkedList<Integer>();
            //对需要处理的数据元素入栈
            for(int i=numbers.length-1;i>=0;--i){
                A.push(numbers[i]);
            }
            //双栈排序
            while(A.size()>0){
                Integer curr=A.pop();
                //linkedlist没有empty()方法,所以只能用size()与0比较判断是否为空
                if(B.size()==0){
                    B.push(curr);
                    continue;
                }else{
                    //如果A弹出的元素大于B栈顶,则需要把它放在当前B栈顶的下面某处
                    while(B.peekFirst()!=null && curr>B.peekFirst()){
                    //所以把B栈顶弹出,放回A,使得B栈中第二个元素成为栈顶,用于下一循环时比较
                    Integer back=B.pop();
                    //把B弹回的元素放入A栈,等下重新弹出入B栈
                    A.push(back);
                }
                //B栈中小于当前A弹出元素的已回到A栈,这时把curr入B栈,就是合适的位置。B栈从下往上是大——>小。
                B.push(curr);
              }
            }
            //上面循环结束后,B栈中元素已经是有序的:从下往上==从大到小
            while(B.size()>0){
                A.push((Integer)B.pop());
            }
            ArrayList<Integer> res=new ArrayList<Integer>();
            while(A.size()>0){
                res.add((Integer)A.pop());
            }
            return res;
        }   
  • 相关阅读:
    OC,iOS浮点型数据的计算不正确问题
    Swift中AVFoundation的简单使用
    一款经典的stick footer布局
    swift获取当前活动的viewcontroller
    xib的NSLayoutConstraint是否有效
    bootstrap的quartz定时任务简单使用
    Python实现:某个用户登录后,查看自己拥有所有权限
    ftp作业
    实现计算器
    面向对象作业——校园管理系统
  • 原文地址:https://www.cnblogs.com/ygj0930/p/6857537.html
Copyright © 2011-2022 走看看