合并两个有序单链表

我们考虑如何将两个值域有序的单链表合并成一个新的有序单链表,使用原有的单链表的头结点作为新链表的头结点。

我们可以通过不断比较两个表的数据域的大小,先将较小者插入到以原有表的其中一个头结点构造的空表尾部,再比较再将较小者插入新表的尾部,一直执行下去。当遇到原有两个表其中一个表,现到达尾节点时,只需将没有到达尾节点的表剩余的节点全部插入到新表尾部即可。

下面我们代码实现(注意看代码注释)

void combine_linklist(LinkList Ll1, LinkList Ll2)
{   
    LinkList C; //指向空表C的头结点的指针(后续构造的新表)
    LinkList p = Ll1->pNext; //指针p始终指向表一中将要判断指针域大小的节点,初始指向表一头结点的后驱节点
    LinkList q = Ll2->pNext; //指针q始终指向表二中将要判断指针域大小的节点,初始指向表二头结点的后驱节点
    LinkList r; //指针r始终指向空表C的尾节点,初始指向空表C的头结点
    LinkList s; //临时保存指针p或q,可以通过直接操作s,来操作未进行后移的指针p或q

    
    //借助第一个链表的头结点构造一个新的空表,该空表用来存储新合并的有序链表
    C = Ll1;    
    C->pNext = NULL;
    
    //指针r,始终指向空表C的尾节点。(初始指向空表C的头结点)
    r = C;

    //释放表二的头结点
    free(Ll2);  
    
    //当p和q都不为空的时候执行循环,当其中有一个为空时,只需将不为空所在的表剩余的节点全部插入到表C尾部即可
    while (p && q)  
    {   
        //整个if语句是用来找到,表一和表二中将要比较数据域的两个节点中较小的节点,并将指向该节点的指针用s临时保存
        //如果表一的数据域小于表二的数据域
        if (p->data < q->data)
        {
            //先将指针p保存到指针s, 因为后续p要后移,以保证可以通过直接操作s,来操作未进行后移的指针p
            s = p;

            //p后移,以保证始终指向表一中将要判断指针域大小的节点
            p = p->pNext;  
        }
        //如果表二的数据域小于或等于表一的数据域
        else 
        {   
            //先将指针q保存到指针s, 因为后续q要后移,以保证可以通过直接操作s,来操作未进行后移的指针q
            s = q;

            //q后移,以保证始终指向表二中将要判断指针域大小的节点
            q = q->pNext;
        }  
        
        //将找到的较小者,通过操作临时保存过指向该较小者的指针s,来插入表C的尾部
        s->pNext = r->pNext; //相当于将插入表C尾部的节点的指针域置空 
        r->pNext = s; //将表C的尾节点的指针域指向要插入的节点(执行插入操作)
        r = r->pNext; //r后移,以保证始终指向表C的尾节点
    } 
    
    //判断循环是因为p空而结束,还是因为q空而结束
    if (p)
        //如果是q空导致,则将表一剩余部分插入到C表的尾部  
        r->pNext = p;   
    else
        //如果p空导致,则将表二剩余部分插入到C表的尾部   
        r->pNext = q; 

    //结束函数
    return;
}

因为上述代码只是整个可以运行的代码中独立封装的一个函数,不太完整,下面给出完整函数(完整函数中去除了这个合并函数) 

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>

typedef struct Node
{
    int data; //数据域
    struct Node * pNext; //指针域
}LNode, * LinkList;

//函数声明
LinkList create_linklist(); //创建一个链表
void combine_linklist(LinkList, LinkList); //合并两个有序表
void show_linklist(LinkList); //输出链表数据域


int main(void)
{
    LinkList Ll1 = NULL; //定义一个指向空的节点指针
    LinkList Ll2 = NULL; //定义一个指向空的节点指针
    
    Ll1 = create_linklist(); //创建第一个单链表
    Ll2 = create_linklist(); //创建第二个单链表

    show_linklist(Ll1); //输出表一
    show_linklist(Ll2); //输出表二

    combine_linklist(Ll1, Ll2); //合并表一表二

    show_linklist(Ll1); //输出合并的新表

    return 0;
}

LinkList create_linklist()
{
    int len; //用来存储节点的个数
    int i; //for循环中的循环变量
    int val; //用来存放节点的值域


    //为头结点分配内存
    LinkList Ll = (LinkList)malloc(sizeof(LNode));
    Ll->pNext = NULL;
    
    //判断是否分配成功
    if(!Ll)
    {
        printf("动态内存分配失败,程序退出!\n");
        exit(-1);
    }

    //请求用户输入节点个数
    printf("请输入节点的个数:\n");
    scanf("%d", &len);

    //定义一个始终指向当前链表尾节点的指针,初始指向头结点
    LinkList pTail = Ll;
    pTail->pNext = NULL;

    for (i = 0; i < len; i++)
    {
        //每循环一次生成一个新的节点
        LinkList LNew = (LinkList)malloc(sizeof(LNode));

        //判断是否分配成功
        if(!LNew)
        {
            printf("动态内存分配失败,程序退出!\n");
            exit(-1);
        }

        //请求用户输入新节点的值
        printf("请输入第%d个节点的值\n", i+1);
        scanf("%d", &val);

        //将用户输入的值存储到数据域
        LNew->data = val;

        //将原始链表的最后一个节点挂在新节点上
        pTail->pNext = LNew;

        //新节点变成当前的尾节点,需要将其指针域变为空
        LNew->pNext = NULL;

        //pTail后移,使其指向当前的尾节点
        pTail = LNew;
    }

    //提示用户链表创建成功
    printf("链表创建成功!\n");

    //返回指向头结点的指针
    return Ll;   
}

void show_linklist(LinkList Ll)
{
    LinkList p = Ll->pNext;

    while(p != NULL)
    {
        printf("%d ", p->data);
        p = p->pNext;
    }

    printf("\n");

    return;
}

上一篇:数据结构第一次上机题


下一篇:单链表的操作