[Leetcode] Merge k sorted lists 合并k个已排序的链表
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
思路:这题最容易想到的是,(假设有k个链表)链表1、2合并,然后其结果12和3合并,以此类推,最后是123--k-1和k合并。至于两链表合并的过程见merge two sorted lists的分析。复杂度的分析见JustDoIT的博客。算法复杂度:假设每个链表的平均长度是n,则1、2合并,遍历2n个节点;12结果和3合并,遍历3n个节点;....123...k-1的结果和k合并,遍历kn个节点,总共遍历n(2+3+4+....k)=n*(k^2+k-2)/2,因此时间复杂度是O(n*(k^2+k-2)/2)=O(nk^2)。 其次,可以想到用分治的思想,两两合并直至最后。
针对方法一,
1 class Solution { 2 public: 3 //用向量存储链表 4 ListNode *mergeKLists(vector<ListNode *> &lists) 5 { 6 if(lists.size()==0) return NULL; 7 ListNode *res=lists[0]; //取出第一个链表 8 for(int i=1;i<lists.size();i++) //反复调用 9 res=mergeTwoList(res,lists[i]); 10 11 return res; 12 } 13 14 //归并排序 15 ListNode *mergeTwoList(ListNode *head1,ListNode *head2) 16 { 17 ListNode node(0); //创建头结点的前置结点 18 ListNode *res=&node; 19 while(head1&&head2) 20 { 21 if(head1->val<=head2->val) 22 { 23 res->next=head1; 24 head1=head1->next; 25 } 26 else 27 { 28 res->next=head2; 29 head2=head2->next; 30 } 31 res=res->next; 32 } 33 34 if(head1) 35 res->next=head1; 36 else if(head2) 37 res->next=head2; 38 39 return node.next; 40 } 41 };
针对方法二:比较难想的是,两两合并时,链表的选取。
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 ListNode *mergeKLists(vector<ListNode *> &lists) 12 { 13 int len=lists.size(); 14 if(len==0) 15 return NULL; 16 17 while(len>1) 18 { 19 int k=(len+1)>>1; 20 21 for(int i=0;i<len/2;++i) 22 { 23 lists[i]=mergeTwoLists(lists[i],lists[i+k]); 24 } 25 len=k; 26 } 27 return lists[0]; 28 } 29 30 ListNode *mergeTwoLists(ListNode *head1,ListNode *head2) 31 { 32 ListNode *nList=new ListNode(-1); 33 nList->next=head1; 34 ListNode *pre=nList; 35 36 while(head1&&head2) 37 { 38 if(head1->val >head2->val) 39 { 40 pre->next=head2; 41 head2=head2->next; 42 } 43 else 44 { 45 pre->next=head1; 46 head1=head1->next; 47 } 48 pre=pre->next; 49 } 50 51 if(head1) 52 { 53 pre->next=head1; 54 } 55 else 56 pre->next=head2; 57 58 return nList->next; 59 } 60 };
还有一种方法是最小堆的方法,维护一个大小为k的最小堆,初始化堆中的元素为每个链表的表头,它们会自动排好序,然后取出其中的最小元素介入新的链表中,然后,将最小元素的后继压入堆中,下次再从堆中选取最小的元素。元素加入堆中的复杂度为O(longk),总共有kn个元素加入堆中,因此,复杂度也和算法2一样是O(nklogk)。具体代码如下:
1 class Solution 2 { 3 private: 4 struct cmp 5 { 6 bool operator()(const ListNode *a,const ListNode *b) 7 { 8 return a->val >b->val; 9 } 10 } 11 public: 12 //用向量存储链表 13 ListNode *mergeKLists(vector<ListNode *> &lists) 14 { 15 int n=lists.size(); 16 if (n==0) return NULL; 17 18 ListNode node(0); 19 ListNode *res=&node; 20 21 priority_queue<ListNode*,vector<ListNode*>,cmp> que; 22 23 for(int i=0;i<n;i++) 24 { 25 if(lists[i]) 26 que.push(list[i]); 27 } 28 29 while(! que.empty()) 30 { 31 ListNode *p=que.top(); 32 que.pop(); 33 res->next=p; 34 res=p; 35 36 if(p->next) 37 que.push(p->next); 38 } 39 40 return node.next; 41 } 42 }