0
点赞
收藏
分享

微信扫一扫

【LeetCode】23. 合并K个升序链表 & 24. 两两交换链表中的节点 & 25. K 个一组翻转链表

上古神龙 2022-03-12 阅读 54

23. 合并K个升序链表【堆】【归并】

归并

思想如下图,两两合并。

在这里插入图片描述

  • 时间复杂度分析:假设链表长度为 n n n,共有 k k k个链表,可以看到每一层都共有 k ∗ n k*n kn个节点进行合并,合并的次数为递归深度 l o g k logk logk。因此时间复杂度为: O ( k × n × l o g k ) O(k×n×logk) O(k×n×logk)
  • 空间复杂度为递归深度: O ( l o g k ) O(logk) O(logk)
/**
 * 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 mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        return merge(lists, 0, lists.length - 1);
    }
    
    // 递归划分
    private ListNode merge(ListNode[] lists, int low, int high) {
        if (low == high) {
            return lists[low];
        }

        int mid = (low + high) / 2;
        ListNode l1 = merge(lists, low, mid);
        ListNode l2 = merge(lists, mid + 1, high);
        return merge2Lists(l1, l2);
    }

    // 合并两个有序链表
    private ListNode merge2Lists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode();
        ListNode tail = dummy;

        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                tail.next = l1;
                l1 = l1.next;
            } else {
                tail.next = l2;
                l2 = l2.next;
            }
            tail = tail.next;
        }

        tail.next = l1 != null ? l1 : l2;

        return dummy.next;
    }
}

方法二:小根堆(优先队列)

将链表都存到小根堆中,实质上,堆中存的的是链表的头节点,并且会按照增序进行排列。每次从堆中取出头节点最小的链表,然后将该链表的头节点插入到结果链表的末尾,最后再将去点头节点的链表加回到堆中。

  • 时间复杂度:堆中元素不超过 k k k个,那么插入/删除的时间复杂度为 O ( l o g k ) O(logk) O(logk),而一共最多有 k n kn kn个节点,因此时间复杂度为 O ( k × n × l o g k ) O(k×n×logk) O(k×n×logk)
  • 空间复杂度:堆的大小 O ( k ) O(k) O(k)
/**
 * 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 mergeKLists(ListNode[] lists) {
        // 小根堆
        Queue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
        for (ListNode elem : lists) {
            if (elem != null) {
                pq.offer(elem);
            }
        }

        ListNode dummy = new ListNode();
        ListNode tail = dummy;
        while (!pq.isEmpty()) {
            ListNode minElem = pq.poll(); // 取出头节点最小的链表
            tail.next = minElem; // 将头节点元素加入结果链表
            tail = tail.next;
            if (minElem.next != null) { // 将去除头节点后的链表加回堆
                pq.offer(minElem.next);
            }
        }

        return dummy.next;

    }
}

在这里插入图片描述

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

设置一个虚头节点。
将p的前驱pre后插到p的后面。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
/**
 * 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) {
        // 空链表,返回null
        if (head == null) {
            return null;
        }

        // 虚头节点
        ListNode dummy = new ListNode();
        dummy.next = head;
        // pre : 两个待交换节点中的前一个
        // p : 两个待交换节点中的后一个
        // tmp : pre交换前的前驱节点
        ListNode pre = head, p, tmpHead = dummy;
        while (pre != null && pre.next != null) {
            p = pre.next;
            tmpHead.next = p;
            pre.next = p.next;
            p.next = pre;
            tmpHead = pre;
            pre = pre.next;
        }

        return dummy.next;
    }
}

25. K 个一组翻转链表

先求链表长度,以计算出需要反转的子段有几个。
设置虚头节点,即实际需要反转的子段的前一个节点。
反转函数,将长度为k的链表反转,并返回下一个需要反转的链表的虚头节点。

/**
 * 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 reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(); // 要返回的链表的虚拟头节点
        dummy.next = head;

        int len = 0; // 链表长度
        ListNode q = head;
        while (q != null) {
            ++len;
            q = q.next;
        }
        int maxCnt = len / k; // 需要反转的段数

        // 进行分段反转
        ListNode nextHead = dummy;
        for (int i = 0; i < maxCnt; i++) {
            if (nextHead.next != null) {
                nextHead = reverse(nextHead, k);
            }
        }

        return dummy.next;
    }

    // 反转k个节点,并返回下一段的虚头节点
    private ListNode reverse(ListNode head, int k) {
        ListNode p = head.next, q = head;
        ListNode nextHead = head.next;
        head.next = null;
        for (int i = 0; i < k; i++) {
            q = p.next;
            p.next = head.next;
            head.next = p;
            p = q;
        }
        nextHead.next = q;
        
        return nextHead;
    }
}
举报

相关推荐

0 条评论