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

269 阅读3分钟

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

题目链接:24. 两两交换链表中的节点

思路:找到要交换的两个节点的前一个节点就可以进行交换,修改链表指针基本操作。注意,有虚拟头结点操作更方便。

时间复杂度:O(n)

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode i = dummy;
        ListNode j = head;
        while (i.next != null && j.next != null) {
            i.next = j.next;
            j.next = j.next.next;
            i.next.next = j;
            i = j;
            j = j.next;
        }
        return dummy.next;
    }
}

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

题目链接:19.删除链表的倒数第N个节点

思路:双指针法。通过双指针实现一次遍历找到倒数第n个节点。注意,有虚拟头结点操作更方便。

时间复杂度:O(n)

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) { // 双指针法
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode left = dummy;
        ListNode right = dummy;
        for (int i = 0; i < n; i++) {
            right = right.next;
        }
        while (right.next != null) {
            left = left.next;
            right = right.next;
        }
        left.next = left.next.next;
        return dummy.next;
    }
}

面试题 02.07. 链表相交

题目链接:面试题 02.07. 链表相交

思路:先判断链表是否相交,链表不相交返回null,相交的话根据记录的链表长度,让长的链表指针先走,走到和短的链表同样长的时候,两个链表的指针同时再走,就可以找到第一个相交节点。

时间复杂度:O(n)

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headB == null || headA == null) {
            return null;
        }
        // 先判断是否相交
        ListNode i = headA;
        ListNode j = headB;
        int lenA = 0, lenB = 0;
        while (i.next != null) {
            i = i.next;
            lenA++;
        }
        while (j.next != null) {
            j = j.next;
            lenB++;
        }
        if (i != j) {
            return null;
        }
        i = headA;
        j = headB;
        while (lenA > lenB) {
            i = i.next;
            lenA--;
        }
        while (lenB > lenA) {
            j = j.next;
            lenB--;
        }
        while (i != null && j != null) {
            if (i == j) {
                return i;
            }
            i = i.next;
            j = j.next;
        }
        return null;
    }
}

142.环形链表II

题目链接:142.环形链表II

思路:定义快慢指针,慢指针每次走一步,快指针每次比慢指针多走一步。首先判断是否有环,如果有环的话,快指针一定先进环,慢指针进环后第一圈快慢指针一定相遇,没有环的话返回null。通过数学推导,可以确定,快指针从相遇的节点开始走一步,慢指针从第一个节点开始走一步,两个指针再次相遇的时候就是入环节点。

这里引用卡哥网站上的内容记录一下数学推导过程

20210318162938397.png 相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z)

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示头结点到环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

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

时间复杂度:O(n)

我的代码实现

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }
        // 判断是否有环
        ListNode slow = head;
        ListNode fast = head;
        while (slow.next != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast || slow == null || fast == null) {
                break;
            }
        }
        if (slow != fast) {
            return null;
        }
        // 寻找入环节点
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
            if (slow == fast) {
                break;
            }
        }
        return slow;
    }
}

卡哥代码实现

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {// 有环
                ListNode index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}