【算法设计与分析】链表翻转(时间复杂度O(n),空间复杂度O(1)的做法)

问题:
现有链表为: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;
123456
(第二步)
           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;
123456
(第三步)
               p1  p2  p3
dummy-->3-->2-->1-->4-->5-->null



一直进行同样的操作,直到p2指向空节点null,最后的状态为:

                        p1  p2
dummy-->5-->4-->3-->2-->1-->null


最后返回dummy.next就可以得到翻转后的链表了!!

JAVA实现
当链表中只有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; }
}

 

领扣地址:https://leetcode-cn.com/problems/reverse-linked-list/submissions/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值