p32 面试题2:输入一个链表的头结点,从尾到头反过来打印出每一个结点的值。面试
从头遍历节点,将全部遍历到的值都压入栈,遍历完以后再从栈弹出值并打印,就实现了从尾到头打印的要求。bash
public static void reversePrintNodes(Node head) {
Stack<Integer> stack = new Stack<>();
while (head != null) {
stack.push(head.data);
head = head.next;
}
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
复制代码
递归的本质是栈。数据结构
这句话应该如何理解?函数
须要从起源去理解:计算机发展过程当中,为了表示函数调用自身的场景,不得不建立一种叫 Stack 的数据结构,来表达这种函数调用形式,并将其称为 recursion,即递归。post
详见下文:《虚拟机栈和栈帧的起源和关系》ui
理解递归的前提是先理解 Stack:入栈就是先从头一个个元素积压到最后一个,取元素(即出栈)则只能从最后一个挨个取到最底层 -- 先进后出,先来的压到最底,最后才弹出。spa
扩展到递归:3d
入栈阶段:
Basic Notation:栈帧 Stack Frame【切成一片片的小栈叫栈帧】
函数第一次被调用,压入方法栈底的栈帧(未执行到返回语句,栈帧还得活着)
函数第二次被调用,压到方法栈第二层栈帧(未执行到返回语句,栈帧还得活着)
函数第三次被调用,压到方法栈第三层栈帧(未执行到返回语句,栈帧还得活着)
······
函数最后一次被调用,压到方法栈最顶层(执行到返回语句,栈帧能够撤走了)
出栈阶段:
最顶层的方法执行到返回语句,将 Base Case (触底了) 的执行结果返回给上一个栈帧,而后撤走当前栈帧(即弹出栈)。
此时返回值做为参数传入到倒数第二个栈帧,被其中的函数做为计算的参数,执行到返回语句时,
同上,将结果返回给上一个栈帧,而后撤走当前栈帧。
······
出栈到最底下的那个栈帧,前面全部的计算结果一个传递给上一个,累积计算,直到传到祖宗十八代这里,宣告大结局。
这一代拿到前面十八代祖宗流传下来的值,计算完成,经过执行返回语句,将最终结果交给大boss,
出栈,这个家族的使命就完美结束了。
以上,即递归的整个流程分析。
复制代码
递归写法以下:code
public static void reversePrintNodesRecursively(Node head) {
if (head == null) {
return;
}
Node tmpNode = head.next;
if (tmpNode != null) {
reversePrintNodesRecursively(tmpNode);
}
System.out.println(head.data);
}
复制代码