1.题目
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
限制:
0 <= 链表长度 <= 10000
2.思路与实现
1.动态数组
依次读取链表中每个值并将其存入动态数组中,创建一个与动态数组大小相同的数组,遍历动态数组,将值反序存入数组
时间N
空间N
引入额外数据结构
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
ArrayList<Integer> res = new ArrayList<>();
while(head != null)
{
res.add(head.val);
System.out.println(head.val);
head = head.next;
}
int[] arr = new int[res.size()];
int j = arr.length - 1;
for(Integer i : res)
{
arr[j--] = i;
}
return arr;
}
}
可能可以优化的地方:
- 动态数组的存在一方面记录元素的数量,一方面记录元素的值,首先想到如果直接知道链表的长度,一次遍历链表即可
- 这个链表是简化的,没有size()方法,因此需要想办法知道链表的长度
- 先遍历一次得到长度,再遍历一次得到值
- 但问题存在于,遍历一次后,head就代表最后一个节点,所以需要在第一次遍历前就将head存储起来
2.两次遍历
时间N
空间N
不引入额外数据结构
速度最快
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
ListNode temp = head;
int len = 0;
while(temp != null)
{
len++;
temp = temp.next;
}
int[] arr = new int[len];
int i = len - 1;
while(head != null)
{
arr[i--] = head.val;
head = head.next;
}
return arr;
}
}
3.总结思路与实现
其他方法
3.递归法:在走至链表末端的过程中统计链表,到达末端时利用已知的长度创建数组,回溯时依次将元素存入数组
时间n
空间n
不引入额外数据结构
class Solution {
int[] res;
int i;
int j;
public int[] reversePrint(ListNode head) {
solve(head);
return res;
}
public void solve(ListNode head)
{
if(head == null)
{
res = new int[i];
return;
}
i++;
solve(head.next);
res[j] = head.val;
j++;
}
}
- 注意,递归法也可以使用动态数组实现,但引入额外数据结构
4.栈
利用栈的后进先出原则,将节点值压入栈,利用栈的大小创建数组,再进行出栈操作并存储数值
N
N
class Solution {
public int[] reversePrint(ListNode head) {
Stack<Integer> stack = new Stack<>();
while(head != null)
{
stack.push(head.val);
head = head.next;
}
int size = stack.size();
int[] res = new int[size];
for(int i = 0; i < size; i++)//不可使用size(),因为栈在压出后大小会减一
{
res[i] = stack.pop();
}
return res;
}
}
4.相关知识
- 解决链表问题优先思考1递归和2栈,3将递归优化为循环