typedef struct Train {
char id[10]; // 車次號
char from[20]; // 出發站
char to[20]; // 到達站
char time[10]; // 發車時間
float price; // 票價
int total; // 總票數
int left; // 剩餘票數
struct Train* next;
} Train;
typedef struct Order {
char trainId[10]; // 車次號
char passenger[20]; // 乘客姓名
char idCard[19]; // 身分證
int seatNum; // 座位號
struct Order* next;
} Order;
Train* trainHead = NULL;
Order* orderHead = NULL;
// 建立新節點
Train* createTrainNode() {
Train* p = (Train*)malloc(sizeof(Train));
printf("車次 輸站 達站 時間 價格 總票: ");
scanf("%s %s %s %s %f %d", p->id, p->from, p->to, p->time, &p->price, &p->total);
p->left = p->total;
p->next = NULL;
return p;
}
// 尾插法建立鏈表
void appendTrain(Train** head) {
Train* p = createTrainNode();
if (*head == NULL) { *head = p; return; }
Train* cur = *head;
while (cur->next) cur = cur->next;
cur->next = p;
}
// 依車次號查找
Train* findTrain(Train* head, const char* id) {
while (head) {
if (strcmp(head->id, id) == 0) return head;
head = head->next;
}
return NULL;
}
// 刪除車次
void deleteTrain(Train** head, const char* id) {
Train *cur = *head, *prev = NULL;
while (cur) {
if (strcmp(cur->id, id) == 0) {
if (prev) prev->next = cur->next;
else *head = cur->next;
free(cur);
puts("刪除成功"); return;
}
prev = cur; cur = cur->next;
}
puts("未找到");
}
bool bookTicket(const char* trainId, const char* name, const char* idCard) {
Train* t = findTrain(trainHead, trainId);
if (!t) { puts("車次不存在"); return false; }
if (t->left <= 0) { puts("票已售罄"); return false; }
// 產生座位號
int seat = t->total - t->left + 1;
t->left--;
// 建立訂單
Order* o = (Order*)malloc(sizeof(Order));
strcpy(o->trainId, trainId);
strcpy(o->passenger, name);
strcpy(o->idCard, idCard);
o->seatNum = seat;
o->next = orderHead;
orderHead = o;
// 同步寫入檔案
saveTrainsToFile();
saveOrdersToFile();
printf("訂票成功!座位號:%d\n", seat);
return true;
}
bool refundTicket(const char* trainId, const char* idCard) {
// 從訂單鏈表找到並刪除
Order *cur = orderHead, *prev = NULL;
while (cur) {
if (strcmp(cur->trainId, trainId) == 0 && strcmp(cur->idCard, idCard) == 0) {
if (prev) prev->next = cur->next;
else orderHead = cur->next;
// 歸還座位
Train* t = findTrain(trainHead, trainId);
if (t) t->left++;
free(cur);
saveTrainsToFile();
saveOrdersToFile();
puts("退票成功"); return true;
}
prev = cur; cur = cur->next;
}
puts("無此訂單"); return false;
}
void saveTrainsToFile() {
FILE* fp = fopen("trains.dat", "wb");
for (Train* p = trainHead; p; p = p->next)
fwrite(p, sizeof(Train), 1, fp);
fclose(fp);
}
void loadTrainsFromFile() {
FILE* fp = fopen("trains.dat", "rb");
if (!fp) return;
Train temp;
while (fread(&temp, sizeof(Train), 1, fp)) {
Train* p = (Train*)malloc(sizeof(Train));
*p = temp; p->next = NULL;
// 尾插
if (!trainHead) trainHead = p;
else { Train* cur = trainHead; while (cur->next) cur = cur->next; cur->next = p; }
}
fclose(fp);
}
void adminMenu() {
int op;
while (1) {
puts("\n=== 管理員選單 ===");
puts("1.新增車次 2.刪除車次 3.修改車次 4.查看所有 5.返回");
scanf("%d", &op);
if (op == 1) appendTrain(&trainHead);
else if (op == 2) { char id[10]; scanf("%s", id); deleteTrain(&trainHead, id); }
else if (op == 3) { /* 修改邏輯 */ }
else if (op == 4) { /* 遍列印 */ }
else if (op == 5) break;
}
}
void passengerMenu() {
int op;
while (1) {
puts("\n=== 旅客選單 ===");
puts("1.查詢車次 2.訂票 3.退票 4.我的訂單 5.返回");
scanf("%d", &op);
if (op == 1) { /* 查詢邏輯 */ }
else if (op == 2) { char tid[10], name[20], idc[19]; scanf("%s%s%s", tid, name, idc); bookTicket(tid, name, idc); }
else if (op == 3) { char tid[10], idc[19]; scanf("%s%s", tid, idc); refundTicket(tid, idc); }
else if (op == 4) { /* 列印訂單 */ }
else if (op == 5) break;
}
}
int main() {
loadTrainsFromFile(); loadOrdersFromFile();
int role;
while (1) {
puts("\n=== 火車票系統 ===");
puts("1.管理員 2.旅客 3.退出");
scanf("%d", &role);
if (role == 1) adminMenu();
else if (role == 2) passengerMenu();
else if (role == 3) break;
}
return 0;
}
初始狀態: trainHead -> NULL
新增 G123: trainHead -> [G123|next] -> NULL
新增 G456: trainHead -> [G123] -> [G456] -> NULL
刪除 G123: trainHead -> [G456] -> NULL
查找 G456: 遍歷比對 id 字串 -> 返回節點指標
| 錯誤 | 原因 | 修正 |
|---|---|---|
| Segmentation Fault | 解空指標、未初始化 next | malloc 後立即 p->next=NULL;遍歷前檢查 head |
| 資料不一致 | 檔案讀寫結構體對齊問題 | 使用 `#pragma pack(1)` 或逐欄位 fwrite/fread |
| 記憶體洩漏 | 刪除節點未 free、程式結束未釋放鏈表 | 寫 destroyList() 於 main 結束前呼叫 |
| 重複訂票 | 未檢查身分證是否已訂購同車次 | bookTicket 前遍歷 orderHead 檢查 |
此系統展示了鏈表在動態資料管理中的核心優勢:插入刪除 O(1)、記憶體彈性分配、無固定容量限制。掌握此範例即可舉一反三實作通訊錄、庫存、排程等各類管理系統。