今天在做LeetCode的一道Reverse Singly LinkedList题时,从discuss中汲取了不少经验,写文一篇做为总结和思考。
1.三个pointer迭代的常规方法
如图1所示,给出一个链表,现在我们要做的是把它翻转过来使得最终变为5->4->3->2->1的形式。最自然的想法是我们将每一个正在访问的结点(记为current)的next指针指到它的前一个结点(记为parent)。但是这样会有一个小问题,当current的next被重新赋值之后,current原本的下一个结点会丢失,所以我们还需要一个指针then来记录原链表中current的下一个结点。假设我们刚开始进行reverse的操作,第一个操作的结点是2,所有的记号如图2所示。
现在将current指向parent,结果如图3。从图中可以看到,有了then的存在,我们就可以找到下一个要访问的结点,继续往前遍历链表。
为了叙述简单,这里不考虑边界条件,给出该方法的主要循环体:
current.next = parent;
parent = current;
current = then;
then = then.next;
遇到边界条件,如head为空,链表长度不足3个等情况,处理起来也很容易。这里有一个链表中常用的技巧,就是使用一个不关心它存的值是多少的dummy node,把它放置在头结点的前面,这样可以省去关于头结点的边界条件判断。
2.头插法
此方法和建立链表时使用的头插法是一个意思,就是在一个链表的头部不断插入新的结点。在reverse完成后,原链表的头结点将成为新链表的尾部,因此我们把它记为tail。假设我们添加了一个dummy node在结点1的前面,那么头插法的运行过程如下:
访问继tail之后的第一个结点current,并把current拿出来,插入到链表的最前面(dummy node的后面),这样tail和current的位置就发生了交换。过程如图4所示。
然后,将current的值更新为此时tail所指向的下一个结点,再次重复该操作,直到末尾即可。这里给出完整的实现代码。
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode tail = dummy.next;
ListNode cur = tail.next;
while(cur != null) {
tail.next = cur.next;
cur.next = dummy.next;
dummy.next = cur;
cur = tail.next;
}
return dummy.next;
}
相比于第一个方法,个人觉得这种方法比较容易操作,需要考虑的东西比较少,当然第一个方法是最容易想到的。