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

[LeetCode] 24. 两两交换链表中的节点

[LeetCode] 24. 两两交换链表中的节点 文章解释

[LeetCode] 24. 两两交换链表中的节点

核心思想是使用三个节点记录住需要交换的两个节点, 以及这两个节点之前的一个节点. 因为要交换的两个节点交换完顺序后, 他们之前的节点需要链接到这两个节点的后者, 比如原来的顺序是 ... -》A-》B-》C, B 和 C 交换完之后, 需要将 A 指向 C, 即变成 ... -》A -》C -》B.

同时如果不使用虚拟头节点的话, 需要判断的逻辑就非常多. 如果需要对链表做增加、删除、修改操作的话, 使用虚拟头节点会更方便.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
// 不使用虚拟头节点
class Solution {

    public ListNode swapPairs(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode result = null;
        ListNode pre = head;
        ListNode current = head.next;
        ListNode next;

        while (current != null) {
            next = current.next;
            current.next = pre;
            if (result == null) {
                result = current;
            }
            if (next != null) {
                if (next.next != null) {
                    pre.next = next.next;
                } else {
                    pre.next = next;
                }
                pre = next;
                current = next.next;
            } else {
                pre.next = null;
                current = null;
            }
        }
        return result == null ? pre : result;
    }
}
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {

    public ListNode swapPairs(ListNode head) {

        if (head == null) {
            return null;
        }

        // 使用虚拟头节点
        ListNode dummy = new ListNode(0, head);
        ListNode pre = dummy;
        ListNode current = dummy.next;
        ListNode next = null;
        while (current != null && current.next != null) {
            next = current.next;

            pre.next = next;
            current.next = next.next;
            next.next = current;

            pre = current;
            current = current.next;
        }
        return dummy.next;
    }
}

[LeetCode] 19. 删除链表的倒数第 N 个结点

[LeetCode] 19. 删除链表的倒数第 N 个结点 文章解释

[LeetCode] 19. 删除链表的倒数第 N 个结点

这一题完全忘记了, 如何知道倒数第 N 个节点是谁呢? 这里有一个巧妙的思想, 利用双指针, slowIndex 指向虚拟头节点, fastIndex 指向从虚拟头节点往第 N 个节点, 不包括虚拟头节点. 然后将 slowIndex 和 fastIndex 同时往后移动, 直到 fastIndex 移动到最后一个节点, 遇到 fastIndex.next == null 就停止移动. 这时候 slowIndex 指向的是倒数第 N 个节点的前一个节点, 就很好执行删除操作了. 确实很妙.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyNode = new ListNode(0, head);
        ListNode fastNode = dummyNode;
        ListNode slowNode = dummyNode;
        while (n-- > 0 && fastNode != null) {
            fastNode = fastNode.next;
        }
        if (fastNode == null) {
            return dummyNode.next;
        }
        while (fastNode.next != null) {
            fastNode = fastNode.next;
            slowNode = slowNode.next;
        }

        if (slowNode.next != null) {
            slowNode.next = slowNode.next.next;
        }
        return dummyNode.next;
    }
}

[LeetCode] 面试题 02.07. 链表相交

[KamCoder] 面试题 02.07. 链表相交 文章解释

[LeetCode] 面试题 02.07. 链表相交

这一题比较简单, 如果链表相交, 则从相交的节点开始往后的节点都是一致的, 因此从相交的节点开始的链表长度也是一致的. 因此如果两个链表长度不一致, 则可以删除长度更长的链表多出来的那些节点, 然后再从头比对两个链表, 直到找到相同的节点即可.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        int lengthA = 0;
        int lengthB = 0;
        ListNode head = headA;
        while (head != null) {
            lengthA++;
            head = head.next;
        }
        head = headB;
        while (head != null) {
            lengthB++;
            head = head.next;
        }

        if (lengthA > lengthB) {
            for (int i = 0; i < lengthA - lengthB; i++) {
                headA = headA.next;
            }
        } else if (lengthA < lengthB) {
            for (int i = 0; i < lengthB - lengthA; i++) {
                headB = headB.next;
            }
        }

        while (headA != null) {
            if (headA == headB) {
                return headA;
            } else {
                headA = headA.next;
                headB = headB.next;
            }
        }
        return null;
    }
}

[LeetCode] 142. 环形链表 II

[LeetCode] 142. 环形链表 II 文章解释

[LeetCode] 142. 环形链表 II

二刷的时候环形链表就比较简单了.

    第一步判断是会否有环: 使用快慢节点(双指针思想), 快节点每次走两步: fastNode = fastNode.next.next, 慢节点每次走一步: slowNode = slowNode.next. 如果 fastNode 走到链表的结尾, 说明没有环. 如果 slowNode == fastNode 则说明有环.

    第二步寻找环的入口: 假设起点到环的入口节点数量为 x, 环的入口到快慢节点相遇的地方节点数为y, 快慢节点相遇的地方绕着环抵达环的入口一共有 z 个节点. 我们可以得到 2 * (x + y) = x + y + m * (y + z), 其中 m 为快节点和慢节点相遇时, 绕环的圈数. 合并同类项可得: x = m * (y + z) - y, 对等式进行变换可得 x = m * (y + z) - y - z + z, 继续变换可得 x = (m - 1) * (y + z) + z, 也就是说“如果一个节点从快慢节点相遇的地方继续绕着环走, 一个节点从出发点出发, 每次走一个节点, 他们相遇的位置就是环的入口.  这里 x 就是从起点出发到环的入口的节点数, (m - 1) * (y + z) + z 是从环的快慢节点相遇的地方出发, 绕 m - 1 圈后, 继续走 z 个节点, 由 z 的定义可知, 绕 m - 1 圈后再走 z 个节点, 这时候是抵达到环的入口的.

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fastNode = head;
        ListNode slowNode = head;

        boolean hasCircle = false;

        while (fastNode != null && fastNode.next != null) {
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
            if (slowNode == fastNode) {
                break;
            }
        }
        if (fastNode == null || fastNode.next == null) {
            // 走到末尾了, 没有环
            return null;
        }

        while (head != fastNode) {
            head = head.next;
            fastNode = fastNode.next;
        }
        return head;
    }
}

  • 18
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值