Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
自己比较菜,分治法不太会,思想和归并算法差不多,归根结底还是不太熟悉递归,后来想明白了,便觉得编程还是蛮有意思的;
最开始想的是投机取巧的方法,先将所有链表的所有节点都保存到vector里,然后从小到大排序再新建一个包含所有节点值的链表返回;但面试官估计不喜欢这样。代码如下:
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
vector <int> ans;
if (!lists.size()) return NULL;
int temp;
ListNode * p , *q ,*head =NULL;
for (int i = 0; i < lists.size(); ++i)
{
p = lists[i];
while (p != NULL)
{
temp = p->val;
ans.push_back(temp);
p = p->next;
}
}
sort(ans.begin(), ans.end());
for (int i = 0; i < ans.size();++i )
{
p = new ListNode(ans[i]);
if (head != NULL)
{
head = p;
}
else
{
q->next = p;
}
q = p;
}
return head;
}
};
第二种是面试官比较喜欢的算法吧,分治算法,和归并排序的思想一样,拆分为有序的子序列然后在一一合并。归并排序算法如下:
#include <iostream>
using namespace std;
void mergeArray(int a[] , int left , int mid , int right , int newOrder [])
{ //newOrder 起到保存中间结果的作用,合并完再将结果返回给a 对应位置
int i = left, j = mid + 1 ;
int k = 0;
while (i <= mid && j <= right)
{
if (a[i] < a[j])
newOrder[k++] = a[i++];
else
newOrder[k++] = a[j++];
}
while (i <= mid)
newOrder[k++] = a[i++];
while (j <= right)
newOrder[k++] = a[j++];
for (int c = 0; c < k; c++)
{
a[left + c] = newOrder[c];
}
}
void splitArray(int a[], int left, int right, int neworder[])
{
int mid = (left + right) / 2;
if (left < right)
{
splitArray(a, left, mid, neworder);
splitArray(a, mid + 1, right, neworder);
mergeArray(a, left, mid, right, neworder);
}
}
int main()
{
int num[8] = { 1, 5, 2, 33, 44, 10, 7, 3 };
int n = 8;
int* newOrder = new int[n];
splitArray(num, 0, n - 1, newOrder);
delete[] newOrder;
for (int i = 0; i < 8; ++i)
{
cout << num[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
最后到了归并的方法,思路复制别人的,感觉下次遇到类似题目能自己写了,深入理解递归后,妈妈再也不用担心我的编程了;这是是一个比较经典的O(nlogn)的排序算法,还是比较重要的。思路是先分成两个子任务,然后递归求子任务,最后回溯回来。这个题目也是这样,先把k个list分成两半,然后继续划分,知道剩下两个list就合并起
来,合并时会用到Merge Two Sorted Lists这道题,不熟悉的朋友可以复习一下。来分析一下上述算法的时间复杂度。假设总共有k个list,每个list的最大长度是n,那么运行时间满足递推式T(k) = 2T(k/2)+O(n*k)。根据主定理,可以算出算法的总复杂度是O(nklogk)。空间复杂度的话是递归栈的大小O(logk)。代码如下:
/**
* 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) {
vector <int> ans;
if (!lists.size()) return NULL;
return splitKLists(lists, 0, lists.size() - 1);
}
private :
ListNode* splitKLists(vector<ListNode*>& lists , int l , int r ) //二分拆开成两个链表
{
if (l < r)
{
int mid = (l + r) / 2;
ListNode * k1 = splitKLists(lists, l, mid);
ListNode * k2 = splitKLists(lists, mid + 1, r);
return merge(k1, k2);
}
return lists[l]; //只有一个链表,则直接返回,此时l=r
}
ListNode * merge(ListNode * l1 , ListNode * l2 ) //合并两个链表,结果保存在第一个链表l1中
{
ListNode * dummy = new ListNode(0);
dummy->next = l1;
ListNode * cur = dummy;
while (l1 != NULL && l2 != NULL)
{
if (l1->val < l2->val)
{
l1 = l1->next;
}
else
{
ListNode * next = l2->next;
cur->next = l2;
l2->next = l1;
l2 = next;
}
cur = cur->next;
}
if (l2 != NULL)
cur->next = l2;
return dummy->next;
}
};
完!