两个有序无头节点单链表的合并

      现在正是春招的时候,我这个准毕业生也参加了各大公司的招聘活动,前段时间过了一家的线上笔试,有幸去到面试现场和面试官直接交流,结果虽然不尽如人意,可也算是为了今年春招作下准备工作。当时,面试官让我现场写一道编程题,题目是:现有两个已排序单链表,请设计算法将两个链表合并为一个已排序单链表。初看我觉得很简单,虽然数据结构已经有段时间没看了,可这种“基础题”也不至于完全不会啊!面试官让我先整理思路,想好了先和他说一遍解题思路再写代码,开始我向他确定了几个我认为比较有用的信息——单链表是否带头结点、二升序还是降序,得到的答案是——无头结点、升序降序都行。可是,在他给我的时间里,我整个大脑像是停止运转了一样,思绪非常的混乱,等到最后面试官都看不下去了,直接和我挑明“写不出代码也没关系,说一遍思路也行”,这句话对于当时的我来说就像一根救命稻草,我连忙接下这话,把还没有真正理顺的思路就这样讲了出来...现在想想,当时真的好怂啊,一直担心时间不够,最应该关心的题目反而都没静下心来好好解决。

      后来,我在《剑指offer》里面遇见了这道题,现在就好好讲讲这个问题吧~

      在这之前,我们先看一下“合并两个有序数组”这个问题。因为数组是一组连续存储的单元组成的,如果在合并时,只考虑在原来数组的基础上进行合并,并且紧接着数组之后的空间可用、充足的话,那么我们的合并方法就如下图所示:

两个有序无头节点单链表的合并_第1张图片

      上图是完整的合并过程,其中要注意两点: 1.  已排序新数组的长度一定要小于等于原数组的长度  2.拷贝顺序为从后往前,如采用从前往后的方式,则整个合并过程的时间复杂度为O(n∧2),并且可能发生内存重叠。

      合并两个单链表不像合并两个数组一样操作简单,因为链表的各个结点间不是连续存储的,而是通过指针的相互指向联系在一块的。那么该怎样合并呢?

      思路一:如果合并要求在原链表的基础上进行合并,不产生新的结点,那么在合并两个单链表的过程中,一定会破坏它们原有的结构。总的来说,需要注意以下几点:

      1. 确定新链表的头结点 ;

      2. 合并过程中,标识新链表的尾节点,方便插入下一个排序数据;

      3. 当链表之一遍历完,需将另一链表的数据继续插入到新链表中。

两个有序无头节点单链表的合并_第2张图片

具体实现代码如下:

HNode* MergeHList2(HList H1, HList H2)
{       //一定要先处理异常情况
	if(H1 == NULL)
	{
		return H2;
	}
	else if(H2 == NULL)
	{
		return H1;
	}
	else if(H1==NULL && H2==NULL)
	{
		return NULL;
	}

	HNode* phead = H1;//最终返回的头数据节点
	HNode* p = H1->next;//p循环的是主链表
	HNode* q = H2;//q循环的是另一链表
	if(p->data > q->data)
	{
		phead = H2;
		p = H2->next;
		q = H1;
	}

	HNode* s = phead;//记录新链表的最后一个节点,方便下一次接上新节点

	while(p!=NULL && q!=NULL)
	{
		if(p->data < q->data)
		{
			s->next = p;
			p = p->next;
			s = s->next;
		}
		else
		{
			s->next = q;
			q = q->next;
			s = s->next;
		}
	}

	if(p == NULL)//主链表先遍历完,说明另一链表还有数据得接过来
	{
		s->next = q;
	}

	if(q == NULL)//另一链表先遍历完,因为此时主链表的结构已经发生变化,所以仍需拼接过来
	{
		s->next = p;
	}

	return phead;
}

      思路二:可以发现,每一次插入到新链表的新结点都有一个共同特征——两个单链表头结点中值较小的那个头结点,这是一个典型的递归现象。 

      我们用指针p和q分别对H1和H2链表进行遍历,p和q分别指向H1和H2的头结点(一直都是),经过比较p->data和q->data,确定插入到新链表中的结点,如果是p,则phead=p、p=p->next,q同理。这时,p和q都指向了原链表的“新”头结点,再进行新一轮的比较,直到p或q中的一个或两个指向NULL,比较结束。

两个有序无头节点单链表的合并_第3张图片

HNode* MergeHList(HList H1, HList H2)
{
	if(H1==NULL)//处理情况异常
	{
		return H2;
	}
	else if(H2==NULL)
	{
		return H1;
	}
	else if(H1==NULL && H2==NULL)
	{
		return NULL;
	}

	HNode* phead = H1;
	HNode* p = phead->next;
	HNode* q = H2;
	if(H1->data > H2->data)
	{
		phead = H2;
		p = H1;
		q = phead->next;
	}

	phead->next = MergeHList(p,q);//每一次新链表的下一个都是 两链表中 小的那一个

	return phead;
}


你可能感兴趣的:(数据结构,C语言)