首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C 語言鏈表實作火車票管理系統完整教學

     編輯:程式設計疑難解答

系統需求分析

  • 車次資訊管理:車次號、出發站、到達站、時間、票價、餘票
  • 旅客訂票:查詢、訂購、退票、顯示訂單
  • 管理員功能:增刪改查車次、統計報表
  • 資料持久化:檔案讀寫

資料結構設計

1. 車次節點

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;

2. 訂單節點

typedef struct Order {
    char trainId[10];   // 車次號
    char passenger[20]; // 乘客姓名
    char idCard[19];    // 身分證
    int seatNum;        // 座位號
    struct Order* next;
} Order;

3. 全域鏈表頭指標

Train* trainHead = NULL;
Order* orderHead = NULL;

核心操作實作

1. 車次鏈表基本操作


// 建立新節點
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("未找到");
}

2. 訂票邏輯(含檔案同步)


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;
}

3. 退票邏輯


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解空指標、未初始化 nextmalloc 後立即 p->next=NULL;遍歷前檢查 head
資料不一致檔案讀寫結構體對齊問題使用 `#pragma pack(1)` 或逐欄位 fwrite/fread
記憶體洩漏刪除節點未 free、程式結束未釋放鏈表寫 destroyList() 於 main 結束前呼叫
重複訂票未檢查身分證是否已訂購同車次bookTicket 前遍歷 orderHead 檢查

進階擴充建議

  • 引入雙向鏈表(`prev` 指標)支援反向遍歷
  • 使用 B+ 樹或哈希表優化車次查找至 O(1)
  • 加入座位圖(二維陣列/位圖)支援選座功能
  • 實作並發控制(檔案鎖/互斥鎖)支援多執行緒
  • 移植至 C++ STL `list` 簡化記憶體管理

此系統展示了鏈表在動態資料管理中的核心優勢:插入刪除 O(1)、記憶體彈性分配、無固定容量限制。掌握此範例即可舉一反三實作通訊錄、庫存、排程等各類管理系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved