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

375 阅读3分钟

形与数

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

题目链接leetcode.cn/problems/sw…

题目要求:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)

代码:

// 双指针法
class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode temp = null;
        ListNode temp2 = null;
        ListNode pre = new ListNode(-1, head); // 虚拟头结点
        ListNode res = pre.next.next;
        ListNode cur = head;
        while (cur != null && cur.next != null) {
            temp = pre.next;
            pre.next = cur.next;
            temp2 = pre.next.next;
            pre.next.next = temp;
            cur.next = temp2;

            pre = cur;
            cur = pre.next;
        }
        return res;
    }
}

// 递归法
class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode t = head.next;
        head.next = swapPairs(t.next);
        t.next = head;
        return t;
    }
}

解题之思考及结果

这题还是建议画图比较好理解,因为指针指来指去的不画图容易晕乎。

双指针法:定义一个pre节点作为虚拟头结点,pre指针指向上一个节点,cur指向当前节点,方便操作。期间需要用temp1和temp2两个指针来暂存操作时的指针防止丢失,其他就是模拟循环过程了。

递归法:附上一个解读递归比较好的文章lyl0724.github.io/2020/01/25/…


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

题目链接leetcode.cn/problems/re…

题目要求:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

代码:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode f = head;
        ListNode l = head;
        ListNode pre = new ListNode(-1, head);
        ListNode res = pre;
        int index = 1;

        // 先知道链表长度,再根据链表长度删除对应节点
        while (l.next != null) {
            l = l.next;
            index++;
        }
        while ((n - (index--)) != 0) {
            pre = f;
            f = f.next;
        }
        pre.next = f.next;

        return res.next;
    }
}

解题之思考及结果

这题我用的是挺普通的解法,先求得链表的长度,然后再让头指针走 (n - index-- == 0) 步即到达待删节点的前一节点。


160. 链表相交

题目链接leetcode.cn/problems/in…

题目要求:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

代码:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode a = headA;
        ListNode b = headB;
        while (a != b) {
            a = a != null ? a.next : headB;
            b = b != null ? b.next : headA;
        }
        return a;
    }
}

解题之思考及结果

阅读了Krahets大神的代码,惊叹于代码之美。此题核心在于两链表A、B走的路会有一个重合点,设链表重合部分为C,则有A+C+B = B+C+A 。那么只要headA和headB走完自己的路再走别人的路,不断重复,那么总会重合


142.环形链表II

题目链接leetcode.cn/problems/li…

题目要求:给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表

代码:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        // 判断有没有环,有环必定相遇
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            // 查找入环点
            if (fast == slow) {
                ListNode index1 = head;
                ListNode index2 = fast;
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        
        return null;
    }
}

解题之思考及结果

此题要解决的问题有二:

一为如何判断有环;

二为如果有环该怎么找到入环点。

解决问题一很简单,我们只需要定义快慢指针,快指针每次走两步,慢指针每次走一步。那么如果有环,他们总会相遇。既然相遇了那么怎么找到入环点。这里我推荐Carl兄的视频,因为这视频里数学或是逻辑上都不错。

把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II