作業1 of 計導課程設計(俗稱“大作業”)
實驗題如下(共3道題):
第1題 .實驗手冊中實驗11的第4題(連結串列的建立、查詢、釋放)
第2題 .實驗手冊中實驗11的第10題(連結串列排序)
第3題 .實驗手冊中實驗11的第11題或者第13題選一題。
——————————————我是分割線——————————————————
—————————WARNING————————————
下面是本人的作業程式碼,僅供參考,請勿抄襲。
(小心查重)……
學術誠信 >> A+/A !!!
——————————————我是分割線——————————————————
第1題 .實驗手冊中實驗11的第4題(連結串列的建立、查詢、釋放)
//第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;//初始化指標,使用指標時,一定要防止危險:warning:的野指標; //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題(連結串列排序)
第一次嘗試:
// 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(){ ListNode *headPtr=NULL;//相當於建立一個空表(只有頭指標,沒有尾指標); headPtr=input_datas(headPtr);//處理輸入資訊,假設處理的輸入資訊都是以「標準輸入」實現的; show_one_list(headPtr); // If you wanna take a look at it…… o(^_^)o …… ListNode *outputList_head=NULL; outputList_head=SelectSort(&headPtr); //排序,並將排序結果放入新的連結串列中,我們稱他為"輸出 連結串列/列表"; show_one_list(outputList_head); //輸出排序所得輸出連結串列; return 0; }