最简单的递归俗称"自己调自己",在一个方法内部调用相同方法的行为。
递归套路解析
如下,这是一个基础的递归函数,当传参为3时候,打印结果为0 1 2。
public static void recursion(int i){
if(i == 0){
return;
}
i--;
recursion(i);
System.out.println(i);
}
首先从JVM的角度分析该方法,当它被执行的时候,Java虚拟机会同步创建一个栈帧,在虚拟机栈中,栈帧的入栈和出栈标志着方法的完成。而该方法的调用如下所示:
出栈即执行顺序为:recursion(0)等于0返回,recursion(1)此时打印0,recursion(2)此时打印1,recursion(3)此时打印2。
从数据结构的角度看,实则对应的就是栈结构,对应Java中的Stack类,递归便是让当前方法反复入栈,但栈结构的性质决定——先入后出,即第一次入栈的方法最后才会出栈,可以想象为一个"垃圾桶",最后丢的垃圾在上面,便最先拿出。
下列一道算法题帮助理解递归套路:
可以使用数据结构,反转一个栈中的所有数据:
public static Stack reverserUsingStack(Stack<Integer> stack){
Stack<Integer> temp = new Stack<>();
while (!stack.isEmpty()){
temp.push(stack.pop());
}
return temp;
}
不显示使用任何数据结构,反转一个栈中的所有数据:
public static void reverser(Stack<Integer> stack){
if(stack.isEmpty()){
return;
}
int f = popLast(stack);
reverser(stack);
stack.push(f);
}
/**
* @param stack
* @return 栈中的最后一个元素
*/
public static int popLast(Stack<Integer> stack){
Integer pop = stack.pop();
if(stack.isEmpty()){
return pop;
}else{
int last = popLast(stack);
stack.push(pop);
return last;
}
}
可以发现该方法使用递归其实是隐式的使用虚拟机栈帮助我们把数据重新入栈,即每次pop出栈中的最后一个元素,reverser方法为递归方法,使用递归时执行方法的出栈顺序等效于temp.push(stack.pop());
上述两道题得出,在Java中,任何递归行为都可以通过Stack改成非递归行为,无非是不让JVM帮你的方法压栈,改为自己压栈。同样得出的递归行为的一个特性:先入后出,实则就是由方法得出的栈帧在虚拟机栈中的入栈和出栈。