代码随想录算法训练营第四天|链表part02|24.19.面试02.07.142

2023/7/29 任务
24. 两两交换链表中的节点,19.删除链表的倒数第N个节点面试题,02.07. 链表相交,142.环形链表II ,总结

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

题目链接/文章讲解/视频讲解

代码随想录算法训练营第四天|链表part02|24.19.面试02.07.142_第1张图片

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(-1, head);
        ListNode pre = dummy;
        // pre为所有操作的基本点,cur和nex为交换的两个节点
        // 当节点数为偶数时,如上图,3和4交换完毕后pre指向4,需要保证pre.next不为空;当节点数为奇数时,需pre.next.next不为空。
        while (pre.next != null && pre.next.next != null) {
            ListNode cur = pre.next;
            ListNode nex = pre.next.next;
            pre.next = nex;
            cur.next = nex.next;
            nex.next = cur;
            pre = cur;
        }
        return dummy.next;
    }
}

时间复杂度:O(n)
空间复杂度:O(1)

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

题目链接/文章讲解/视频讲解

  • 双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
  • 注意,删除第N个节点,那么我们当前遍历的指针一定要指向第N个节点的前一个节点。
  • 本来在while的前后两句代码段都加了判断条件next != null,但是第一个for处不需要是因为倒数第n个节点一定在总节点数的范围内,不存在找倒数第3个节点,结果链表只有1个节点这样的情况。第二个slow处也不要是因为slow走的是fast的老路并且保证fast和slow的差值了,不会有空指针异常。
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1, head);
        ListNode fast = dummy;
        ListNode slow = dummy;
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return dummy.next;
    }
}

时间复杂度: O(n)
空间复杂度: O(1)

面试题 02.07.
链表相交

题目链接/文章讲解

根据两个链表的长度差,使长的经过长度差后与短链表起始位置相同,保证了同时走到末尾。因为链表一旦相交,相交后的部分一定是一样的。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        ListNode a = headA;
        ListNode b = headB;
        int aL = 1;
        int bL = 1;
        while (a.next != null) {
            a = a.next;
            aL++;
        }
        while (b.next != null) {
            b = b.next;
            bL++;
        }
        a = headA;
        b = headB;
        int l = aL - bL;
        if (l > 0) {
            for (int i = 0; i < l; i++) {
                a = a.next;
            }
        } else {
            l = -l;
            for (int i = 0; i < l; i++) {
                b = b.next;
            }
        }
        while (a != b) {
            a = a.next;
            b = b.next;
        }
        if (a == b) {
            return a;
        }
        return null;
    }
}

时间复杂度:O(n + m)
空间复杂度:O(1)

142.环形链表II

题目链接/文章讲解/视频讲解

  • 判断链表是否有环

fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇。那么为什么有环一定会相遇?因为fast是相对slow多一个节点靠近slow的,所以fast一定可以和slow重合。

  • 如果有环,如何找到这个环的入口

假设从头结点到环形入口节点的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点节点数为y。 从相遇节点再到环形入口节点节点数为 z。

相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针,n>=1。

代码随想录算法训练营第四天|链表part02|24.19.面试02.07.142_第2张图片

代码随想录算法训练营第四天|链表part02|24.19.面试02.07.142_第3张图片

n的意义就是fast指针在环形内转n圈之后才遇到 slow指针,可以说n对结果没影响。
x = z : 从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

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) {
                fast = head;
                while (fast != slow) {
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;
            }
        }
        return null;
    }
}

时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度: O(1)

你可能感兴趣的:(算法训练营,算法)