目的:
在初次接触顺序储存和链式储存的概念后,尝试掌握函数增、删、查、改的操作原理和实现,并编写函数来解决相应问题以加深理解。
课题:
对任意输入的两个按值非递减有序的整数序列,写一程序将他们归并成一个按值非递减有序数列。请分别用顺序储存和链式储存结构表示整数数列,并相应完成归并。
举例:
输入:
“intput.txt”:
5
1 4 8 10 29
7
2 4 20 35 50 60 86
3
38 45 100
4
38 50 100 120
-1
输出:
“output.txt”:
case 1:12
1 2 4 4 8 10 20 29 35 50 60 86
case 2:7
38 38 45 50 100 100 120
代码实现:
用顺序储存实现:
#include <stdio.h>
#include <stdlib.h>
#define ERROR -1 //异常退出标识
typedef struct { //为线性表创建结构体
int *date;//用数组存放线性表的值
int length;//线性表的长度
} SqList;
void InitLIst_Sq(SqList &L, int date_number);//初始化表长为date_number的线性表L
int Sort_settings(const void *a, const void *b);//快速排序的排序方式设置
void MergeAndWriteList_Sq(SqList &List_1, SqList &List_2, int case_number);//归并List_1和List_2且写入文档
在这里插入代码片
int main() {
int date_number;//临时存储读入的线性表数据数量
int case_number = 0;//记录这是第几次case
//获取输入文件指针并以只读方式打开
FILE *fp = NULL;
fp = fopen("input.txt", "r");
fscanf(fp, "%d", &date_number);//获取第一组线性表数据的数量
while (date_number > 0) {//如果date_number是负数则结束循环
case_number++;//case递增
//初始化线性表List_1并用for循环依次逐个从输入文件中获取线性表数组元素
SqList List_1;
InitLIst_Sq(List_1, date_number);
for (int i = 0; i < date_number; ++i) {
fscanf(fp, "%d", &List_1.date[i]);
List_1.length++;
}
fscanf(fp, "%d", &date_number);//获取第二组线性表数据的数量
//初始化线性表List_2并用for循环依次逐个从输入文件中获取线性表数组元素
SqList List_2;
InitLIst_Sq(List_2, date_number);
for (int i = 0; i < date_number; ++i) {
fscanf(fp, "%d", &List_2.date[i]);
List_2.length++;
}
//调用函数归并List_1和List_2,并输出到“output.txt”
MergeAndWriteList_Sq(List_1, List_2, case_number);
fscanf(fp, "%d", &date_number);//获取下一对数据的第一组线性表数据的数量
}
fclose(fp);//关闭文件通道
}
void InitLIst_Sq(SqList &L, int date_number) {//初始化表长为date_number的线性表L
L.date = (int*)malloc(date_number * sizeof(int));//为线性表数组分配date_number大小的空间
if (!L.date) exit(ERROR);//如果输入表为空,则返回异常退出
L.length = 0;
}
int Sort_settings(const void *a, const void *b) { //快速排序的排序方式设置
return *(int*)a - *(int*)b; //由小到大排序
//return *(int *)b - *(int *)a; 这是由大到小排序的设定
}
void MergeAndWriteList_Sq(SqList &List_1, SqList &List_2, int case_number) {//归并List_1和List_2且写入文档
//为归并后的数据初始化一个线性表,长度为List_1,和List_2表长之和
SqList List_total;
InitLIst_Sq(List_total, List_1.length + List_2.length);
List_total.length = List_1.length + List_2.length;
//依次将两表放入归并线性表
for (int i = 0; i < List_1.length; ++i)
List_total.date[i] = List_1.date[i];
for (int i = List_1.length; i < List_total.length; ++i)
List_total.date[i] = List_2.date[i - List_1.length];
//用快速排序将归并表从小到大排序
qsort(List_total.date, List_total.length, sizeof(int), Sort_settings);
//获取输入文件指针并以读写的方式打开
FILE *fp = NULL;
fp = fopen("output.txt", "a");
//依次将归并表按照格式输出到“output.txt”
//fseek(fp, 0, SEEK_END)用于移动文件指针至最后,否则是文件顶部追加输入,会呈现逆情况输出(就是case3、case2、case1)
fseek(fp, 0, SEEK_END);
fprintf(fp, "case %d:%d\n", case_number, List_total.length);
for (int i = 0; i < List_total.length; ++i)
{
fprintf(fp, "%d ", List_total.date[i]);
}
fprintf(fp, "\n");
fclose(fp);//关闭文件通道
}
用链式储存实现:
#include <stdio.h>
#include <stdlib.h>
#define ERROR -1 //异常退出标识
//创建链表节点
typedef struct LNode {
int date;//存放数据的数组
struct LNode *next;//指针域
} LNode, *LinkList;
void init_LNode(LNode *pnode, int date);//初始化节点,pnode为链表头指针,date为输入数据
int ListInsert_Byhead(LinkList head, int input);//头插法插入有序数列,head为链表头指针,input为输入数据
int ListInsert_Bytail(LinkList head, int input);//尾插法插入有序数列,head为链表头指针,input为输入数据
void MergeAndWriteList_Nd(LinkList List_1, LinkList List_2, int case_number);将List_2降序整合至list_1中并写入文档,其中case_number用于输出这是第几次归并
int main() {//将List_2降序整合至list_1中并写入文档,其中case_number用于输出这是第几次归并
int date_number;//临时存储读入的线性表数据数量
int case_number = 0;//记录这是第几次case
int now_date;//用于临时储存从输入文件获取的值
//获取输入文件指针并以只读方式打开
FILE *fp = NULL;
fp = fopen("input.txt", "r");
fscanf(fp, "%d", &date_number);//获取第一组线性表数据的数量
while (date_number > 0) {//如果date_number是负数则结束循环
case_number++;//case递增
//初始化线性表List_1并用for循环依次逐个从输入文件中获取线性表数组元素
LinkList List_1;
List_1 = (LNode *)malloc(sizeof(LNode));
List_1->next = NULL;
//printf("2:%d\n", now_date);
for (int i = 0; i < date_number; ++i) {
fscanf(fp, "%d", &now_date);
ListInsert_Bytail(List_1, now_date);
//printf("1:%d\n", now_date);
}
fscanf(fp, "%d", &date_number);//获取第二组线性表数据的数量
//初始化线性表List_2并用for循环依次逐个从输入文件中获取线性表数组元素
LinkList List_2;
List_2 = (LNode *)malloc(sizeof(LNode));
List_2->next = NULL;
for (int i = 0; i < date_number; ++i) {
fscanf(fp, "%d", &now_date);
ListInsert_Bytail(List_2, now_date);
//printf("2:%d\n", now_date);
}
//调用函数归并List_1和List_2,并输出到“output.txt”
MergeAndWriteList_Nd(List_1, List_2, case_number);
fscanf(fp, "%d", &date_number);//获取下一对数据的第一组线性表数据的数量
}
fclose(fp);//关闭文件通道
}
//初始化节点,pnode为链表头指针,date为输入数据
void init_LNode(LNode *pnode, int date) {
pnode = (LNode *)malloc(sizeof(LNode));
pnode->date = date;
pnode->next = NULL;
}
//遍历链表并输出,pHead为链表头指针
void traverse_list(LinkList pHead) {
LinkList p = pHead->next; //将头节点的指针给予临时节点p
while (NULL != p) { //若节点p的next不为空,移动至下一个节点并循环
printf("%d ", p->date);
p = p->next;
}
printf("\n");
return;
}
//头插法插入有序数列,head为链表头指针,input为输入数据
int ListInsert_Byhead(LinkList head, int input) {
LinkList s; //为插入数据初始化一个新节点s并为其申请空间
s = (LNode *)malloc(sizeof(LNode));
s->date = input; //将节点接入头节点之后的位置
s->next = head->next;
head->next = s;
}
//尾插法插入有序数列,head为链表头指针,input为输入数据
int ListInsert_Bytail(LinkList head, int input) {//将List_2降序整合至list_1中并写入文档,其中case_number用于输出这是第几次归并
LinkList s, rail; //为插入数据初始化一个新节点s并为其申请空间,并设置一个尾指针用于指明接入点
s = (LNode *)malloc(sizeof(LNode));
rail = head; //初始化尾指针并用一个while循环将指针移动至链表尾部并接入节点s
while (rail->next != NULL)rail = rail->next;
s->date = input;
s->next = NULL;
rail->next = s;
}
//归并链表List_1和List_2且写入文档,其中case_number用于输出这是第几次归并
void MergeAndWriteList_Nd(LinkList List_1, LinkList List_2, int case_number) {
//sum_date作为新List_1的数据计数器
//p1,p2是用于读取List_1,List_2的临时指针,pre1作为p1的前//将List_2降序整合至list_1中并写入文档,其中case_number用于输出这是第几次归并驱指针
int sum_date = 0;
LinkList p1, p2, pre1;
pre1 = List_1, p1 = List_1->next, p2 = List_2->next;
//在这个while循环中,遍历List_2并依次取值和List_1作比较,以降序插入至List_1
while (p2) {
//判断:若p1为NULL,则将剩下的p2都接在p1的位置
if (p1 == NULL) {
pre1->next = p2;
//此while用于计数数据个数sum_date
while (p2) {
p2 = p2->next;//将List_2降序整合至list_1中并写入文档,其中case_number用于输出这是第几次归并
sum_date++;
}
break;
}
//判断:若p1的值大,就把p2的值接在p1前,否则p2移动至下一个节点
if (p1->date >= p2->date) {
LinkList temp;
temp = (LNode *)malloc(sizeof(LNode));
temp->date = p2->date;
pre1->next = temp;
temp->next = p1;
p2 = p2->next;
pre1 = pre1->next;
sum_date++;
} else {
pre1 = pre1->next;
p1 = p1->next;
sum_date++;
}
}
//获取输入文件指针并以读写的方式打开
FILE *fp = NULL;
fp = fopen("output.txt", "a");
//依次将归并表按照格式输出到“output.txt”
//fseek(fp, 0, SEEK_END)用于移动文件指针至最后,否则是文件顶部追加输入,会呈现逆情况输出(就是case3、case2、case1)
fseek(fp, 0, SEEK_END);
fprintf(fp, "case %d:%d\n", case_number, sum_date);
//将p1移动至list_1的首节点,然后遍历List_1并输出至文件
p1 = List_1->next;
while (p1) {
fprintf(fp, "%d ", p1->date);
p1 = p1->next;
}
fprintf(fp, "\n");//文件内换行
fclose(fp);//关闭文件通道
}
代码思路:
①用顺序储存(或链式储存)的思路设置结构体。
②编写接下来将会用到的增、删、查、改的函数。
③在主函数中编写文件读取输入文件的代码。以两次读取为一轮进行死循环,第一次读取为有序列数组数量,第二次读取为有序列数据并存入线性表。当第一次读取数为-1时跳出循环。
④每一轮死循环中我们都可以得到了两个有序列,新建一个总线性表将两个有序列按降序存入并输出。
本菜鸡写代码遗忘点:
①结构体的创建和运用
②结构体指针的初始化以及作为函数参数传入
③文件读取和写入操作
实验总结:
在撰写代码上,这次实验写的比较墨迹,尤其是在编写链式储存的那块代码上,首先是因为对链式储存的不熟悉,其次是上学期c语言指针没学好,导致这次在涉及指针的代码块上又取复习了一下C语言的指针。本来链式结构是按照顺序结构的思路已经写好了,后来老师提醒链表由于其特性是不用再创建一个新链表,才发现自己原来的代码没有体现链表的优点,反而徒增占用空间。于是又重新写了整合链表的函数部分,代码也精简了不少
算法上,这次我在顺序储存和链式储存的排序插入的步骤我用了不同的方法。前者用的是快排函数,后者是遍历有序表依次存入。在运算速度上,肯定是后者快。但前者的思路和代码真的是简单暴力,当时就偷了一个懒。前者的时间复杂度是O(nlogn),后者是O(n),速度上差得很大。