转载请注明原文地址: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; }
来源:https://www.cnblogs.com/ygj0930/p/6857537.html