链表双指针技巧

题目 难度 要点
分隔链表 快慢指针:不用两个新链表拼接,使用原地修改
合并K个升序链表 最小堆:类ProirityQueue的使用
环形链表 快慢指针:相遇有环
环形链表II 快慢指针:入环第一个节点

分隔链表

image
题目要求按原顺序,以x值将小的放链表前半段,其他的放链表后半段。很容易想到一种解法,新开两条链表分别存放,最后将两条链表合并。题目难度给个中等,可以考虑换个复杂点的原地修改解法。那么需要了解2个地方的信息:1.待插入的节点 2.需要移动的节点。很容易就可以想到快慢指针,慢指针等待插入,快指针寻找小于x的节点,进行操作即可。

public ListNode partition(ListNode head, int x) {
        ListNode dummy = new ListNode(-1, head);
        ListNode slow = dummy;
        while (slow.next != null && slow.next.val < x) {
            slow = slow.next;
        }
        ListNode fast = slow;
        while (fast != null && fast.next != null) {
            if (fast.next.val < x) {
                ListNode temp = fast.next;
                fast.next = temp.next;
                temp.next = slow.next;
                slow.next = temp;
                slow = slow.next;
            } else {
                fast = fast.next;
            }
        }
        return dummy.next;
    }

合并K个升序链表

image
两个链表合并的升级版。JAVA没有大小堆的直接类,但是优先级队列类底层默认为最小堆,可指定Comparator变为最大堆。
思路:首先放链表个数的节点到最小堆中,每次取出最小的置于新链表。如果节点有子节点则加入优先级队列自动排序。重复步骤直到队列为空即可。

    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        ListNode dummy = new ListNode(), p = dummy;
        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length, (a, b) -> (a.val - b.val));
        for (ListNode node: lists) {
            if (node != null) {
                pq.add(node);
            }
        }
        while (!pq.isEmpty()) {
            ListNode node = pq.poll();
            if (node.next != null) {
                pq.add(node.next);
            }
            p.next = node;
            p = p.next;
        }
        return dummy.next;
    }

环形链表


快慢指针经典题,两个指针相遇即有环,否则无环。

    public boolean hasCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }

环形链表II


环形链表升级,返回入环的第一个节点。借用截图中例子,任意假设0节点相遇,那么此时slow跑了长度n,fast跑了长度2n。假设2节点到0节点长度为k,那么通过slow可得3节点到2节点长度为n-k,通过fast(fast比slow多跑了一个圈)可得-4节点到2节点长度为2n-n-k=n-k。那么slow或fast重置为head,都单步前进直到相遇即答案。

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

··

posted @ 2023-02-27 16:14  kiper  阅读(21)  评论(0编辑  收藏  举报