---
# System prepended metadata

title: 資料結構 HW2 筆記

---

# 資料結構 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 所指的兩個節點中間等同於插在最尾端。