现有链表为:1–>2–>3–>4–>5–>null
如何在O(n)的时间复杂度,O(1)的空间复杂度内实现这个链表的翻转?
即将链表翻转为5–>4–>3–>2–>1–>null
链表翻转的general做法是,借助一个假链表头(用dummy表示),使用三个指针来完成。以翻转1-->2-->3-->4-->5-->null
这个链表为例,链表翻转的过程中,各个链表节点和指针的移动顺序为:
第一步:
p1 p2 p3
dummy-->1-->2-->3-->4-->5-->null
第二步:
p1 p2 p3
dummy-->2-->1-->3-->4-->5-->null
第三步:
p1 p2 p3
dummy-->3-->2-->1-->4-->5-->null
第四步:
p1 p2 p3
dummy-->4-->3-->2-->1-->5-->null
第五步:
p1 p2
dummy-->5-->4-->3-->2-->1-->null
p1:指针p1指向原链表的头节点,p1在整个链表翻转的过程都只指向原链表的头结点即节点1(节点1也是翻转后链表的尾节点)
p2:指针p2指向翻转过程中,即将变成临时头结点的节点
比如第二步中,p2指向节点3,第三步时节点3就是新的头结点,但是节点3作为头结点是临时的,因为我们会一直翻转,直到整个链表都翻转完,节点5才是最终的头结点。所以节点3只是临时的头结点
(这里只将dummy认做一个辅助节点,而不认做是头结点)
p3:指针p3指向p2的下一个节点。
首先p1 p2 p3分别指向链表的前三个节点:
p1 p2 p3
dummy-->1-->2-->3-->4-->5-->null
经过下面的操作可以将链表状态和指针的指向改变为第二步的样子:
p2.next=dummy.next;
p1.next=p3;
dummy.next=p2;
p2=p3;
p3=p2.next;
(第二步)
p1 p2 p3
dummy-->2-->1-->3-->4-->5-->null
重复一遍同样的操作可以将链表状态和指针的指向更新到第三步的状态:
p2.next=dummy.next;
p1.next=p3;
dummy.next=p2;
p2=p3;
p3=p2.next;
(第三步)
p1 p2 p3
dummy-->3-->2-->1-->4-->5-->null
一直进行同样的操作,直到p2指向空节点null,最后的状态为:
p1 p2
dummy-->5-->4-->3-->2-->1-->null
最后返回dummy.next就可以得到翻转后的链表了!!
当链表中只有1个或者2个节点时,可以直接翻转,不用上面的三个指针的方法
当链表中的节点大于等于三个,就可以使用上面的方法。
public ListNode reverseList(ListNode head) {
if(head==null) return null;
// 先检查一下链表的长度,如果链表只有两个节点就直接翻转,不用借助三个指针
int len=0;
ListNode p=head;
while(p!=null) {
len++;
p=p.next;
}
if(len==1) return head;
if(len==2) {
ListNode newHead=head.next;
newHead.next=head;
head.next=null;
return newHead;
}
// 链表长度超过3个节点时,需要借助辅助头节点dummy和三个指针
// dummy的值随便取一个就可以了
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode p1=head,p2=p1.next,p3=p2.next;
while(p2!=null) {
p2.next=dummy.next;
p1.next=p3;
dummy.next=p2;
p2=p3;
if(p2!=null) p3=p2.next;
}
return dummy.next;
}
其中我们的节点结构(ListNode)为:
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}