Day4 两两交换链表节点、删除链表的倒数第 N 个结点、环形链表 II、链表相交

10

两两交换链表中的节点

难点在于,要用到临时变量记录被交换位置的节点,边界的处理,以及cur的移动

public ListNode swapPairs(ListNode head) {
        //定义哨兵节点
        ListNode s = new ListNode(0, head);
        //定义当前指针位置
        ListNode cur = s;
        //临时变量记录要被交换的节点
        ListNode temp1;
        //临时变量记录cur要移动的位置
        ListNode temp2;
        //定义循环边界
        while(cur.next != null && cur.next.next != null){
            temp1 = cur.next;
            temp2 = cur.next.next.next;
            cur.next = cur.next.next;
            cur.next.next = temp1;
            temp1.next = temp2;
            cur = cur.next.next;
        }
        return s.next;
    }

删除链表的倒数第 N 个结点

难点在于要删除倒数n个节点,就要拿到前一个节点的指针,也就是快指针走n+1步

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode s = new ListNode(-1, head);
        ListNode p1 = s;
        ListNode p2 = s;
        for (int i = 0; i < n + 1; i++) {
            p2 = p2.next;
        }
        while (p2 != null) {
            p1 = p1.next;
            p2 = p2.next;
        }
        p1.next = p1.next.next;
        return s.next;
    }

环形链表 II

慢指针一次走一步,快指针一次走两步,慢指针能追上快指针就说明有环。

【注意】快指针在原地,慢指针到起点,同时走一步,相遇了,同时走一步,说明是环的入口,但是要考虑特殊情况:如果是环形链表,很明显,上一步才是环入口,所以要提前判断slow == fast。

public ListNode detectCycle(ListNode head) {
        ListNode slow = head; //慢指针
        ListNode fast = head; //快指针
        while(fast!=null && fast.next!=null){ //防止快指针走两步走到null了导致空指针,再加个判断
            slow = slow.next;
            fast = fast.next.next;
            //假如是环形链表,那么相等的位置就是入口
            if(slow == fast){ //第一次相遇
                //进入第二阶段,快指针留在原地,慢指针回到起点,再次相遇的时候就是入口
                slow = head;
                //快慢指针同时移动一步,直到相遇,就是入口
                while (true){
                    if(slow == fast){//第二次又相遇,说明是环形链表,所处的节点就是入口
                        return slow;
                    }
                    slow = slow.next;
                    fast = fast.next;
                }
            }
        }
        return null;
    }

面试题 02.07. 链表相交

这道题目难点在认识到:

  • 双指针的路径长度相同,所以一定会相遇。

  • 相遇点就是第一个公共节点(相交节点)。

  • 不相交时,双指针会同时走到 null

演示:

每个指针都走一步

链表B遍历完成了,开始遍历链表A

链表A遍历完成,开始遍历链表B

指针AB重合于公共交点,返回A即可

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 初始化两个指针,分别指向两个链表的头节点
        ListNode A = headA;
        ListNode B = headB;

        // 当两个指针不相遇时,继续循环
        while (A != B) {
            // 如果指针 A 走到链表 A 的末尾,则跳到链表 B 的头节点
            // 否则,继续向后移动
            A = (A == null) ? headB : A.next;

            // 如果指针 B 走到链表 B 的末尾,则跳到链表 A 的头节点
            // 否则,继续向后移动
            B = (B == null) ? headA : B.next;
        }

        // 循环结束时,A 和 B 要么指向相交节点,要么都为 null(不相交)
        return A;
    }