有序表归并:用顺序储存和链式储存分别实现

目的:

在初次接触顺序储存和链式储存的概念后,尝试掌握函数增、删、查、改的操作原理和实现,并编写函数来解决相应问题以加深理解。

课题:

对任意输入的两个按值非递减有序的整数序列,写一程序将他们归并成一个按值非递减有序数列。请分别用顺序储存和链式储存结构表示整数数列,并相应完成归并。

举例:

输入:

“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),速度上差得很大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值