LeetCode206 给你单链表的头节点 head,请你反转链表,并返回反转后的链表。
本题有两种方法,带头结点和不带头结点,我们都应该会,因为这两种方式都很重要,如果搞清楚,很多链表的算法题就不用做了。
1 建立虚拟头结点辅助反转
前面分析链表插入元素的时候,会发现如何处理头结点是个比较麻烦的问题,为此可以先建立一个虚拟的结点ans,并且令ans.next=head,这样可以很好的简化我们的操作。如下图所示,如果我们将链表{1->2->3->4->5}进行反转,我们首先建立虚拟结点ans,并令ans.next=node(1),接下来我们每次从旧的链表拆下来一个结点接到ans后面,然后将其他线调整好就可以了。
如上图所示,我们完成最后一步之后,只要返回ans.next就得到反转的链表了,代码如下:
// 方法1:虚拟结点法
public static ListNode reverseList(ListNode head) {
ListNode ans = new ListNode(-1);
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = ans.next;
ans.next = cur;
cur = next;
}
return ans.next;
}
建立虚拟结点是处理链表的经典方法之一,虚拟结点在很多工具的源码里都有使用,用来处理链表反转也比较好理解,因此我们必须掌握好。
2 直接操作链表实现反转
上面的方式虽然好理解应用也广,但是可能会被面试官禁止,为啥?原因是不借助虚拟结点的方式更难,更能考察面试者的能力。
我们观察一下反转前后的结构和指针位置:
我们再看一下执行期间的过程示意图,在图中,cur本来指向旧链表的首结点,pre表示已经调整好的新链表的表头,next是下一个要调整的。注意图中箭头方向,cur和pre是两个表的表头,移动过程中cur经过一次中间状态之后,又重新变成了两个链表的表头。
public ListNode reverseList(ListNode head ) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
这段代码很重要,建议理解的过程中且背下来