链表--从尾到头打印链表(剑指 LeetCode)
基础知识
链表应该是面试时被提及最频繁的数据结构。链表的结构很简单,它由指针把若干个节点连接成链状结构。链表的创建、插入节点、删除节点等操作都只需要20行左右的代码就能实现,其代码量比较适合面试。
链表是一种动态数据结构,是因为在创建链表时,无须知道链表的长度。当插入一个节点时,我们只需要为新节点分配内存,然后调整指针的指向来确保新节点被链接到链表当中。内存分配小是在创建链表时一次性完成的,而是每添加- • 个节点分配一次内存。由于没有闲置的内存,链表的空间效率比数组高。
由于链表中的内存不是一次性分配的,因而我们无法保证链表的内存和数组一样是连续的。因此,如果想在链表中找到它的第i个节点,那么我们只能从头节点开始,沿着指向下一个节点的指针遍历链表,它的时间效率为0( N )。而在数组中,我们可以根据下标在0(1)时间内找到第i个元素。下面是在链表中找到第一个含有某值的节点并删除该节点的代码;
问题
题目:输入个链表的头结点,从尾到头反过来打印出每个结点的值。
通常,这种情况下,我们不希望修改原链表的结构。返回一个反序的链表,这就是经典的“后进先出”,我们可以使用栈实现这种顺序。每经过一个结点的时候,把该结点放到一个栈中。当遍历完整个链表后,再从栈顶开始逐个输出结点的值,给一个新的链表结构,这样链表就实现了反转。
看到这道题后,很多人的第一反应是从头到尾输出将会比较简单,于是我们很自然地想到把链表中链接节点的指针反转过来,改变链表的方向,然后就可以从头到尾输出了。但该方***改变原来链表的结构。是否允许在打印链表的时候修改链表的结构?这取决于面试官的要求,因此在面试的时候我们要询问清楚面试官的要求
但有一个问题:当链表非常长的时候,就会导致函数调用的层级很深,从而有可能导致函数调用桟溢出。显然用桟基于循环实现的代码的鲁棒性要好一些。更多关于循环和递归的
讨论,
代码
用栈
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> returnList = new ArrayList<>();
if(listNode == null){
return returnList;
}
Stack<ListNode> stacks = new Stack<>();
while(listNode != null){
stacks.push(listNode);
listNode = listNode.next;
}
while(!stacks.isEmpty()){
returnList.add(stacks.pop().val);
}
return returnList;
}
}
用队列
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> addressArrayList = new ArrayList<Integer>();
if(listNode == null){
ArrayList first = new ArrayList();
return first;
}
while (listNode != null) {
addressArrayList.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(addressArrayList);
return addressArrayList;
}
递归
import java.util.ArrayList;
public class Solution {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if (listNode == null)
return arrayList;
printListFromTailToHead(listNode.next);
arrayList.add(listNode.val);
return arrayList;
}
}
LeetCode
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
方法一:迭代
在遍历列表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针来存储下一个节点。不要忘记在最后返回新的头引用!
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}