您的位置:首页 > 其它

两个升序单向链表的合并

2016-09-14 00:20 197 查看

1、问题描述:

有两个非空升序排列的单向链表pHeadA和pHeadB,将其合并为一个升序链表pHead,并剔除其中的重复值。

2、问题分析:

(1)、两个升序链表合并成一个升序链表,其实就是原链表删除节点,但不释放内存,而是将要删除(移动)的节点根据数据域大小关系以及不重复原则,挂在新的头指针上,形成新的链表的过程。并且新的链表依然是升序,就要求我们正向建链合并。

(2)、数据域大小关系:在链表删除的过程中,我们从只需要比较两个链表头指针指向的节点数据域大小,将指针p指向数据域小的那个头结点上,并将该链表头指针移至下一个节点,然后将p指向的节点与原链表断开连接,与新链表建立连接就可以完成一个节点的移动过程,每次比较的都是头结点,重复以上步骤即可实现合并。

(3)、不重复原则:那么如果遇到重复元素该怎么办?首先我们会想到比较数据域值,若数据域值相等,必然重复,释放重复节点的内存即可。然而,两条链表中可能某一条中自身有重复值,可能两链表中互相有重复值,情况比较复杂。那么我们选择一种较为简单的方法:由于新链表的建立(旧链表的合并删除)由空链表尾插节点一步步建成,所以每次尾插一个新的节点,其前面的节点必然是升序有序且无重复值的。将p指针指向的即将要删除(尾插在新链表或释放内存)的节点与新链表的尾节点数据域相比较,如果数据域相等释放p指向节点内存,如果不相等尾插在新的链表尾节点之后,成为新的尾节点即可。

3、代码实现:

关于链表的建立可以参考我的链表操作法则之逆向遍历与倒置算法

将建链与遍历写入“link.h”文件中。

# include <stdio.h>
# include <stdlib.h>
# include "link.h"

PNODE MergeFour_Link(PNODE pHeadA, PNODE pHeadB);

int main (void)
{
PNODE pHead = NULL;
PNODE pHeadA = NULL;
PNODE pHeadB = NULL;

int a[18] = {1,1,1,3,6,11,15,22,37,57,61,85,111,112,112,113,113,113};
int b[12] = {1,1,2,4,6,11,15,32,37,44,68,85};

printf("链表A:\n");
pHeadA = FowardCreate_Link(a,18);//A链表创建
ForwardTraversal_Link(pHeadA);//遍历A链表
printf("链表B:\n");
pHeadB = FowardCreate_Link(b,12);//B链表创建
ForwardTraversal_Link(pHeadB);//遍历B链表
printf("合并后:\n");
pHead = MergeFour_Link(pHeadA,pHeadB);//合并两链表
ForwardTraversal_Link(pHead);//遍历合并后的链表

return 0;
}
/*传递A、B链表头指针,返回新的合并后的链表头指针*/
PNODE MergeFour_Link(PNODE pHeadA, PNODE pHeadB)
{
PNODE pHead = NULL, pTail = NULL, p = NULL;//pHead新的头指针,pTail始终指向尾节点,p指向要移动或释放的节点
while(pHeadA && pHeadB){
if(pHeadA->data < pHeadB->data){
p = pHeadA;
pHeadA = pHeadA->pNext;
}//如果A链表头结点数据域小于B链表头结点数据域,p指向A链表头结点,pHeadA后移
else{
p = pHeadB;
pHeadB = pHeadB->pNext;
}//如果A链表头结点数据域不小于B链表头结点节点数据域,p指向B链表头结点,pHeadB后移
if(NULL == pHead){
pTail = pHead = p;
}//如果新的链表为空,单独处理,否则,比较新链表尾节点数据域与要移动节点p数据域大小
else if(pTail->data == p->data){
free(p);
}//如果相等释放p
else{
pTail = pTail->pNext = p;
}//如果不相等尾插即可
}//退出while循环后,必定只有一个链表节点已经删除完毕
p = pHeadA = (pHeadB) ? pHeadB : pHeadA;
while(pHeadA){
pHeadA = pHeadA->pNext;
if(pTail->data == p->data){
free(p);
}
else{
pTail = pTail->pNext = p;
}
p = pHeadA;
}//对剩余的未删除完的那条链表进行处理,过程与上类似,仅仅少了A->data与B->data的比较
pTail->pNext = NULL;//最后一个节点可能会被删除导致pTail指针域不为空,故最后给pTail指针域赋空
return pHead;
}


4、代码优化:

3中代码由于有两个while循环,且代码有重复,则有必要对其代码进行整合,只需要一个while循环,将情况分为p指向pHeadA和p指向pHeadB两种,如下:

/*
进入while循环则pHeadA与pHeadB不可能都为空。故有四种情况:
1、pHeadA为空,pHeadB不为空,则p指向pHeadB;
2、pHeadA不为空,pHeadB不为空且pHeadA->data < pHeadB->data不成立,则p指向pHeadB;
3、pHeadA不为空,pHeadB为空,则p指向pHeadA;
4、pHeadA不为空,pHeadB不为空且pHeadA->data < pHeadB->data成立,则p指向pHeadA。
*/
PNODE MergeFive_Link(PNODE pHeadA, PNODE pHeadB)
{
PNODE pHead = NULL, pTail = NULL, p = NULL;

while(pHeadA || pHeadB)
{
if(!pHeadB || pHeadA && pHeadA->data < pHeadB->data)
{
p = pHeadA;
pHeadA = pHeadA->pNext;
}
else
{
p = pHeadB;
pHeadB = pHeadB->pNext;
}//先从原链表中删除节点(取出来)
if(NULL == pHead){
pTail = pHead = p;
}
else if(pTail->data == p->data){
free(p);
}
else{
pTail = pTail->pNext = p;
}
}
pTail->pNext = NULL;
return pHead;
}


以上便是链表合并过程中的最初思路与代码优化的过程,写出来作为分享与学习过程的记录。晚安!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  链表 合并
相关文章推荐