原版:
第一次实验(本学期第一次作业)
实验目的
1。链表的使用(链表的创建,遍历,释放,链表结点的插入和删除)
2。看来是代码风格(括号,空行,缩进……)和注释习惯的养成了……
实验题如下(共3道题):
第1题 .实验手册中实验11的第4题(链表的建立、查找、释放)
第2题 .实验手册中实验11的第10题(链表排序)
第3题 .实验手册中实验11的第11题或者第13题选一题。
代码风格要求:
注释:
变量定义处对每个变量的用途(即用于保存什么数据)进行说明;
对代码段的功能要有注释;
空行分隔:
适当使用空行来分隔实现不同功能的代码段,将不同功能的代码段进行分割,提高代码的可读性;
缩进:选择、循环语句要有缩进,提高代码的可读性;
——————————————我是分割线——————————————————
—————————WARNING————————————
下面是本人的作业代码,仅供参考,请勿抄袭。
(小心查重)……
学术诚信 >> A+/A !!!
——————————————我是分割线——————————————————
第1题 .实验手册中实验11的第4题(链表的建立、查找、释放)
涉及的算法:
链表的创建与遍历
我的代码:
以下代码,均欢迎hack ~~
//第1题 .实验手册中实验11的第4题(链表的建立、查找、释放)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define MIN_OF_LONG -2147483648
#define MAX_OF_LONG 2147483647
//习惯于使用双空行进行分割~~
struct node{//创建结构;
long data;//考虑到数据类型,使用long较为合适;
node* nextPtr;
};
typedef node LISTNODE;
void create_one_node(node** headPtr,node** ptr,node** lastPtr,long num){
//创建一个单向链表,同时处理输入;
//还是喜欢传引用~~
if ((*headPtr)==NULL){//如果头指针指向NULL类型,说明之前是空表;
(*ptr)=(node*)malloc(sizeof(node));//动态分配内存;
if ((*ptr)==NULL){//如果动态分配内存成功,则*ptr!=NULL,
printf("malloc error!\n");//所以如果*ptr==NULL,说明动态分配内存失败,报告错误;
}
else{ //否则动态分配内存成功
(*headPtr)=(*ptr);//现在*ptr指向第一个node,将其赋给头指针,使得头指针指向该单向链表的头部;
(*lastPtr)=(*ptr);//现在,*ptr也是指向最后一个元素,将*ptr赋给指向最后一个元素的指针;
(*ptr)->data=num;//将输入的数据赋给数据域data;
}
}
else{ //如果之前不是空表
(*ptr)=(node*)malloc(sizeof(node));//动态分配内存;
if ((*ptr)==NULL){ //如果动态分配内存成功,则*ptr!=NULL,
printf("malloc error!\n");//所以如果*ptr==NULL,说明动态分配内存失败,报告错误;
}
else{ //否则动态分配内存成功
(*lastPtr)->nextPtr=(*ptr);//将指向最后一个node的*ptr赋给之前最后一个元素的指针域;
(*ptr)->data=num;//将输入的数据赋给数据域data;
(*lastPtr)=(*ptr);//现在,*ptr也指向最后一个元素,将*ptr赋给指向最后一个元素的指针;
}
}
}
void max_min_sum_of_list_and_Free(node* headPtr,node* ptr,node* lastPtr,long* max,long *min,long *sum){
ptr=headPtr;
while(ptr!=NULL){//只要指向的node不为空,说明这个节点储存了数据,那么就要进行处理;
if (ptr->data>*max){//如果有数据比max大,更新max;
*max=ptr->data;
}
if (ptr->data<*min){//如果有数据比min小,更新min;
*min=ptr->data;
}
*sum+=ptr->data; //累加求和;
headPtr=ptr;
ptr=ptr->nextPtr; //令ptr指向下一个node;
free(headPtr);
}
}
int main(){
//1.声明变量~~
long num; //考虑到数据类型,使用long较为合适;~~虽然对于Mac而言,long long = long ,sizeof(long long)=sizeof(long)=8;~~
long max=MIN_OF_LONG,min=MAX_OF_LONG,sum=0;//最大值要初始化为long的下界,最小值要初始化为long的上界,sum初始化为0;
node* headPtr=NULL,*ptr=NULL,*lastPtr=NULL;//初始化指针,使用指针时,一定要防止危险⚠️的野指针;
//2.输入数据;
printf("Please input a series of integars:");
scanf("%ld",&num); //输入一个num;
while (num!=-1){//只要num!=-1,说明输入没有结束,就要————
create_one_node(&headPtr,&ptr,&lastPtr,num);//"create one node";
scanf("%ld",&num);//继续输入~~
}
printf("\n");
//3.处理数据,求得最大值max,最小值min,所有元素之和sum,并进行输出;
max_min_sum_of_list_and_Free(headPtr,ptr,lastPtr,&max,&min,&sum);
printf("The maximum, minmum, and the total are:%ld %ld %ld",max,min,sum);
//4.释放之前动态分配的内存;
//如果为了释放还要再次遍历电话,不如就在之前求最值与和的时候,就顺便完成free的任务,时间复杂度稍低一些;
return 0;
}
第2题 .实验手册中实验11的第10题(链表排序)
涉及的算法:
链表的排序
第一次尝试:
(请注意:仍有未解决的BUG!)
// first attempt
// 各种分析和思路都已经写在注释里了~~
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define STOP_SIGNAL -1
struct node{//建立一个结构,表示一个node———节点
long long data=1000000000000;//数据域;
node* nextPtr=NULL;//指针域;
};
typedef node NODE;//类型定义,设置别名;
void create_one_list(node** headPtr,node** ptr,node** lastPtr,long long num){
if ((*headPtr)==NULL){//如果头指针指向空类型,那么说明之前(或者说是"当前")为空表;
*ptr=(node*)malloc(sizeof(node));//动态分配内存;
if (*ptr==NULL){//如果创建失败;
printf("Malloc error!\n");//异常情况处理,给出错误报告……;
return;
}
else{
(*ptr)->data=num;
*headPtr=*ptr;//由于现在只有一个节点,所以头指针和尾指针都要指向 *ptr 所指向的那个节点,即第一个节点;
*lastPtr=*ptr;
}
}
else{
*ptr=(node*)malloc(sizeof(node));//动态分配内存;
if (*ptr==NULL){//如果创建失败;
printf("Malloc error!\n");//异常情况处理,给出错误报告……;
return;
}
else{
(*ptr)->data=num;//储存数字;
(*lastPtr)->nextPtr=*ptr;//之前的最后一个节点就是现在的倒数第二个节点,将其指针域指向当前的最后一个元素;
*lastPtr=*ptr;//更新尾指针(即指向最后一个节点的指针);
(**lastPtr).nextPtr=NULL;
}
}
}
void sort_list_up(node** headPtr,node** ptr,node** lastPtr){ //排序(要求是只能通过修改指针域来进行排序)
/*
*
* 先讲一下排序的思路,采用比较简单(虽然时间复杂度不是最优)的选择排序;
* 对于选择排序,如果是对结构数组或者是简单的数组进行处理的话,是非常容易的,
* 即————一开始数组是乱序的,用present储存乱序部分的第一个位置,运用线性查找,找到乱序序列的最小值,并和present位置的数进行交换……
* 但是在这里我们储存数据的数据结构是「单向链表」,而且只允许通过修改节点的指针域来进行排序;
* 所以就要有一些其他的方式来进行「交换」;
* 声明变量————指向节点的指针presentPtr和lastPtr,分别是 指向乱序部分之前的那个节点的指针,和指向当前最小节点的前驱的指针;
* 刚刚我们说过了这个排序算法的关键是在于 交换操作;
* 对于单向链表,我们不能只能通过结构或者其他一般数据结构的直接赋值法进行交换(即tem=x;x=y;y=tem);
* 要交换乱序部分的第一个节点和最小值对应的节点,关键是要交换二者的前驱和后继;
* 所以我们一开始采用了令presentPtr指向乱序部分之前那个节点的方法,即presentPtr指向的就是乱序部分第一个节点的前驱;
* 这样,将presentPtr和minPtr进行交换就完成了前驱的交换,将二者的后继交换,就完成了对应节点的交换(data的交换+后继的交换),
*
* 当然,要考虑特殊情况的处理,即presentPtr=*headPtr的情况,链表的第一个元素(头部元素)没有前驱;
* 就要进行特殊处理~~
*
*/
node *present_ptr=NULL,*minPtr=NULL,*temPtr=NULL;
//特殊情况的处理,即presentPtr=*headPtr的情况,链表的第一个元素(头部元素)没有前驱;
minPtr=temPtr=*headPtr;
while (temPtr->nextPtr->nextPtr != NULL || temPtr->nextPtr==temPtr->nextPtr->nextPtr){
if (temPtr->nextPtr->data < minPtr->nextPtr->data){
minPtr=temPtr;//这里的minPtr和temPtr都是指min和tem的前驱
}
temPtr=temPtr->nextPtr;
}
node *last1=*headPtr,*next1=(*headPtr)->nextPtr,*last2=minPtr->nextPtr,*next2=minPtr->nextPtr->nextPtr;
*headPtr=last2;
minPtr->nextPtr=last1;
last1->nextPtr=next2;
last2->nextPtr=next1;
//对下面 2-n 进行排序;
present_ptr=*headPtr;
while (present_ptr->nextPtr!=NULL && present_ptr->nextPtr != present_ptr->nextPtr->nextPtr){
minPtr=temPtr=present_ptr;//都是前驱;
while (temPtr->nextPtr != NULL && temPtr->nextPtr!=temPtr->nextPtr->nextPtr){
if (temPtr->nextPtr->data < minPtr->nextPtr->data){
minPtr=temPtr;
}
if (temPtr->nextPtr!=temPtr) {
temPtr=temPtr->nextPtr;
}
}
node *last1=present_ptr->nextPtr,*last2=minPtr->nextPtr,*next1=present_ptr->nextPtr->nextPtr,*next2=minPtr->nextPtr->nextPtr;
present_ptr->nextPtr=last2;
minPtr->nextPtr=last1;
last1->nextPtr=next2;
last2->nextPtr=next1;
if (present_ptr!=present_ptr->nextPtr) {
present_ptr=present_ptr->nextPtr;
}
}
}
void print_the_result(node **headPtr,node **ptr,node **lastPtr){ //输出排序结果:
printf("The new list is:");
*ptr=*headPtr;
printf("%lld",(**ptr).data);
*ptr=(*ptr)->nextPtr;
while (*ptr!=NULL){
printf(" %lld",(*ptr)->data);
if ((*ptr)->nextPtr == (*ptr)) {
break;
}
*ptr=(*ptr)->nextPtr;
}
printf("\n");
}
void free_memories_allocated(node **headPtr,node **ptr,node **lastPtr){ //释放所有节点占据的空间:
*ptr=*headPtr;
while (*ptr!=NULL){
*headPtr=(*ptr)->nextPtr;
free(*ptr);
if ((*ptr)->nextPtr == (*ptr)) {
break;
}
*ptr=*headPtr;
}
free(*ptr);
}
int main(){
node *headPtr=NULL,*ptr=NULL,*lastPtr=NULL;//声明变量并进行初始化;
long long num;//声明变量
printf("Please input a list(end by -1):");//输入数据,动态分配内存,建立单链表,进行存储;
scanf("%lld",&num);
while (num!=STOP_SIGNAL){
create_one_list(&headPtr,&ptr,&lastPtr,num);
scanf("%lld",&num);
}
sort_list_up(&headPtr,&ptr,&lastPtr);//排序(要求是只能通过修改指针域来进行排序)
print_the_result(&headPtr,&ptr,&lastPtr);//输出排序结果;
free_memories_allocated(&headPtr,&ptr,&lastPtr);//释放所有节点占据的空间;
return 0;
}
//input:
//Please input a list(end by -1):49 38 65 97 76 13 27 49 -1
更新版
(反正这个暂时没有发现任何BUG,可能就是最终版了吧)
(也不一定,没准过段时间学会了mergesort什么的呢)
// 好吧,还是不怎么会在单向链表里面进行swap操作……
// 下面是第二题的 新解法————看来只能使用经典方法进行操作了————维持一个输入列表和输出列表,进行选择排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INPUT_STOP_SIGNAL -1
struct ListNode{
long long data;
ListNode* next=NULL;
};
void malloc_error_report(void){//malloc错误报告;
printf("Memory allocation failed!\nPlease check it again to find the reason which may be too much data input!\n");
}
void other_error_report(void){//其他错误报告;
printf("There may be an error in the code. Please check it again and try to debug it!\nGood luck to you!\n");
}
void create_one_listNode(ListNode** headPtr,ListNode** presentPtr,long long num){//创建一个节点;(对头指针和尾指针采用传引用方式);
ListNode *ptr=NULL;//声明变量,用于进行新节点的申请;
if ((*headPtr)==NULL){//如果头指针指向空类型,那么,之前必是空表;
ptr=(ListNode*)malloc(sizeof(ListNode));//申请,并用临时指针ptr指向;
if (ptr==NULL){ //如果申请失败,打印错误报告;
malloc_error_report();
}
else{ //否则,即如果申请成功;
(*headPtr)=ptr; //将该指针指向位置赋值给 头指针;
(*headPtr)->data=num; //将数据存入 头部节点的数据域中;
(*presentPtr)=(*headPtr);//现在的List不在是empty List,所以将尾部指针指向最后一个节点(由于只有一个节点,所以也就是头节点);
}
}
else{
ptr=(ListNode*)malloc(sizeof(ListNode)); //申请,并用临时指针ptr指向;
if (ptr==NULL){ //如果申请失败,打印错误报告;
malloc_error_report();
}
else{ //否则,即如果申请成功;
(*presentPtr)->next=ptr; //将下一个节点的实际位置赋给presentPtr的指针域;
(*presentPtr)=(*presentPtr)->next; //将尾指针指向下一个节点;
(*presentPtr)->data=num; //将数据存入 最后一个节点的 数据域中;
}
}
}
ListNode* input_datas(ListNode* headPtr){//处理输入数据,并将其存入一个链表中;
ListNode* ptr=NULL,*presentPtr=NULL;
long long num;
scanf("%lld",&num);//逐个输入数据;
while (num != INPUT_STOP_SIGNAL){//如果输入数据不为-1,就创建一个新的节点,然后将数据储存进去;
create_one_listNode(&headPtr,&presentPtr,num);//创建一个新的节点,并储存数据;
scanf("%lld",&num);//读入下一个数据;
}
return headPtr;//返回链表的头指针;
}
void show_one_list(ListNode* headPtr){
ListNode *ptr=headPtr;
while(ptr != NULL){
printf("%lld ",ptr->data);
ptr=ptr->next;
}
printf("\n");
}
ListNode* SelectSort(ListNode** headPtr){
ListNode* outputList_head=NULL,*outputList_lastPtr=NULL,*ptr=NULL,*minPtr=NULL;
while ((*headPtr)->next != NULL){ //当头部节点的指针域为NULL时,整个链表中就只剩下了一个元素 ———— **headPtr;
long long num=0;
minPtr=ptr=*headPtr;
while (ptr->next != NULL) { // ptr 的后继为空,即ptr是指向最后一个节点的指针;
if (ptr->next->data < minPtr->next->data) { // 如果当前节点的数据域小于最小值节点的数据域;
minPtr = ptr; //将他的前驱赋给最小值节点的前驱;
}
ptr = ptr->next;// 指向当前节点的指针后移;
}
//由上面的循环可知,ptr如果是最后一个节点,那么就会跳出循环,也就是说,最后一个节点并没有参与比较,
// 但是很明显,最后一个节点作为倒数第二个节点的后继,参与了比较,由于这是单向链表,所以真正没有参与比较的,其实就只有头部节点,因为头部节点没有前驱;
if ((*headPtr)->data < minPtr->next->data){ //如果头部节点的数据比最小值还要小,
num=(*headPtr)->data; //将最小值赋给num,
(*headPtr)=(*headPtr)->next; //并将头指针指向之前头指针的后继,从而完成了对该节点的删除;
}
else {
num=minPtr->next->data;
minPtr->next=minPtr->next->next;
}
create_one_listNode(&outputList_head,&outputList_lastPtr,num);//新建一个节点,在输出列表中储存这个数字;
}
create_one_listNode(&outputList_head,&outputList_lastPtr,(*headPtr)->data);//新建一个节点,在输出列表中储存最后一个节点;
return outputList_head;//返回输出链表的头指针;
}
int main(){
printf("Please input a list(end by -1):");
ListNode *headPtr=NULL;//相当于创建一个空表(只有头指针,没有尾指针);
headPtr=input_datas(headPtr);//处理输入信息,假设处理的输入信息都是以「标准输入」实现的;
printf("The list you have just inputted is:");
show_one_list(headPtr); // If you wanna take a look at it…… o(^_^)o ……
ListNode *outputList_head=NULL;
outputList_head=SelectSort(&headPtr); //排序,并将排序结果放入新的链表中,我们称他为"输出 链表/列表";
printf("The new list is:");
show_one_list(outputList_head); //输出排序所得输出链表;
return 0;
}
第3题 .实验手册中实验11的第11题或者第13题选一题。
实验11的第11题
涉及的算法:
判断一个链表是否是另一个链表的子链表
我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INPUT_STOP_SIGNAL -1
struct ListNode{
long long data;
ListNode* next=NULL;
};
void malloc_error_report(void){//malloc错误报告;
printf("Memory allocation failed!\nPlease check it again to find the reason which may be too much data input!\n");
}
void create_one_listNode(ListNode** headPtr,ListNode** presentPtr,long long num){//创建一个节点;(对头指针和尾指针采用传引用方式);
ListNode *ptr=NULL;//声明变量,用于进行新节点的申请;
if ((*headPtr)==NULL){//如果头指针指向空类型,那么,之前必是空表;
ptr=(ListNode*)malloc(sizeof(ListNode));//申请,并用临时指针ptr指向;
if (ptr==NULL){ //如果申请失败,打印错误报告;
malloc_error_report();
}
else{ //否则,即如果申请成功;
(*headPtr)=ptr; //将该指针指向位置赋值给 头指针;
(*headPtr)->data=num; //将数据存入 头部节点的数据域中;
(*presentPtr)=(*headPtr);//现在的List不在是empty List,所以将尾部指针指向最后一个节点(由于只有一个节点,所以也就是头节点);
(*presentPtr)->next=NULL;
}
}
else{
ptr=(ListNode*)malloc(sizeof(ListNode)); //申请,并用临时指针ptr指向;
if (ptr==NULL){ //如果申请失败,打印错误报告;
malloc_error_report();
}
else{ //否则,即如果申请成功;
(*presentPtr)->next=ptr; //将下一个节点的实际位置赋给presentPtr的指针域;
(*presentPtr)=(*presentPtr)->next; //将尾指针指向下一个节点;
(*presentPtr)->data=num; //将数据存入 最后一个节点的 数据域中;
(*presentPtr)->next=NULL;
}
}
}
ListNode* input_datas(ListNode* headPtr){//处理输入数据,并将其存入一个链表中;
ListNode *presentPtr=NULL;
long long num;
scanf("%lld",&num);//逐个输入数据;
while (num != INPUT_STOP_SIGNAL){//如果输入数据不为-1,就创建一个新的节点,然后将数据储存进去;
create_one_listNode(&headPtr,&presentPtr,num);//创建一个新的节点,并储存数据;
scanf("%lld",&num);//读入下一个数据;
}
return headPtr;//返回链表的头指针;
}
void show_one_list(ListNode* headPtr){
ListNode *ptr=headPtr;
while(ptr != NULL){
printf("%lld ",ptr->data);
ptr=ptr->next;
}
printf("\n");
}
void judge_sub_seq(ListNode* head_of_listA,ListNode* head_of_listB){
//判断「序列B」是否是「序列A」的子序列;
//如果B是A的子序列,即A包含B,即从A的某个节点开始有一子列与B完全相同;
//使用匹配算法即可;
ListNode *ptrA=NULL,*ptrB=NULL;//申明变量以及初始化;
ptrA=head_of_listA;
ptrB=head_of_listB;
while (ptrB != NULL && ptrA != NULL){
if (ptrA->data != ptrB->data){ //因为B是子序列,所以只要 B第一个节点的数据 与 A的不同,
ptrA=ptrA->next; //就让指针ptrA"扫描"下一个节点;
}
else{ //B第一个节点的数据 与 A的相同,开始尝试匹配;
head_of_listA=ptrA; //将当前A的头指针存下;
head_of_listB=ptrB; //将当前B的头指针存下;
while (ptrB != NULL && ptrA != NULL && ptrA->data == ptrB->data ){ //从当前位置开始匹配;
ptrA=ptrA->next;
ptrB=ptrB->next;
}
if (ptrB == NULL){ //如果是因为ptrB==NULL终止的,那么说明匹配完全成功;
printf("ListB is the sub sequence of ListA.\n");
return;
}
else if (ptrA == NULL){
//如果是因为ptrA==NULL而终止,说明A已经无法继续匹配,但是B仍然有一段未匹配;
//请注意,这里我们之前先判断了ptrB==NULL?,
//即如果终止时A的最后一段和B匹配,那么也是属于匹配成功的;
//这种特殊情况如果出现,也会先在上一步时,已经输出"YES",而不会在这一步输出"NO";
printf("ListB is not the sub sequence of ListA.\n"); //匹配不可能成功,直接输出
return;
}
else{ //如果匹配失败,但是ptrA仍未指到A序列的末尾,说明A-B匹配仍然有希望~~
// ————"回溯"———— 将ptrA,ptrB退回之前位置,继续尝试匹配;
ptrA=head_of_listA->next;
//请注意:这里不能再让ptrA=head_of_listA
// 因为之前的 head_of_listA 和 head_of_listB 就是相等的,
// 如果还是这样的话,ptrA,ptrB回到了循环之前的状态,程序就会陷入死循环;
// 或者说,ptrA=head_of_listA,刚刚已经被证明是不匹配的了,
// 必须让ptrA往后移动一个节点,重新尝试匹配;
// 这注释好冗长,翻来覆去都在讲那个东西,希望能看懂……
ptrB=head_of_listB;
}
}
}
// 如果是匹配成功,
// 那么这个子程序 应该是通过前面的ptrB==NULL返回;
// 所以既然程序能够运行到这里,说明一定是没有成功匹配;
// 所以输出"NO",返回;
printf("ListB is not the sub sequence of ListA.\n");
return;
}
int main(){
//printf("Please input listA(end by -1):");//输入listA;
ListNode *head_of_listA=NULL;
head_of_listA=input_datas(head_of_listA);
//printf("The listA just inputted is:"); //输出刚刚输入的listA;
//show_one_list(head_of_listA);
//printf("Please input listB(end by -1):");//输入listB;
ListNode *head_of_listB=NULL;
head_of_listB=input_datas(head_of_listB);
//printf("The listB just inputted is:");//输出刚刚输入的listB;
//show_one_list(head_of_listB);
judge_sub_seq(head_of_listA,head_of_listB);
return 0;
}
实验11的第13题
涉及的算法:
链表中某两段节点的交换
我的代码:
请大家之后看这个吧:
https://www.jianshu.com/p/aa159c9300eb