我们考虑如何将两个值域有序的单链表合并成一个新的有序单链表,使用原有的单链表的头结点作为新链表的头结点。
我们可以通过不断比较两个表的数据域的大小,先将较小者插入到以原有表的其中一个头结点构造的空表尾部,再比较再将较小者插入新表的尾部,一直执行下去。当遇到原有两个表其中一个表,现到达尾节点时,只需将没有到达尾节点的表剩余的节点全部插入到新表尾部即可。
下面我们代码实现(注意看代码注释)
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;
}