想必对于每一个初学链表的人来说,链表反转都是绕不过去的一道题。在面试算法中,更是长期霸榜TOP1。本期就让我们来好好分析分析,链表反转,是不是真的有那么难。
单链表反转的根本,我将其总结为4点:
1.结点指针域的变动 2.while循环中指针的移步操作 3.while循环的退出条件 4.返回值如何确定
掌握这四点,你将再也不会忘记单链表反转,从而战无不胜!
在接下来文章介绍的方法中,你一定会看到这四点的影子。
链表反转的题目大致如下:
给你一个单链表的头结点head,要你实现链表反转,并返回反转后链表的头结点。
下面我们来介绍链表反转的两种常用做法:
1.不借助虚拟头结点直接反转
首先将这句话牢牢记在心中:
没有虚拟头结点反转单链表,需要三个指针pre,cur和next。首先保存原链表的cur.next为next,再将cur的next域指向pre。最后,cur和pre都向后移动一步开启下一轮。
代码如下:
public static ListNode reverseList(ListNode head){ ListNode cur = head; ListNode pre = null; while (cur != null){ ListNode next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; }
接着我们来分析为什么这么做:
第一,循环中保存cur.next为next一定要在其他所有步骤之前 。因为原链表中cur的后继结点只有cur.next一个引用。
1 <-- 2 3 --> 4 --> 5
如上,如果你还没有保存4的引用就让3的next指向2,那么4就会没有引用,我们就无法获得这个节点了。而我们需要这个节点来改变cur的引用,以实现循环。
所以,保存cur.next (4)才能确保while循环中指针移步正常进行。
第二,让cur.next指向pre,实现指针域的反转。即,让3的next指向2。
最后一步,我们改变cur和pre的位置,继续反转下一个节点的指针域。
第三,考虑退出循环的条件。我们在代码中看到,我们实现反转,是通过操作cur的next指针域实现的。所以cur指向原链表最后一个节点时,也需要修改指针域,这时还不能退出循环。所以,cur再移步,cur==null的时候,我们才能退出循环。
第四,返回值。cur == null的时候退出循环,这时pre的状态是什么呢?因为pre是cur的前驱结点,所以pre这时指向原链表最后一个节点,即,反转链表的头结点。所以我们返回pre。
这个图请牢牢记在心中:
2.借助虚拟头结点,使用头插法完成链表反转。
我们首先建立虚拟头结点。然后,每次从原链表上拆一个节点下来接在虚拟头结点后面。
先上图:
首先我们需要明白,1节点的next域原来是不为null的,所以在反转链表的时候,一定要记住先把1节点的next域置空,这样它才能成为反转链表尾结点。
public static ListNode reverseList1(ListNode head){ if (head == null || head.next == null){ System.out.println("链表长度必须大于等于2才能反转!"); return null; } ListNode temp = new ListNode(-1); temp.next = head;//虚拟头结点及连接temp.next到head ListNode node = head.next;//node指向第二节点(把这里的node理解成图里的cur) head.next = null;//将原第一节点的next赋值为null,因为它是反转链表尾结点 while (node != null){ ListNode next = node.next;//同上,先保存下一个待修改指向的节点 node.next = temp.next; temp.next = node;//把node节点接入到temp后面 node = next;//node指向下一个待修改节点 } return temp.next; }
也是同第一种方法一样,直到node == null我们才退出循环。然后返回值的话,就直接返回temp.next。
你看,掌握节点指针域如何变动,移步操作的进行,while循环的退出条件,返回值的把握,单链表反转你还会忘记吗?希望本文对您有所帮助!