剑指 Offer II 078. 合并排序链表(23. 合并 K 个升序链表)
题目:
思路:
【1】顺序合并
【2】分治合并
【3】使用优先队列合并
代码展示:
顺序合并的方式:
//时间99 ms击败23.18% //内存43.6 MB击败9.45% //时间复杂度:假设每个链表的最长长度是 n。在第一次合并后,ans 的长度为 n; //第二次合并后,ans 的长度为 2×n,第 i 次合并后,ans 的长度为 i×n。 //第 i 次合并的时间代价是 O(n+(i−1)×n)=O(i×n), //那么总的时间代价为 O((1+k)⋅k/2*n) =O(k^2*n),故渐进时间复杂度为 O(k^2*n))。 //空间复杂度:没有用到与 k 和 n 规模相关的辅助空间,故渐进空间复杂度为 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 mergeKLists(ListNode[] lists) { ListNode ans = null; for (int i = 0; i < lists.length; ++i) { ans = mergeTwoLists(ans, lists[i]); } return ans; } public ListNode mergeTwoLists(ListNode a, ListNode b) { if (a == null || b == null) { return a != null ? a : b; } ListNode head = new ListNode(0); ListNode tail = head, aPtr = a, bPtr = b; while (aPtr != null && bPtr != null) { if (aPtr.val < bPtr.val) { tail.next = aPtr; aPtr = aPtr.next; } else { tail.next = bPtr; bPtr = bPtr.next; } tail = tail.next; } tail.next = (aPtr != null ? aPtr : bPtr); return head.next; } }
分治合并的方式:
//时间1 ms击败100% //内存43.8 MB击败5.15% //时间复杂度:考虑递归「向上回升」的过程——第一轮合并 k/2 组链表,每一组的时间代价是 O(2n); //第二轮合并 k/4 组链表,每一组的时间代价是 O(4n)......所以总的时间代价是 O(kn×logk), //故渐进时间复杂度为 O(kn*logk)。 //空间复杂度:递归会使用到 O(logk) 空间代价的栈空间。 class Solution { public ListNode mergeKLists(ListNode[] lists) { return merge(lists, 0, lists.length - 1); } public ListNode merge(ListNode[] lists, int l, int r) { if (l == r) { return lists[l]; } if (l > r) { return null; } int mid = (l + r) >> 1; return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r)); } public ListNode mergeTwoLists(ListNode a, ListNode b) { if (a == null || b == null) { return a != null ? a : b; } ListNode head = new ListNode(0); ListNode tail = head, aPtr = a, bPtr = b; while (aPtr != null && bPtr != null) { if (aPtr.val < bPtr.val) { tail.next = aPtr; aPtr = aPtr.next; } else { tail.next = bPtr; bPtr = bPtr.next; } tail = tail.next; } tail.next = (aPtr != null ? aPtr : bPtr); return head.next; } }
使用优先队列合并的方式:
//时间4 ms击败65.16% //内存43 MB击败89.47% //时间复杂度:考虑优先队列中的元素不超过 k 个,那么插入和删除的时间代价为 O(logk), //这里最多有 kn 个点,对于每个点都被插入删除各一次, //故总的时间代价即渐进时间复杂度为 O(kn×logk)。 //空间复杂度:这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O(k)。 class Solution { class Status implements Comparable<Status> { int val; ListNode ptr; Status(int val, ListNode ptr) { this.val = val; this.ptr = ptr; } public int compareTo(Status status2) { return this.val - status2.val; } } PriorityQueue<Status> queue = new PriorityQueue<Status>(); public ListNode mergeKLists(ListNode[] lists) { for (ListNode node: lists) { if (node != null) { queue.offer(new Status(node.val, node)); } } ListNode head = new ListNode(0); ListNode tail = head; while (!queue.isEmpty()) { Status f = queue.poll(); tail.next = f.ptr; tail = tail.next; if (f.ptr.next != null) { queue.offer(new Status(f.ptr.next.val, f.ptr.next)); } } return head.next; } }