23. Merge k Sorted Lists

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;
	}
};
完!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值