题目
将两个非递减的有序链表合并为一个非递减的有序链表。
(要求利用原来两个链表的存储空间,不另外占用其他空间,表中不允许有重复数据)
这是福宝第一次写数据结构的作业(只学了一个月),部分注释可能不准确,还烦请大家指出来!另外,初版代码写完后,我发现要实现无重复,最好在排序结束后就把单个链表中重复的结点删掉。否则分情况讨论复杂到了一定境界,真的让人崩溃。后面还会有逆序版的解答,大家也可以参考一下。
代码
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <ctime>
/*****自定义结构*****/
struct Node // 单链表的结点
{
int data; // 存放在结点中数据的类型,在本题中为整型
struct Node *next; // 存放后继结点的内存地址
};
typedef struct Node Node; // 【自己的理解】重命名struct Node为Node
typedef struct Node *LinkList; // 【自己的理解】重命名struct Node *为LinkList
/*****自定义结构*****/
/*****随机生成一个长度为l的单链表(未排序并且有头结点)*****/
LinkList List_TailInsert(int l, LinkList &L)
{
int k = 1;
int x = 0;
L = (LinkList)malloc(sizeof(Node)); // 创建头结点
LinkList s;
LinkList r = L; // r为表尾指针
// srand(time(0));//放在这一行会导致两组随机数差不多
while (k <= l)
{
x = rand() % 101;
s = (LinkList)malloc(sizeof(Node)); // 由系统生成一个Node型的结点,同时将该结点的起始位置赋值给指针变量s
s->data = x;
r->next = s;
r = s;
k = k + 1;
printf("%d ", x);
}
r->next = NULL;
printf("\n");
return L;
}
/*****随机生成一个长度为l的单链表(未排序并且有头结点)*****/
/*****对单链表进行冒泡排序(结合了输出功能)*****/
LinkList BubbleSort(LinkList &L)
{
LinkList pa, pb, pc;
int c = 0;
pa = L->next;
pb = L->next->next;
pc = L->next;
int j = 1;
/***求链表长度***/
int l = 0;
while (pc != NULL)
{
l = l + 1;
pc = pc->next;
}
/***求链表长度***/
while (j < l)
{
if (pa->data > pb->data)
{
c = pb->data;
pb->data = (int)pa->data;
pa->data = c;
j = 1;
}
else
{
j = j + 1;
}
pb = pb->next;
pa = pa->next;
if (pa->next == NULL && j != l)
{
j = 1;
pa = L->next;
pb = L->next->next;
}
}
pc = L->next;
while (pc != NULL)
{
printf("%d ", pc->data);
pc = pc->next;
}
printf("\n");
return L;
}
/*****对单链表进行冒泡排序(结合了输出功能)*****/
/*****核心功能函数1:排序后单链表删重函数optimize*****/
LinkList opt(LinkList &L)
{
LinkList p0, p1;
p0 = L->next;
p1 = p0->next;
while (p1 != NULL)
{
if (p0->data != p1->data)
{
p0 = p0->next;
p1 = p1->next;
}
else
{
p1 = p1->next;
p0->next = p1;
}
}
return L;
}
/*****核心功能函数1:排序后单链表删重函数optimize*****/
/*****核心功能函数2:归并链表*****/
LinkList MergeList(LinkList &La, LinkList &Lb) // 【自己的理解】LinkList &La就是La头结点的内存地址,也就是头指针
// 通常用头指针来标识一个单链表
{
LinkList pa, pb, pc, q; // LinkList是自定义的指针类型
pa = La->next; // 把单链表A头结点的指针【域】赋给pa
pb = Lb->next; // 把单链表B头结点的指针【域】赋给pb
pc = La; // 初始化pc,防止pc变成野指针,同时将pc作为链表合并后新的头节点
// 此时新头节点pc在La,pc作为pa的前驱节点链接pa
// pc作为pa的前驱节点,意义在于游标
while (pa != NULL && pb != NULL) // La和Lb任意一条链表为空时跳出while循环
{
if (pa->data < pb->data) // 如果pa的值小于pb,则pb不插入,pb不动,pa前进
{
pc->next = pa;
pc = pa;
pa = pa->next; // 插入pa以后,pa前进,剩下的结点和La看作整体进行比较
}
else if (pa->data > pb->data) // 若pa的值大于pb,则需要把pb插在pa的前边
{
pc->next = pb;
pc = pb;
pb = pb->next; // 插入pb以后,pb前进,剩下的结点和Lb看作整体进行比较
}
else // 如果pa的值小于pb,则pb不插入,pb前进,pa前进
{
pc->next = pa;
pc = pa;
pa = pa->next;
q = pb;
pb = pb->next;
free(q);
}
}
pc->next = pa ? pa : pb; // 插入剩余结点,由于之前删重了,所以不会有重复的元素
free(Lb);
return La;
}
/*****核心功能函数2:归并链表*****/
/*****主函数*****/
int main()
{
/*生成两个乱序的单链表,这里规定了第一个长度为12,第二个长度为8*/
LinkList La = NULL;
LinkList Lb = NULL;
srand(time(0));
printf(" 生成的第一个单链表:");
List_TailInsert(12, La);
printf(" 生成的第二个单链表:");
List_TailInsert(8, Lb);
/*对两个链表进行排序(结合了输出功能)*/
printf("排序后的第一个单链表:");
La = BubbleSort(La);
printf("排序后的第二个单链表:");
Lb = BubbleSort(Lb);
/*对排序后的单链表进行优化(删除单链表中的重复数据)*/
La = opt(La);
Lb = opt(Lb);
/*执行功能函数*/
La = MergeList(La, Lb);
/*输出符合题意的单链表*/
printf("按题意处理后的单链表:");
LinkList pp = La->next;
while (pp != NULL)
{
printf("%d ", pp->data);
pp = pp->next;
}
printf("\n");
system("pause"); // 防止运行后自动退出,需头文件【stdlib.h】
}
/*****主函数*****/
感想
功能函数初稿写完的时候,我以为已经很完善了。但在强迫自己写了生成随机链表和冒泡排序的代码,并经过了大量测试以后,我发现功能函数初稿实在有很多问题,比如重复的去不干净、死循环、临界点顺序错误、非法内存地址空间、结果漏结点……
经过了好几个小时的打磨之后,这份代码终于算是差强人意了!
建议大家解这道题的时候一定要写好生成随机链表和排序的函数,只有大量的测试才能击溃你的骄傲找到代码的问题。