首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

單鏈表(Singly Linked List)資料結構深度解析與 C 語言完整實作

     編輯:關於C語言

單鏈表核心概念

單鏈表採用非連續記憶體儲存,每個節點包含資料域與指標域,指標指向下一個節點。透過頭節點(哨兵節點)統一操作邏輯,避免頭尾特殊情況判斷。優勢在於插入刪除 O(1)(已知前驅節點),劣勢為查找 O(n)、額外指標空間開銷。

節點與鏈表定義

typedef int ElemType;
typedef struct Node {
    ElemType data;
    struct Node *next;
} Node, *LinkList;   // LinkList 即 Node* 指向頭節點

核心操作完整實作

1. 初始化與銷毀

void InitList(LinkList *L) {
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
}
void DestroyList(LinkList *L) {
    LinkList p = *L, q;
    while (p) { q = p->next; free(p); p = q; }
    *L = NULL;                   // 避免野指標
}
void ClearList(LinkList *L) {    // 保留頭節點,釋放資料節點
    LinkList p = (*L)->next, q;
    while (p) { q = p->next; free(p); p = q; }
    (*L)->next = NULL;
}

2. 建立鏈表:頭插法 vs 尾插法

頭插法:新節點插在頭節點後,順序與輸入相反,適合堆疊語意。
尾插法:維護尾指標,新節點接在尾部,保持輸入順序,適合佇列語意。

void HeadCreateList(LinkList *L, int n) {
    for (int i=1; i<=n; i++) {
        LinkList s = (LinkList)malloc(sizeof(Node));
        scanf("%d", &s->data);
        s->next = (*L)->next;
        (*L)->next = s;
    }
}
void TailCreateList(LinkList *L, int n) {
    LinkList tail = *L;
    for (int i=1; i<=n; i++) {
        LinkList s = (LinkList)malloc(sizeof(Node));
        scanf("%d", &s->data);
        tail->next = s;  s->next = NULL;  tail = s;
    }
}

3. 基礎查詢操作

bool ListEmpty(LinkList L) { return L->next == NULL; }
int ListLength(LinkList L) {
    int cnt=0; LinkList p=L->next;
    while(p){ cnt++; p=p->next; } return cnt;
}
bool GetElem(LinkList L, int i, ElemType *e) {  // 按位查找
    if (i<1) return false;
    LinkList p = L->next;
    for (int j=1; p && j

4. 前驅/後繼查找

void GetPriorElem(LinkList L, ElemType cur, ElemType *pre) {
    LinkList p = L->next;
    while (p->next) {
        if (p->next->data == cur) { *pre = p->data; return; }
        p = p->next;
    }
    printf("無前驅或元素不存在\n");
}
void GetNextElem(LinkList L, ElemType cur, ElemType *next) {
    LinkList p = L->next;
    while (p) {
        if (p->data == cur && p->next) { *next = p->next->data; return; }
        p = p->next;
    }
    printf("無後繼或元素不存在\n");
}

5. 插入與刪除(核心:找前驅)

bool ListInsert(LinkList *L, int i, ElemType e) {  // 第 i 個位置前插入
    LinkList p = *L; int cnt = 0;
    while (p && cnt < i-1) { p = p->next; cnt++; }
    if (!p) return false;
    LinkList s = (LinkList)malloc(sizeof(Node));
    s->data = e; s->next = p->next; p->next = s;
    return true;
}
bool ListDelete(LinkList *L, int i, ElemType *e) {   // 刪除第 i 個
    LinkList p = *L; int cnt = 0;
    while (p && cnt < i-1) { p = p->next; cnt++; }
    if (!p || !p->next) return false;
    LinkList q = p->next; *e = q->data;
    p->next = q->next; free(q); return true;
}

6. 遍歷輸出

void DisplayList(LinkList L) {
    if (!L) { puts("List destroyed"); return; }
    LinkList p = L;
    while (p->next) { printf("%d ", p->next->data); p = p->next; }
    puts("\n********displayList executed!");
}

主程式綜合測試

int main() {
    LinkList L; InitList(&L);
    TailCreateList(&L,4); DisplayList(L);
    ClearList(&L); ListEmpty(L); DisplayList(L);
    DestroyList(&L); DisplayList(L);   // 驗證銷毀後狀態

    InitList(&L); TailCreateList(&L,5);
    printf("Length=%d\n", ListLength(L)); DisplayList(L);

    ElemType r;
    GetElem(L,5,&r) ? printf("GetElem 5=%d\n",r) : puts("Failed");
    GetElem(L,6,&r) ? printf("GetElem 6=%d\n",r) : puts("Failed");

    GetPriorElem(L,3,&r); printf("Prior of 3=%d\n",r);
    GetNextElem(L,3,&r);  printf("Next of 3=%d\n",r);

    ListInsert(&L,5,999); DisplayList(L);
    ListDelete(&L,2,&r);  DisplayList(L); printf("Deleted=%d\n",r);
    return 0;
}

進階話題與實務建議

  • 雙向鏈表:增設 prior 指標,支援 O(1) 反向遍歷與刪除已知節點;
  • 循環鏈表:尾節點指向頭節點,適用循環輪詢(如 Josephus 問題);
  • 記憶體池:頻繁 malloc/free 造成碎片,實務可預分配節點陣列自行管理;
  • 智慧指標:C++ 可用 unique_ptr/shared_ptr 自動管理生命週期,消除記憶體洩漏風險。

總結

單鏈表以指標串聯離散節點,實現動態擴容、高效插刪,是實作堆疊、佇列、哈希桶、圖鄰接表等高階結構的基石。熟練掌握「找前驅、改指標、管記憶體」三步驟,並養成畫圖驗證指標變化習慣,是攻克鏈表題目的關鍵。

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