【题目】
一个栈依次压入1、2、3、4、5,那么从栈顶到栈底分别为5、4、3、2、1。将这个栈转置后,从栈顶到栈底为1、2、3、4、5,也就是实现栈中元素的逆序,但只能用递归函数来实现,不能用其他数据结构。
【难度】
☆☆
【解答】
(其实不考虑其它,完全可以另外开一个栈做个中转,但是这道题就是要让你节省空间,只使用一个栈。)
设计两个递归函数。
递归函数一:移除并取得栈底元素。
递归函数二:利用递归函数一依次取得栈底元素然后将其逆序push入栈。
//递归移除并返回该栈的栈底元素
public static int getAndRemoveLastElement(Stack<Integer> stack) {
int result=stack.pop();
if(stack.isEmpty()) {
return result;
}else {
int last=getAndRemoveLastElement(stack);
stack.push(result);
return last;
}
}
//使用getAndRemoveLastElement函数依次取出栈底元素储存在i中然后逆序入栈
public static void reverse(Stack<Integer> stack) {
if(stack.isEmpty()) {
return ;
}
int i=getAndRemoveLastElement(stack);
reverse(stack);
stack.push(i);
}
【理解】
对于递归函数一,由于栈只能对栈顶操作,所以想要访问栈底元素,就得先把栈底以上的元素先pop掉,但是pop又不能丢掉,所以对于递归的每一层都用一个变量result来存储该层元素值,然后在取得最里面一层栈底元素后,就将每一层的result一层层地还原,即使用stack.push(result)。递归到栈底,即pop栈底元素后isEmpty()判断为真即进入递归终止条件,返回栈底元素赋值给倒数第二层函数的last,然后将该层的result入栈还原,最后返回last,这样一直返回到最外层返回的始终是last的值即栈底元素的值。
对于递归函数二,它是一个void型函数,首先对于递归函数都是要写它的终止条件,这个逆序函数的终止条件就是将栈中元素取空,然后开始返回调用,一层层地“合”上,直到递归的最外层。其实这个递归函数的机理和递归函数一是一样的,用一个变量值i来储存getAndRemoveLastElement()返回的每一层的栈底元素,最先取出的栈底元素在最上面,这样就实现了栈元素的逆序,然后将其一一push到栈中,逆序完成。
对于递归函数的理解:
我们可以把递归看做一个洋葱,递归函数全程分为三步:一层层剥洋葱,剥到芯,一层层合上洋葱。
【剥洋葱】进入递归函数你先要一层层地剥,这就是在一步步地执行调用自身的代码,进入每一层的函数都会执行除终止条件以外的调用自身函数之前的代码。如在递归函数二里进入每一层函数都先执行这一句代码:
int i=getAndRemoveLastElement(stack);
【剥到芯】递归函数一定要有终止函数,否则就会陷入死循环。一层层地进入函数后,执行到最后一层即满足递归终止条件后,就相当于剥完洋葱看到芯了,进入开始执行终止条件里面的代码,在递归函数二里就会执行:
if(stack.isEmpty()) {
return ;
}
【合洋葱】剥完洋葱后,就开始一层层把洋葱合上,即开始一层层地返回,返回后开始执行调用自身函数以后的代码,在递归函数二里面在依次取得并返回栈底元素给 i 后,给空栈一层层还原:
stack.push(i);