数据结构与算法之栈:反转一个栈的两种解法


问题描述:要求反转一个栈,
例如 :栈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 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值