将k个有序链表合并成一个


链接: https://leetcode-cn.com/problems/merge-k-sorted-lists/.

归并法

今天只做了这一个算法题,做到我快自闭了~先是理解归并的思想,接着在编好程序后,出现指针指向随机内存的问题;好不容易解决了指针的问题,多次提交,还总是出现超时的问题。反复检查代码,居然是在merge时候,左半边的list总是从0号链表开始merge的,我要炸了!果然还是太菜了。
写下在编程中遇到的问题,以及苦苦探寻解决办法的过程,希望能帮助到大家。

合并两个有序链表

首先,我们考虑只合并2个有序链表的合并的情况:

  1. 定义一个新的头结点pHead ,用于保存合并后的新链表的头部;
  2. 定义一个变量p,用于指向下一个插入位置的前一个位置()pa,pb分别指两个链表待合并的剩下部分的第一个结点
  3. 当pa,pb不为空的时候,判断它们val值的大小进行合并;如果pa为空了,则将剩下的pb链表直接合并到新链表的后面;如果pb为空,则将剩下的pa链表直接合并到新链表后面;
  4. 在合并的过程中,将要合并的结点添加到p->nextt,然后改变p的指向,p=p->next;
    代码:
	//合并两个有序链表 
		ListNode* mergeTwoList(ListNode* a,ListNode* b){
			if((!a)||(!b)) return a?a:b;
			ListNode pHead,*p=&pHead,*pa=a,*pb=b;
			//ListNode* pHead,*p=pHead;这样定义的两个变量都是一个指针,它们指向一个随机的地址,
			//如果事先不给一个确切的ListNode的地址给他们俩,
			//直接运行程序,可能会造成它们俩指向的内存被破坏,导致程序终止运行
			//所以应该要先定义一个ListNode pHead的类型的变量,系统给这个变量分配了内存,然后再定义*p指向这个内存
			while(pa&&pb){
				if(pa->val<pb->val){
					p->next=pa;
					pa=pa->next;
				}
				else{
					p->next=pb;
					pb=pb->next;
				}
				p=p->next;
			}
			p->next=(pa?pa:pb);
			return pHead.next; 
			
		}

在第一次编程的时候,写成了ListNode* pHead,*p=pHead;,导致程序运行时停止了。仔细分析代码,发现这样定义个是两个指针变量,我并没有给他们一个准确内存给他们去指向,它们现在是随机指向了一个内存(这个内存有时是不允许直接访问的),导致在后面做p->next=pa或p->next=pb的时候,去改变一个不知道的内存的值,可能就会导致程序终止。因此,我后面改成了ListNode pHead,*p=&pHead,先定义了一个ListNode pHead的类型的变量,系统给这个变量分配了内存,然后再定义*p指向这个内存

合并k个有序链表

了解合并2个有序链表的过程后,再来理解如何用分治的方法合并k个有序链表,假设每个链表的最长长度为n:

  1. .将k个链表两两配对,并将配对的链表进行合并;
  2. 经过第一轮后,k个的链表合并成了 k 2 \frac{k}{2} 2k ,平均长度 2 k k \frac{2k}{k} k2k 为;然后再两两配对,合并成 k 4 \frac{k}{4} 4k 个链表, k 8 \frac{k}{8} 8k个链表 等等;
  3. 重复上述过程,直到将k个链表合并成1个有序链表。
    代码:
//分治法:每次将链表集合分成两半,分别对两半进行合并,两半都合并好后,再对整体进行合并
   	ListNode* merge(vector<ListNode*> &list,int left,int right){
   		if(left==right) return list[left];
   		if(left>right) return NULL;
   		int mid=(left+right)>>1;//注意这里不能写成list.size()/2,因为list的size是固定的,而我们每次分而治之,数量都在减半,因此要用最新的left和right的中值,从而实现分而治之 

   		return mergeTwoList(merge(list,left,mid),merge(list,mid+1,right));//mergeTwoList(merge(list,0,mid),merge(list,mid+1,right));不能从0开始merge,而应该从left 
   		
   	} 
   	ListNode* mergeKLists(vector<ListNode*> &lists){
   		return merge(lists,0,lists.size()-1);
   	}

在编程过程中,总会遇到这样那样的问题。在取中间值mid的时候,一开始写成了mid=list.size()/2,实际上这里list传过来的是一个引用,和最开始的list是同一个东西,所以后面不管递归多少层,list的大小都是最原始的那个list的大小,即size为k。而我们每次分而治之,数量都在减半,因此要用最新的left和right的中值,从而实现分而治之。同理,merge(list,mid+1,right));不能从0开始merge,而应该从left。
编程中出现的问题,一般都是对算法思想理解不够透彻,所以…加油吧~
为了便于测试,我把创建链表的代码也贴上来了:
代码:

//链表结构
struct ListNode{
	int val;
	ListNode* next;
	ListNode():val(0),next(NULL){
	}
	ListNode(int x):val(x),next(NULL){
	}
};
//创建链表
		ListNode* createList(int n){//n为链表中结点的数量
			int x;
			ListNode* pHead=new ListNode(0);
			ListNode* pre=pHead;
			for(int i=0;i<n;i++){
				cin>>x;//依次输入链表结点
				ListNode* p=new ListNode(x);
				pre->next=p;
				pre=p;
			}
			cout<<pHead<<endl;
			cout<<pHead->val<<endl;
			return pHead->next;
		} 
		//输出链表
		void print(ListNode* L){
			ListNode* p=L;
			while(p){
				cout<<p->val<<"->";
				p=p->next;
			}
			cout<<endl;
		} 

以上总结完毕。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值