栈与队列问题(主要是栈的使用)

人盡茶涼 提交于 2020-01-22 01:02:33

转载请注明原文地址: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;
    }   
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!