leetcode : Merge K sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
题目的重点在与分析描述复杂度。。。一看到多路归并就想到外部排序结果果断二了个逼。
两种思路,一种是建个胜者树或者败者树进行k路归并。那么平均每得到一个最小值,需要进行logk次比较,一共k*n个元素,
复杂度为logk * n * k,即O(n)
其实建一个小根堆也可以,但是堆每次得到一个最小的元素,需要进行一次删除,一次插入,比败者树慢
第二种是建一个平衡二叉树,每个叶子节点为一个list,慢慢向上归并
每一层的归并复杂度为k*n,一共logk层,所以总的复杂度为k *logk *n,还是O(n)
所以非常逗比的两种思路的复杂度是一样的!!!
更进一步的,每次将一个新的段与第一段归并,那么复杂度为
2n + 3n + .. k*n = O(k^2 *n)。还是O(n)。。。。
简单说来就是怎么都要O(n)..
再下面是外部排序,真是不知道哪个逗比在写外部排序的时候,说k路归并,每次找一个最小的需要k-1次比较,所以用败者树优化,
使得只要比较logk次。导致惯性的以为上面的思路1比思路2要快。
实际上对于外部排序思路1确实比思路2快,因为相比思路2,建立一个败者树大大减少了读取外存的次数--每个input文件都只要读一次,而最终的output文件也只要写一次。
AC代码:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *mergeKLists(vector<ListNode *> &lists) { if(lists.size() == 0) return nullptr; return doMerge(lists, 0, lists.size() - 1); } ListNode *doMerge(vector<ListNode *> &lists, int start, int end){ if(start + 1 == end) return mergeTwoLists(lists[start], lists[end]); else if(start == end) return lists[start]; else{ auto l1 = doMerge(lists, start, (start + end) / 2); auto l2 = doMerge(lists, (start + end) / 2 + 1, end); return mergeTwoLists(l1, l2); } } ListNode *mergeTwoLists(ListNode *l1, ListNode *l2){ ListNode *head = nullptr, *tail = nullptr; while(l1 && l2){ if(l1->val < l2->val){ if(head){ tail->next = l1; }else{ head = l1; } tail = l1; l1 = l1->next; }else{ if(head){ tail->next = l2; }else{ head = l2; } tail = l2; l2 = l2->next; } } auto temp = l1 ? l1 : l2; if(head) tail->next = temp; else head = temp; return head; } };
浙公网安备 33010602011771号