问题描述:要求反转一个栈,
例如
:栈A 中元素从栈底到栈顶依次为:1,2,3,4,5;要求对A反转后,A中元素从栈底到栈顶为 5,4,3,2,1
解法一:比较简单的想法是使用一个辅助栈,类似于汉诺塔问题
。
假设:要被反转的栈为A,大小为N,辅助栈为B;
基本思路:
进行第一轮操作,此次
对A中N个元素进行操作,
首先将A中栈顶元素弹出并保存到 临时变量temp中,然后将A中剩余元素全部pop到B中,此时A剩余0个元素,
然后将temp中的值push到A中,最后将B中所有元素全部pop到A中;
进行第二轮操作,此次对
A中N-1个元素(即从A栈顶开始的N-1个元素,保留上一轮中的栈底不变)进行操作,即本轮中的A由最初的A的栈顶开始的N-1个元素组成,也就是除去上一轮操作中的A的栈底外的元素。重复上一轮操作。
进行第三轮操作,此次对
A中N-2个元素(即从A栈顶开始的N-2个元素,保留上一轮中的栈底不变)进行操作,即本轮中的A由最初的A的栈顶开始的N-2个元素组成,也就是除去上一轮操作中的A的栈底外的元素。重复上一轮操作。
。。。
进行第N轮操作,此次对
A中N-(N-1)个元素(即从A栈顶开始的N-(N-1)个元素,保留上一轮中的栈底不变)进行操作,
此时本轮中的A只有一个元素,直接返回即可。也就是递归操作的出口。
解法二:不使用辅助栈,只使用常数个临时变量。
基本思路:假设栈A从栈底到栈顶的存储元素依次是1,2,3,4,5;我们反转以后,希望得到的结果是 5,4,3,2,1;
第一步:栈A 执行pop操作,得到栈顶元素5,并存入temp1中,此时的栈中元素顺序是1,2,3,4(4为栈顶元素);
然后进行递归调用,不考虑具体的过程,我们只需要知道递归调用的结果使栈A中元素顺序变为:4,3,2,1(1为栈顶),
再次执行pop操作,得到栈顶元素1,并存入temp2中,此时的栈中元素顺序为4,3,2(2为栈顶),
然后再次进行递归调用,本次递归调用结果使A中元素顺序变为2,3,4(4为栈顶),
然后将temp1 = 5压入栈中,此时A:2,3,4,5(5为栈顶),
再次进行递归调用,得到栈A:5,4,3,2(2为栈顶),
最后,将temp2压入栈中,得到栈A:5,4,3,2,1(1为栈顶)。
第二步:考虑递归结束条件,因为上述的递归调用中每次都pop出两个元素,所以当栈A大小大于2时,使用递归调用实现反转,
直到当A中只有两个元素时,直接使用两个临时变量,就可以实现反转,所以此为递归的出口。
当然还要考虑栈A 为空和栈A 只有一个元素时的特殊情况,此时直接返回即可。
代码:
package towerOfHanoi;
import java.util.Stack;
/**
* 问题:反转一个栈
* 解法一:使用一个辅助栈和一个临时变量
* 基本思路:假设给出的栈为A,辅助栈为B,临时变量为temp,A的深度为N.
* 首先,将A栈顶执行pop,并存储temp中,然后将A中剩余的pop到B 中,
* 再将temp中的值push到A中,最后将B中的元素都pop到A中。
* 执行完一轮操作后,原本A中栈顶元素到了栈底。
* 接下来进行下一轮操作,把A从栈顶到N-1看作一个新栈A,保证原A中栈底元素保持不动,重复上面操作。
* 接下来进行下一轮操作,把A从栈顶到N-2看作一个新栈A,即上一轮操作中的A从栈顶到它的深度-1作为本轮操作的A,
* 。。。
* 直到把A从栈顶到N-(N-1)看作一个新栈A,直接返回即可,也就是递归的出口。
*
*
*
*
*/
public class ReverseStack {
public static <E> void reverseStackByStack(Stack<E> A) {
Stack<E> B = new Stack<>();
reverseStackByStack(A,B,A.size());
}
/**
*
* @param <E> 泛型
* @param A 要被反转的栈
* @param B 辅助栈B
* @param N A栈要被反转的元素的个数,从栈顶开始
* @return 反转后的栈A
*/
public static <E> void reverseStackByStack(Stack<E> A,Stack<E> B,int N){
if(N ==1 ){
return ;
}
E temp = A.pop();
popAll(A,B,N-1);
A.push(temp);
popAll(B,A,N-1);
reverseStackByStack(A, B, N-1);
}
/**
* 将栈a中从栈顶开始的n个元素push到栈b中
* @param a
* @param b
* @param n
*/
private static <E> void popAll(Stack<E> a, Stack<E> b, int n) {
for(int i = 0;i<n;i++){
b.push(a.pop());
}
}
/**
* 解法二:不使用辅助栈,只是用常数个变量,使用递归
* 递归算法不能考虑中间具体过程,上一层的递归直接使用下一层的递归结果(假设下一层已经实现了想要的效果),
* 最后只要考虑最后一层递归的退出条件即可。
* @param <E>
* @param A 要被反转的栈A
*/
public static <E> void reverseStack2(Stack<E> A){
//最后一层递归的出口
/*
因为递归调用时,会弹出两个元素,所以进行下一层递归调用时,要求本层A大小要大于2,
对于栈A大小为2时直接使用两个临时变量就可以实现反转;
另外还需要考虑A的大小为1和A为空时两种特殊情况;
*/
//对于A为空和A只有一个元素时,直接返回
if(A.isEmpty() || A.size() == 1){
return;
}
//对于A大小为2时,不能再继续进行递归,此为递归出口,要实现具体的解决方法。
if(A.size() == 2){
E temp1 = A.pop();
E temp2 = A.pop();
A.push(temp1);
A.push(temp2);
return;
}else{
//其他层之间的递归调用
/*
* 假设本层递归中A中元素顺序为 1,2,3,4,5,从栈底到栈顶,
* 最终目的是使A中元素顺序变为 5,4,3,2,1
*/
//首先将栈顶元素弹出,并保存 temp1 = 5,A中剩余元素为1,2,3,4,(4为栈顶)
E temp1 = A.pop();
//然后将剩余元素反转
reverseStack2(A);//假设下一层递归调用实现了想要的效果,即A变成了4,3,2,1(1为栈顶)
//然后再次将栈顶元素弹出,并保存temp2 = 1,A中剩余元素为 4,3,2(2为栈顶)
E temp2 = A.pop();
//然后将剩余元素反转
reverseStack2(A);//假设下一层递归调用实现了想要的效果,即A变成了2,3,4(4为栈顶)
//将temp1= 5 push到栈A中,A变成了 2,3,4,5(5为栈顶)
A.push(temp1);
//然后将剩余元素反转
reverseStack2(A);//假设下一层递归调用实现了想要的效果,即A变成了5,4,3,2(2为栈顶)
//最后将temp2 push到栈A中即可
A.push(temp2);//此时A中元素顺序为 5,4,3,2,1(1为栈顶)
}
}
public static void main(String[] args) {
Stack<Integer> A = new Stack<>();
for(int i = 0;i<20;i++){
A.push(i);
}
System.out.println("反转前的栈A为:");
for(Integer i : A){
System.out.print(i +" ");
}
System.out.println();
//reverseStackByStack(A);
reverseStack2(A);
System.out.println("反转后的A为:");
for(Integer i : A){
System.out.print(i +" ");
}
}
}
运行结果:
反转前的栈A为:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
反转后的A为:
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0