题目:
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表:返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
题解:链表无法逆序访问,故不能逆序遍历链表得到从尾到头的结果。
解法一:直接遍历链表,用ArrayList存储,然后反转ArrayList。
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<Integer>();
while (listNode != null) {
list.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(list);
return list;
}
时间复杂度:O(n),遍历链表是一个O(n),反转list也是O(n)
空间复杂度:O(n)
解法二:利用栈先进后出的特性,遍历链表用Stack存储,遍历Stack用ArrayList存储
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<Integer>();
Stack<Integer> stack = new Stack<Integer>();
while (listNode != null) {
stack.add(listNode.val);
listNode = listNode.next;
}
while (!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
时间复杂度:O(n),遍历链表是一个O(n),弹空一个栈需要O(n)
空间复杂度:O(n),栈空间最大长度是链表的长度n
解法三:采用递归
递归就是在方法体中自己调用自己,所以递归必须要有出口,否则就会造成栈溢出。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的简单问题来求解。因此递归过程,最重要的就是判断能不能将大型的问题分解为更小的子问题,这是是否使用递归的关键。递归的思想也可以用栈实现,因为栈是先进后出的,符合逆序的特性,递归本质上就是用栈实现的。
递归是到达底层后才会往上层回溯,直接递归到链表末尾,然后依次添加链表的值。此处递归的出口就是listNode == null,即到达链表的末尾。
递归模板:
if (递归出口) return;
逻辑处理(可有可无,具体问题具体分析)
递归调用
逻辑处理(可有可无,具体问题具体分析)
ArrayList<Integer> list = new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if (listNode != null) {
printListFromTailToHead(listNode.next);
list.add(listNode.val);
}
return list;
}
时间复杂度:O(n),遍历链表是一个O(n)
空间复杂度:O(n),栈空间最大长度是链表的长度n
总结:
涉及数据结构:链表、栈
涉及算法:递归