23.Merge k Sorted Lists (Array, Queue; Sort)
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
思路I: 选择排序
每次都比较各个list的头指针所指的val,取最小的那个。时间复杂度O(n*k)
class Solution { public: ListNode *mergeKLists(vector &lists) { if(lists.empty()) return NULL; ListNode *head = new ListNode(INT_MIN); //创建头指针,不指向任何List元素;最后返回的是head->next head->next = lists[0]; ListNode *p1; ListNode *p2; ListNode *tmp; for(int i = 1; i< lists.size(); i++) { p1 = head; p2 = lists[i]; while(p1->next && p2) { if(p1->next->val <= p2->val) p1 = p1->next; else { tmp = p1->next; p1->next = p2; p1 = p1->next; p2 = p2->next; p1->next = tmp; } } if(p2) { p1->next = p2; } } return head->next; } };
Result:Time Limit Exceeded
思路II: 最小堆。
时间复杂度:堆排序其实也是一种选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。时间复杂度为O(nlogk)
堆排序思想参考:http://jingyan.baidu.com/article/5225f26b057d5de6fa0908f3.html
class Solution { public: ListNode *mergeKLists(vector<ListNode *> &lists) { // 使用堆排序, // 1. 选出每个链表的头来插入小顶堆中, // 2. 再把堆顶接入合并链表中, // 3. 被选出的指针后移再加入小顶堆中,回到2 // 4. 最后所有链表都为空时,返回合并链表的头指针 if(lists.empty()) return nullptr; vector<ListNode* > heap; heap.push_back(0); //padding // 1. 选出每个链表的头来插入小顶堆中, for(int i = 0; i != lists.size(); i ++){ if(lists[i]) heap.push_back(lists[i]); } makeHeap(heap); // 2. 再把堆顶接入合并链表中, ListNode head(-1); // 合并链表的表头 ListNode* p = &head; while(heap.size()>1){ auto minNode = heap[1]; p->next = minNode; // 接入链表 p = p->next; // 3. 被选出的指针后移再加入小顶堆中,回到2 auto next = minNode->next; if(next) { heap[1] = next; }else{ swap(heap[1], heap[heap.size()-1]); heap.pop_back(); } minHeap(heap, 1);//加入新元素到堆顶后,自上向下调整 } // 4. 最后所有链表都为空时,返回合并链表的头指针 return head.next; } // 建立小顶堆 // 自底向上 void makeHeap(vector<ListNode*> &heap){ // 从最后一个元素的父节点开始建立小顶堆 for(int i = (heap.size()-1)/2; i >0 ; i --){ minHeap(heap, i); } } // 小顶堆,以第i个元素为根建立小顶堆 //位置从1开始,取元素时记得-1 // 自顶向下 void minHeap(vector<ListNode*> &heap, int i){ int l = i*2; int r = l+1; int least(i); // 算出最小元素的位置 if((l< heap.size()) && heap[l]->val<heap[i]->val ){ // 如果没有超过边界并且左孩子比父亲小,则换 least = l; } if(r<heap.size() && heap[r]->val<heap[least]->val){ // 如果没有超过边界并且右孩子最小,则换 least = r; } if(least != i){ swap(heap[i], heap[least]); minHeap(heap, least);//换了之后,继续向下调整 } } };
C++的优先队列底部就是用堆排序实现的,所以这里可以使用优先队列。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ struct cmp { bool operator () (ListNode *a, ListNode *b) { return a->val > b->val; } }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*,vector<ListNode*>,cmp> queue; for (int i = 0; i < lists.size(); i++) { if (lists[i] != NULL) { queue.push(lists[i]); } } ListNode *head = new ListNode(INT_MIN); ListNode *prev = head, *temp; while (!queue.empty()) { temp = queue.top(); queue.pop(); prev->next = temp; prev = prev->next; if (temp->next != NULL) { queue.push(temp->next); } } return head->next; } };
优先队列默认从大到小排序,即大顶堆,所以这里我们需要改成小顶堆:
1. 基本类型(例如int)都可以用greater<type>声明小顶堆优先队列:
priority_queue<int, vector<int>, greater<int>> q
2. 自定义的数据结构必须自定义比较函数(如以上代码中定义的cmp),或者自定义 > 操作:
bool operator > (ListNode* a, ListNode* b){ return a->val > b->val; }
思路III:归并排序
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
reference: http://baike.baidu.com/link?url=ayX3MQx_CrmcjOxkL7EKhXukLH9pJKJsD1XDMaP6eQwvFfc-BtnQBUTsElRafXbxqhCFOIlKC5VsL14LgjEjIK
归并排序时间复杂度O(nlogn)
/** * 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.empty()) return NULL; mergeSort(lists, 0, lists.size()-1); return lists[0]; } void mergeSort(vector<ListNode*>& lists, int start, int end){ if(start==end) return; int mid = (start + end) >> 1; mergeSort(lists, start, mid); mergeSort(lists, mid+1, end); merge(lists,start, mid+1); } void merge(vector<ListNode*>& lists, int lst1, int lst2){ ListNode* root = NULL; ListNode* current = NULL; while(lists[lst1] && lists[lst2]){ if(lists[lst1]->val <= lists[lst2]->val){ if(!root){ root = lists[lst1]; current = root; } else{ current->next = lists[lst1]; current = current->next; } lists[lst1] = lists[lst1]->next; } else{ if(!root){ root = lists[lst2]; current = root; } else{ current->next = lists[lst2]; current = current->next; } lists[lst2] = lists[lst2]->next; } } while(lists[lst1]){ if(!root){ root = lists[lst1]; current = root; } else{ current->next = lists[lst1]; current = current->next; } lists[lst1] = lists[lst1]->next; } while(lists[lst2]){ if(!root){ root = lists[lst2]; current = root; } else{ current->next = lists[lst2]; current = current->next; } lists[lst2] = lists[lst2]->next; } lists[lst1] = root; } };