需求分析
- 記錄:姓名 (字串)、電話 (字串)。
- 功能:建立、查找指定姓名、指定節點前插入 (不存在則尾插)。
資料結構設計
#include
#include
#include
typedef struct Node {
char name[32];
char phone[16];
struct Node *next;
} Node;
// 建立節點
Node* create_node(const char *name, const char *phone) {
Node *n = malloc(sizeof(Node));
if (!n) { perror("malloc"); exit(EXIT_FAILURE); }
strncpy(n->name, name, sizeof(n->name)-1);
n->name[sizeof(n->name)-1] = '\0';
strncpy(n->phone, phone, sizeof(n->phone)-1);
n->phone[sizeof(n->phone)-1] = '\0';
n->next = NULL;
return n;
}
核心操作實作
1. 尾插建立鏈表 (保持輸入順序)
Node* build_list(void) {
Node *head = NULL, *tail = NULL;
char name[32], phone[16];
printf("輸入姓名 電話 (空行結束):\n");
while (scanf("%31s %15s", name, phone) == 2) {
Node *n = create_node(name, phone);
if (!head) head = tail = n;
else { tail->next = n; tail = n; }
}
return head;
}
2. 查找姓名 (返回節點指標,未找到返回 NULL)
Node* find_by_name(Node *head, const char *name) {
for (Node *p = head; p; p = p->next)
if (strcmp(p->name, name) == 0)
return p;
return NULL;
}
3. 指定節點前插入 (不存在則尾插)
void insert_before(Node **head_ref, const char *target_name, Node *new_node) {
Node *prev = NULL, *curr = *head_ref;
while (curr && strcmp(curr->name, target_name) != 0) {
prev = curr;
curr = curr->next;
}
if (!curr) { // 未找到 → 尾插
if (!*head_ref) *head_ref = new_node;
else {
Node *tail = *head_ref;
while (tail->next) tail = tail->next;
tail->next = new_node;
}
} else if (!prev) { // 插在頭節點前 → 新頭
new_node->next = *head_ref;
*head_ref = new_node;
} else { // 一般情況
prev->next = new_node;
new_node->next = curr;
}
}
4. 釋放整個鏈表 (防洩漏)
void free_list(Node *head) {
while (head) {
Node *tmp = head;
head = head->next;
free(tmp);
}
}
主程式整合
int main(void) {
Node *contacts = build_list();
// 示範查找
char query[32];
printf("查找姓名: ");
if (scanf("%31s", query) == 1) {
Node *found = find_by_name(contacts, query);
if (found) printf("找到: %s %s\n", found->name, found->phone);
else printf("未找到\n");
}
// 示範插入
char target[32], new_name[32], new_phone[16];
printf("插入於哪位之前 (姓名): ");
if (scanf("%31s", target) == 1) {
printf("新聯繫人 姓名 電話: ");
if (scanf("%31s %15s", new_name, new_phone) == 2) {
Node *n = create_node(new_name, new_phone);
insert_before(&contacts, target, n);
}
}
// 列印驗證
printf("\n通訊錄內容:\n");
for (Node *p = contacts; p; p = p->next)
printf("%s\t%s\n", p->name, p->phone);
free_list(contacts);
return 0;
}
關鍵細節總結
| 環節 | 關鍵點 |
| 字串複製 | strncpy + 手動加 \0,防溢位 |
| 輸入安全 | scanf("%31s", buf) 限制寬度 = 陣列大小-1 |
| 插入邊界 | 頭插、尾插、中間插、空表、目標不存在 全覆蓋 |
| 雙重指標 | Node **head_ref 允許修改頭指標 (頭插時) |
| 記憶體釋放 | 主程式結束前 free_list,Valgrind 0 leaks |
進階擴充建議
- 改用 **雙向鏈表** (含
prev) 使刪除/反向遍歷 O(1)。
- 引入 **雜湊表** (姓名 → 節點) 使查找 O(1)。
- 持久化:
fwrite 結構體陣列或序列化 JSON/CSV 存檔。
- 介面:ncurses TUI / 簡易 HTTP 伺服器 (cJSON + libmicrohttpd)。