代码随想录算法训练营 DAY4| 24.两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07.链表相交 142.环形链表II

24.两两交换链表中的节点

题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)

视频链接:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

讲解链接:代码随想录 (programmercarl.com)

整体思路

在这里插入图片描述

还是用dummyhead。dummyhead.next=head

上来先cur=dummyhead。用两个tmp保存1和3号节点,分3步完成交换。

然后更新cur,最后return dummyhead.next

  • 关键:操作位cur 一定要指向 待交换两个节点的 前一个节点!

  • 交换的过程:

    • 1.先让cur指向2
    • 2.再让2指向1
    • 3.最后让1指向3
  • 遍历的终止条件?

每次交换前可以判断一下,cur.nextcur.next.next是否存在!

如果节点数量为偶数,cur.next为null了,不进行交换,直接return头节点。(空链表的情况也符合)

如果节点数量为奇数,cur.next存在但cur.next.next为null,就不进行交换,直接return 头节点。

如果都存在,那么才能进行交换。

所以遍历条件是:while(cur.next!=null && cur.next.next!=null)。(两种终止情况都包含在里面了,&&运算符是从左到右的!

  • 具体的交换方式:

注意:cur在一次交换的三步里都没有变过!!!

ListNode tmp1 = cur.next;  //先把1号节点保存一下
cur.next = cur.next.next;  //cur.next=2号节点
ListNode tmp2 = cur.next.next;//再把3号节点保存一下
cur.next.next = tmp1;//2号节点.next=1号节点
tmp1.next = tmp2;    //1号节点.next=tmp

在这里插入图片描述

此时,两个节点已经交换好了,接着更新cur

cur=cur.next.next

注意:最后return dummyhead.next !!! 为什么不能return head?

  • 因为head一直都是指向1号节点,没有变过,不能这样返回。

完整代码

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyhead = new ListNode();
        dummyhead.next = head;
        ListNode cur = dummyhead;
        while(cur.next != null && cur.next.next != null) {
            ListNode tmp1 = cur.next;  //先把1号节点保存一下
            cur.next = cur.next.next;  //cur.next=2号节点
            ListNode tmp2 = cur.next.next;//再把3号节点保存一下
            cur.next.next = tmp1;//2号节点.next=1号节点
            tmp1.next = tmp2;    //1号节点.next=3号节点

            cur = cur.next.next;  //更新cur
        }
        return dummyhead.next;
    }
}

19.删除链表的倒数第N个节点

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

视频链接:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili

讲解链接:代码随想录 (programmercarl.com)

双指针法

用虚拟头节点,定义两个快慢指针,要删除倒数第n个节点,让fast指针先走n步,再让fast和low一起走,直到fast指向链表末尾。再删掉slow指向的就行了。

在这里插入图片描述

注意细节部分:

  • 快慢指针为虚拟头节点。
  • fast先走n步,fast最后停在倒数第一个节点。此时slow指向倒N的前一个位置,方便删除
  • 删除照样要用tmp保存一下
  • 最后return dummyhead.next

完整Java代码:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyhead = new ListNode();
        dummyhead.next = head;
        ListNode fast = dummyhead;
        ListNode slow = dummyhead;
        for(int i = 0; i < n;i++) {  //fast先往后走n步
            fast = fast.next;
        }
        while(fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
        //此时slow.next就是倒数第N个节点
        ListNode tmp = slow.next.next;
        slow.next = tmp;
        return dummyhead.next;
    }
}

面试题 02.07.链表相交

题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)

讲解链接:代码随想录 (programmercarl.com)

思路

简单来说,就是求两个链表交点节点的指针

先求出两个链表的长度,然后求出长度差值,让长的作为链表A。让curA移动到与curB末尾对齐的位置。怎么移?

在这里插入图片描述

**然后curA和curB相当于在同一起跑线了。**此时比较curA和curB是否相同。

如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到了交点,即curA。

否则循环退出返回null。

  • A和B有一个空链表的情况:直接返回null
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        int countA = 0;
        int countB = 0;
        ListNode curA = headA;
        ListNode curB = headB;
        while(curA != null) {
            countA++;
            curA = curA.next;
        }
        while(curB != null) {
            countB++;
            curB = curB.next;
        }
        if(countB > countA) {  //让长的成为A链表
            ListNode tmp = headA;
            headA = headB;
            headB = tmp;
        }
        curA = headA;
        curB = headB;
        //让curA往后移动差值步,curA和curB在同一起跑线了
        for(int i = 0; i < Math.abs(countB - countA); i++) {  
            curA = curA.next;
        }
        while(curA != null) {
            if(curA == curB) return curA;
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }
}

142.环形链表II

题目链接:142. 环形链表 II - 力扣(LeetCode)

视频链接:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili

讲解链接:代码随想录 (programmercarl.com)

双指针判断环

双指针,快指针一次走2步,慢指针一次走1步。快指针一定先进入环,在慢指针也进入环后,实际就是快追慢的过程(套圈),每次差值是1步,环长又是1的倍数,最终一定能追上(如果有环的话)。

  • 慢指针进入环时,两指针相差的节点数一定是小于一圈的!因此相遇时慢指针不可能走满了一圈

在这里插入图片描述

找到环的入口

在这里插入图片描述

在这里插入图片描述

定义变量:

  • 起始位置到入口处为x,入口处到相遇点为y,从y再到入口处为z。

根据这三个定义,结合快指针走2,慢指针走1,可以用一个等式连接!

设快指针在环里走了n圈才相遇。

快指针走过的路径长:n * (y + z) + x + y (这里的n圈是从快指针第一次进环 走到交点处开始算的n圈)

慢指针走过的路径长:x + y

再由快 = 慢 * 2:

n * (y + z) + x + y = (x + y) * 2

n一定是大于等于1圈的(因为慢指针不是停住不动的!)。能让一圈出来,变成(n-1)

变形成:n(y+z)-(y+z)+(y+z)-y

化简得:x = (n-1)(y+z) + z

对这个等式的理解:从相遇点出发的指针从入口路过时总能且仅能和从x出发的指针相遇(相同的速度)。即使转了很多圈,交点处也不会改变

  • 假设n=1的情况下,推断出x=z!!!

也可以这样理解:z是快指针比慢指针多跑的距离,快每次多跑1个,当把环中整圈的距离去掉后,剩下的就是快指针在环外多跑的距离,就是x

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

代码实现

while遍历时判断while(fast!=null && fast.next!=null)

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                ListNode index1 = fast;
                ListNode index2 = head;
                while(index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                //while结束后就找到了入口!
                return index1;
            }
        }
        return null;
    }
}

链表大总结

  • 最后都是return dummyhead.next

  • 要想到双指针。都从dummyhead开始比较好

  • 如果要删除或者修改方向记得用tmp保存

  • 注意遍历while的终止条件!考虑停下来的最终情况(第一次不满足时),此时就是while()括号里的条件,走到这个情况了就不会进入while了。

  • 要删除或者插入节点,要让cur指向它的前一个。

  • 求两个链表交点就是让长的那个先走长度的差值步,再从同一起跑线一起走。

  • 一次走两步,遍历条件是while(fast!=null && fast.next!=null)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值