數組雖然訪問快(O(1)),但大小固定、插入刪除需移動元素(O(n))、易造成空間浪費或不足。鏈表通過動態內存分配,按需申請釋放,完美解決上述痛點:
struct node {
int num; // 數據域:此處存整型
struct node *next; // 指針域:指向同類型下一節點
};
注意:結構體內使用未完全定義的類型指針,這是 C 語言允許的特例(遞歸定義)。
struct node *creat(struct node *head) {
struct node *p1, *p2;
p1 = p2 = (struct node *)malloc(sizeof(struct node));
scanf("%d", &p1->num);
p1->next = NULL;
while (p1->num > 0) { // 輸入正整數,-999 結束
if (head == NULL)
head = p1; // 空表,接入表頭
else
p2->next = p1; // 非空表,接到表尾
p2 = p1;
p1 = (struct node *)malloc(sizeof(struct node));
scanf("%d", &p1->num);
}
return head;
}
void print(struct node *head) {
struct node *temp = head;
while (temp != NULL) {
printf("%6d", temp->num);
temp = temp->next;
}
}
刪除節點可能發生在表頭、表中、表尾。關鍵是保持前驅節點指向後繼:
struct node *delet(struct node *head, char *pstr) {
struct node *temp = head, *p;
if (head == NULL) {
printf("鏈表為空!\n");
return head;
}
while (strcmp(temp->str, pstr) != 0 && temp->next != NULL) {
p = temp;
temp = temp->next;
}
if (strcmp(temp->str, pstr) == 0) {
if (temp == head) { // 刪除表頭
head = head->next;
free(temp);
} else { // 刪除表中/表尾
p->next = temp->next;
free(temp);
}
} else {
printf("未找到目標節點!\n");
}
return head; // 返回新頭指針,防止表頭刪除導致頭指針失效
}
插入位置同樣分表頭、表中、表尾三種:
struct node *insert(struct node *head, char *pstr, int n) {
struct node *p1 = (struct node *)malloc(sizeof(struct node));
strcpy(p1->str, pstr);
p1->num = n;
struct node *p2 = head, *p3;
if (head == NULL) { // 空表
head = p1;
p1->next = NULL;
} else {
while (n > p2->num && p2->next != NULL) {
p3 = p2;
p2 = p2->next;
}
if (n <= p2->num) { // 找到插入位置
if (head == p2) { // 插入表頭
head = p1;
p1->next = p2;
} else { // 插入表中
p3->next = p1;
p1->next = p2;
}
} else { // 插入表尾
p2->next = p1;
p1->next = NULL;
}
}
return head;
}
完整程序包含:創建有序鏈表 → 輸出 → 插入新節點 → 輸出 → 刪除指定節點 → 輸出。核心模塊:
creat():輸入學號、姓名,按學號遞增建表,空姓名結束insert():按學號有序插入delet():按姓名刪除print():格式化輸出所有節點malloc 必檢查,free 後指針置 NULL,防止野指針/二次釋放。fgets + sscanf 替代 scanf,處理換行符、緩衝區溢出。掌握單鏈表後,可進階學習:雙向鏈表(O(1) 刪除已知節點)、循環鏈表(輪詢調度)、跳表(O(log n) 查找)、鏈式隊列/棧(ADT 實現)。鏈表是理解圖、樹等複雜結構的基石,建議反復練習手寫核心操作。