Merge k Sorted Lists
问题:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
思路:
归并排序(二分问题)
我的代码:
public class Solution { public ListNode mergeKLists(List<ListNode> lists) { if(lists == null || lists.size() == 0) return null; if(lists.size() == 1) return lists.get(0); int n = lists.size(); int i = 0; List<ListNode> left = new ArrayList<ListNode>(); List<ListNode> right = new ArrayList<ListNode>(); while(i < n/2) { left.add(lists.get(i)); i++; } while(i < n) { right.add(lists.get(i)); i++; } ListNode leftNode = mergeKLists(left); ListNode rightNode = mergeKLists(right); return mergeTwoLists(leftNode, rightNode); } public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(-1); ListNode rst = dummy; rst.next = null; while(l1 != null && l2 != null) { int val1 = l1.val; int val2 = l2.val; if(val1 < val2) { dummy.next = l1; l1 = l1.next; } else { dummy.next = l2; l2 = l2.next; } dummy = dummy.next; } if(l1 != null) { dummy.next = l1; } if(l2 != null) { dummy.next = l2; } return rst.next; } }
他人代码1:
/* SOL 1: 使用merge sort和分治法完成 */ public ListNode mergeKLists1(List<ListNode> lists) { // 记得加上这个合法性判断。 if (lists == null || lists.size() == 0) { return null; } return helper(lists, 0, lists.size() - 1); } /* l, r表示list的左右边界 */ public ListNode helper(List<ListNode> lists, int l, int r) { if (l < r) { int mid = l + (r - l) / 2; /* 分治法。把问题分为2个更小的子问题:左边list的merge,和右边list的merge. 再把2个生成的解合并在一起。 */ return merge(helper(lists, l, mid), helper(lists, mid + 1, r)); } return lists.get(l); } public ListNode merge(ListNode n1, ListNode n2) { ListNode dummy = new ListNode(0); ListNode cur = dummy; while (n1 != null && n2 != null) { if (n1.val < n2.val) { cur.next = n1; n1 = n1.next; } else { cur.next = n2; n2 = n2.next; } cur = cur.next; } if (n1 != null) { cur.next = n1; } else { cur.next = n2; } return dummy.next; }
他人代码2:
/* SOL 2: 使用 priority Queue. */ public ListNode mergeKLists(List<ListNode> lists) { // 记得加上这个合法性判断。 if (lists == null || lists.size() == 0) { return null; } int size = lists.size(); PriorityQueue<ListNode> q = new PriorityQueue<ListNode>(size, new Comparator<ListNode>() { // 注意,此处参数用ListNode public int compare(ListNode o1, ListNode o2) { return o1.val - o2.val; } } ); // Add all the head node to the priority queue. for (ListNode node: lists) { if (node != null) { // Should skip the null node.s q.offer(node); } } ListNode dummy = new ListNode(0); ListNode tail = dummy; while (!q.isEmpty()) { // get the smallest node from the queue. ListNode cur = q.poll(); tail.next = cur; tail = tail.next; // 将下一个节点补充进来。 if (cur.next != null) { q.offer(cur.next); } } return dummy.next; }
学习之处:
- 此问题亦是二分查找的变形,我的方法是通过个数进行划分。
- 而他人的代码通过left和right就比较成功了,对于递归中,left == right的情况也catch到了 left<right的地方也catch到了,十分完美的算法
- 通过最小堆的方式亦可以解决这个问题,最小堆这个数据结果需要学习一下,建立堆的时间复杂度为O(nlgn),以当所有链表都读完时结束,这个时候所有元 素按从小到大放在结果链表中。这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要logk的复杂度,所以总时间复杂度是 O(nklogk)。空间复杂度是堆的大小,即为O(k)