# 資料結構 HW2 筆記 > Write a C program to read 10 car plate number and its color. Sort and store the data in a Singly linked list in ascending way. Print out the data. >使用C語言寫一個能紀錄十筆車牌號碼與車輛顏色的程式。輸入資料後依升序插入至鏈結串列中,最後印出所有節點之資料。 > 5/22 更新: 對不起,我少考慮了插入時新節點的車號等於串列第一個節點的情況,假如是參考我的程式的話,請將情況二的判斷式從 "小於" 改為 "小於等於",筆記中皆已修正。 我想大家主要的問題在於節點插入的演算法,因此我僅對 **insert()** 函數做了額外說明,其餘部分請閱讀我寫的註解。 <details> <summary>完整程式碼(含註解)</summary> ```c= #include <stdio.h> #include <stdlib.h> struct snode{ int pla; // 車牌號碼 char st; // 車輛顏色 struct snode *nx; // 指向下個節點的指標 }; // 宣告一個指標變數 opt 指向串列的第一個節點,因為串列為空所以指向NULL // 建立 linked list 唯一需要的外部指標就只有這個指向第一個節點的 opt // 老師建立的那一堆指標僅是作為節點處理時用到的索引而已 struct snode *opt = NULL; void insert(int number, char color){ // 分配空間給新節點,並宣告一個指標變數 newNode 指向這個新節點 struct snode *newNode = (struct snode*)malloc(sizeof(struct snode)); // 將資料填入新節點 newNode->pla = number; newNode->st = color; // !!開始插入!! // 情況一:串列為空 if(opt == NULL){ // 插在串列最前面 newNode->nx = opt; opt = newNode; return; } // 情況二:新節點小於或等於串列的第一個節點 if(newNode->pla <= opt->pla){ // 插在串列最前面(同情況一) newNode->nx = opt; opt = newNode; return; } // 情況三:依序對串列中各節點比較大小,直到有節點大於新節點或已沒有節點可比較 struct snode *current = opt, *prev; while(newNode->pla > current->pla){ // current 會作為索引從第一個節點依序指向串列中各個節點進行比較 // prev 負責記錄 current 的前一個節點 prev = current; current = current->nx; // 若 current 等於 NULL 代表串列中的節點已經被比光了 if(current == NULL) break; } // 跳出 while 迴圈時 current 指向的節點必大於新節點或等於 NULL // 將新節點插入於 current 與 prev 所指的兩個節點中間 prev->nx = newNode; newNode->nx = current; return; } int main(){ int number; char color; printf("請輸入10組車牌號碼與其顏色對應的字元:\n"); for(int i=0; i<10; ++i){ scanf("%d %c", &number, &color); insert(number, color); } // 宣告一個指標變數 current 作為索引 // 讓它從第一個節點一路指到最後一個節點,便能印出所有節點的資料 struct snode *current = opt; while(current != NULL){ printf("車號: %4d, 顏色: %c\n", current->pla, current->st); current = current->nx; } return 0; } ``` </details> <br> <details> <summary>完整程式碼(無註解)</summary> ```c= #include <stdio.h> #include <stdlib.h> struct snode{ int pla; char st; struct snode *nx; }; struct snode *opt = NULL; void insert(int number, char color){ struct snode *newNode = (struct snode*)malloc(sizeof(struct snode)); newNode->pla = number; newNode->st = color; if(opt == NULL){ newNode->nx = opt; opt = newNode; return; } if(newNode->pla <= opt->pla){ newNode->nx = opt; opt = newNode; return; } struct snode *current = opt, *prev; while(newNode->pla > current->pla){ prev = current; current = current->nx; if(current == NULL) break; } prev->nx = newNode; newNode->nx = current; return; } int main(){ int number; char color; printf("請輸入10組車牌號碼與其顏色對應的字元:\n"); for(int i=0; i<10; ++i){ scanf("%d %c", &number, &color); insert(number, color); } struct snode *current = opt; while(current != NULL){ printf("車號: %4d, 顏色: %c\n", current->pla, current->st); current = current->nx; } return 0; } ``` </details> <br> <details> <summary>翁教授的程式碼</summary> ```c= #include <stdio.h> #include <stdlib.h> struct snode{ int pla; char st; struct snode *nx; }; struct snode *opt = 0, *pt, *p1, *p2; int insert(int x, char y){ struct snode *pp; pp = (struct snode*)malloc(sizeof(struct snode)); pp->pla = x; pp->st = y; if (opt == 0){// initial opt = pp; pp->nx = 0; printf(" init\n"); }else{// non-initial p1 = opt; p2 = p1; while(p1 != 0){ if(x > p1->pla){// next p2 = p1;// 跟上 p1 = p1->nx;// 向前一步 printf(" next "); }else{ if(p2 == p1){ pp->nx = opt; opt = pp; printf(" ih\n"); }else{// insert in the middle or end pp->nx = p2->nx; p2->nx = pp; printf(" im\n"); } break; } } if(p1 == 0){// insert in the end pp->nx = p2->nx; p2->nx = pp; printf(" ie\n"); } } } int main(){ int i, p, s; printf("Insert 5 plates and colors\n"); for(i=0; i<5; i++){ scanf("%d %c", &p, &s); printf("%d %c ", p, s); insert(p, s); } printf("\nThe sorted list is:\n"); p1 = opt; while(p1 != 0){ printf("%d %c\n", p1->pla, p1->st); p1 = p1->nx; } } ``` </details> > 要注意,除了結構、結構內與串列首指標的變數名稱,其餘變數我並未與教授的命名統一。 <details> <summary>inesert() 函數測試程式</summary> ```c= #include <stdio.h> #include <stdlib.h> #include <time.h> struct snode{ int pla; char st; struct snode *nx; } *opt = NULL, testArray[20]; // ----------------------------------- // 替換掉此處的 insert() 來檢測你寫的函數功能是否正常 // 每次執行會自動輸入 20 筆資料,也請不必擔心輸出的排版 void insert(int number, char color){ struct snode *newNode = (struct snode*)malloc(sizeof(struct snode)); newNode->pla = number; newNode->st = color; if(opt == NULL || newNode->pla <= opt->pla){ newNode->nx = opt; opt = newNode; } else{ struct snode *current = opt, *prev; while(current != NULL && newNode->pla > current->pla){ prev = current; current = current->nx; } prev->nx = newNode; newNode->nx = current; } } // ----------------------------------- void mkData(int quantity){ int number; char color; srand(time(NULL)); for(int i = 0; i < quantity; ++i){ number = rand() % 9000 + 1000; color = rand() % 26 + 65; testArray[i].pla = number; testArray[i].st = color; } } int main(){ int number, count = 0; char color; mkData(20); printf("Raw input: \n"); for(int i=0; i<20; ++i){ number = testArray[i].pla; color = testArray[i].st; printf("%4d %c ", number, color); insert(number, color); if(++count%5 == 0) printf("\n"); } printf("\nData of the list: \n"); struct snode *current = opt; for(; current != NULL; current = current->nx) printf("plaNum: %4d, color: %c\n", current->pla, current->st); return 0; } ``` </details> > 這個程式每次執行會自動輸入 20 筆隨機資料,用你打的 insert() 函數取代我寫的部分來檢測功能是否正常。 ## **insert() 函數:** ```c= void insert(int number, char color){ // 分配空間給新節點,並宣告一個指標變數 newNode 指向這個新節點 struct snode *newNode = (struct snode*)malloc(sizeof(struct snode)); // 將資料填入新節點 newNode->pla = number; newNode->st = color; // !!開始插入!! // 情況一:串列為空 if(opt == NULL){ // 插在串列最前面 newNode->nx = opt; opt = newNode; return; } // 情況二:新節點小於或等於串列的第一個節點 if(newNode->pla <= opt->pla){ // 插在串列最前面(同情況一) newNode->nx = opt; opt = newNode; return; } // 情況三:依序對串列中各節點比較大小,直到有節點大於新節點或已沒有節點可比較 struct snode *current = opt, *prev; while(newNode->pla > current->pla){ // current 會作為索引從第一個節點依序指向串列中各個節點進行比較 // prev 負責記錄 current 的前一個節點 prev = current; current = current->nx; // 若 current 等於 NULL 代表串列中的節點已經被比光了 if(current == NULL) break; } // 跳出 while 迴圈時 current 指向的節點必大於新節點或等於 NULL // 將新節點插入於 current 與 prev 所指的兩個節點中間 prev->nx = newNode; newNode->nx = current; return; } ``` <br> **我們將插入節點分為三種情況:** ### **1. 串列為空(串列中沒有任何節點)** * 插入方法:將新節點插入在最前面(作為串列的首個節點)。 ```c= if(opt == NULL){ // 插在串列最前面 newNode->next = opt; opt = newNode; return; } // 程式執行至 return 便會跳出副程式回到主程式 // 善加利用 return 可以簡化多層 if...else 的複雜邏輯 ``` ### **2. 新節點小於或等於串列的第一個節點** * 插入方法:將新節點插入在最前面(同情況一)。 ```c= if(newNode->pla <= opt->pla){ // 插在串列最前面 newNode->nx = opt; opt = newNode; return; } ``` ### **3. 若非前兩點之情況,依序對串列中各節點比較大小,直到有節點大於新節點或串列已沒有節點可比較** * 插入方法:將新節點插入於該節點前一個位置。 (若新節點大於串列中所有節點,則插在 NULL 的前一個位置,也就是最尾端。) ```c= struct snode *current = opt, *prev; while(newNode->pla > current->pla){ // current 會作為索引從第一個節點依序指向串列中各個節點進行比較 // prev 負責記錄 current 的前一個節點 // 如 current 指向節點五,prev 就是指向節點四 prev = current; current = current->nx; // 若 current 等於 NULL 代表串列中的節點已經被比光了 if(current == NULL) break; } // 跳出 while 迴圈時 current 指向的節點必大於新節點或等於 NULL // 將新節點插入於 current 與 prev 所指的兩個節點中間 prev->nx = newNode; newNode->nx = current; return; ``` > 當 currrent == NULL 時 prev 必指向最後一個節點, > 此時插入在 current 與 prev 所指的兩個節點中間等同於插在最尾端。