两个升序单向链表的合并

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

以上便是链表合并过程中的最初思路与代码优化的过程,写出来作为分享与学习过程的记录。晚安!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值