【算法手记03】反转链表

02-27 阅读 0评论

反转链表很重要,是链表题中的高频考点,部分题目可能还需要将反转链表抽出来作为一个函数使用。这几种方式都必须熟练手搓。

【算法手记03】反转链表,【算法手记03】反转链表,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,方法,第1张
(图片来源网络,侵删)

反转整个链表

lc206.给定单链表头结点,给出反转后的链表。

使用dummy虚拟头结点实现

反转后的新链表带一个dummy结点,将原链表的结点依次插入dummy结点之后,即可完成反转。过程大致如下:

【算法手记03】反转链表

【算法手记03】反转链表

下图中,cur作为遍历链表的指针,next指向cur的下一个节点。

【算法手记03】反转链表

【算法手记03】反转链表,【算法手记03】反转链表,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,使用,我们,方法,第5张
(图片来源网络,侵删)

以第一个结点为例,cur结点尾插dummy节点同样遵守"先接管后继,再接入前驱"的准则,先令cur.next指向dummy.next,也就是null;随后令dummy.next指向cur,插入完成。

从上图可以看到,在进行插入之前,我们令cur的后一个结点为next节点,这就便于我们在将前一个节点插入完成后,令cur指针指向next指针。

【算法手记03】反转链表

最后,考虑边界。

由于引入了dummy节点,因此头节点不需要特殊处理。现在需要考虑循环跳出的条件,即什么时候反转完了。

当cur指针指向最后一个节点,next指针指向null。cur节点插入新链表后,新的cur指针指向原来next指针,也就是null,此时退出循环。也就是循环进入条件应该是cur!=null.

【算法手记03】反转链表

public ListNode reverseList(ListNode head) {  
    ListNode cur = head;  
    ListNode dummy = new ListNode();  
    while (cur!=null){  
        ListNode next = cur.next;  
        //先接管后继  
        cur.next = dummy.next;  
        //再接入前驱  
        dummy.next = cur;  
        cur = next;  
    }  
    return dummy.next;  
}

直接操作原链表实现

此方法不引入新链表,空间复杂度更低,但也更复杂。

先画出初始和结束的状态:

【算法手记03】反转链表

下图中,cur作为遍历链表指针,同上。

随后,cur的next指向它的前一个结点prev。初始时,prev为null。随后,prev、cur依次向后移1位。

【算法手记03】反转链表

当cur指向null时,prev指向链表末尾,此时反转完毕,循环跳出。

【算法手记03】反转链表

代码如下:

ListNode prev = null;  
ListNode cur = head;  
while (cur!=null){  
    ListNode next = cur.next;  
    cur.next = prev;  
    prev = cur;  
    cur = next;  
}  
return prev;

指定区间反转

lc92

【算法手记03】反转链表

【算法手记03】反转链表

本题与lc206反转整个链表的实现方法是类似的。

在lc206中,我们将遍历到的原链表的结点依次插到dummy结点的后面,那么本题我们只需要将遍历到的区间节点依次插入到反转区间的第一个位置,也就是反转区间的前驱结点之后即可。

本题指针较多,写题的时候一定要画图,不然很容易把自己搞乱。

过程如下:

【算法手记03】反转链表

下面来考虑具体细节。我们选取反转区间中间的一部分来讨论实现细节。

如下图。由上面可以知道,cur指针每往前走一步,都要把自身插到pre结点之后。

类似这种更换链表中指定结点的位置的需求,我们一定要确定好结点的前驱和后继,以便它变换位置后,其他顺序不变。

【算法手记03】反转链表

明白这点之后,开始操作,将cur插入pre之后。

  1. 插入结点操作遵循 “先接管后继,再接入前驱” 原则,cur先将其next指针指向pre的next,随后pre的next指针指向cur。
  2. front的next指针指向next,cur指针移向next,大功告成。

    【算法手记03】反转链表

    【算法手记03】反转链表

随后考虑初始化条件。front最开始应该和cur同时指向pre.next。

这是因为,按照上面的方法,第一个节点cur.next指向pre.next时,出现自环。

front指向next时,链表恢复正常。由此可见front最开始应该和cur指向同一个位置。第二次移动之后,cur都会移向next,而front不会变。

实际上,front指针永远指向原区间的第一个结点。

【算法手记03】反转链表

【算法手记03】反转链表

再考虑边界条件。

  1. 由题中所给的变量范围可知,链表至少会有一个结点、left一定不会大于right,因此非法输入不需要考虑。
  2. 考虑指针越界。当right=n也就是区间到链表末尾时,当cur移动到right处,其next为null,不会触发空指针异常。

    综上,本题不需要额外考虑边界条件。

想好后,代码就很好写了。

public ListNode reverseBetween(ListNode head, int left, int right) {  
    ListNode dummy = new ListNode();  
    dummy.next = head;  
    int cnt = 1;  
    ListNode pre = dummy,cur,front;  
    while (cnt  
        cnt++;  
        pre = pre.next;  
    }  
    cur = pre.next;  
    front = cur;  
  
    while (cnt  
        ListNode next = cur.next;  
        cur.next = pre.next;  
        pre.next = cur;  
        front.next = next;  
        cur = next;  
        cnt++;  
    }  
    return dummy.next;  
  
}
  
    ListNode dummy = new ListNode();  
    dummy.next = head;  
    ListNode cur = head;  
    int len = 0;  
    while (cur!=null){  
        len++;  
        cur = cur.next;  
    }  
    int groupCnt = len/k;  
    ListNode pre = dummy;  
    cur = pre.next;  
    ListNode front = pre.next;  
    for(int i = 0;i 

免责声明
本网站所收集的部分公开资料来源于AI生成和互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
评论列表 (暂无评论,人围观)

还没有评论,来说两句吧...

目录[+]