鏈表是數據結構面試與工程實踐的高頻考點。本文以單鏈表為例,對比 C 語言(指針手動管理內存)與 Java(引用自動 GC)的實現差異,涵蓋初始化、尾插、節點刪除三大核心操作,幫你建立跨語言的數據結構思維。
| 語言 | 節點定義 | 關鍵差異 |
|---|---|---|
| C | typedef struct nd { int data; struct nd* next; } node; | 結構體 + 指針,需手動 malloc/free |
| Java | class Node { int data; Node next; } | 類 + 引用,自動垃圾回收,無需釋放 |
node* init(void) {
node* head = (node*)malloc(sizeof(node));
if (head == NULL) return NULL; // 內存分配失敗檢查
head->next = NULL; // 空鏈表標記
return head;
}
關鍵點:必須檢查 malloc 返回值;頭節點本身不存有效數據,僅作哨兵。
class Node {
Node next = null;
int data;
public Node(int d) { data = d; }
// 頭節點通常直接 new Node(-1) 或類似哨兵值
}
關鍵點:無內存分配失敗憂慮(OOM 除外);構造器封裝初始化邏輯。
void insert(node* head, int data) {
if (head == NULL) return;
node* p = head;
while (p->next != NULL) p = p->next; // 找尾節點
node* newNode = (node*)malloc(sizeof(node));
if (newNode == NULL) return;
newNode->data = data;
newNode->next = NULL;
p->next = newNode; // 掛接
}
void appendToTail(int d) {
Node end = new Node(d);
Node n = this; // 從當前節點(通常是頭)開始
while (n.next != null) n = n.next;
n.next = end;
}
異同:邏輯完全一致(O(n) 查找尾部),Java 省去內存檢查與釋放代碼。
void delete(node* head, int data) {
if (head == NULL) return;
node *p = head;
// 情況 1:頭節點即目標(假設頭節點存數據,或頭節點後第一個)
if (head->data == data) { // 注意:若頭節點為哨兵,此判斷需調整
head = head->next;
free(p);
return;
}
// 情況 2:中間/尾部節點
node *q = head->next;
while (q != NULL) {
if (q->data == data) {
p->next = q->next; // 斷鏈
free(q); // 釋放內存
return; // 僅刪第一個匹配
}
p = q;
q = q->next;
}
}
難點:需維護前驅指針 p 以便斷鏈;必須 free 否則內存洩漏。
Node deleteNode(Node head, int d) {
if (head.data == d) return head.next; // 刪頭節點
Node n = head;
while (n.next != null) {
if (n.next.data == d) {
n.next = n.next.next; // 斷鏈,被斷開節點自動成 GC 目標
return head;
}
n = n.next;
}
return head; // 未找到
}
優勢:無需前驅指針變量(利用 n.next 間接訪問),無 free,代碼更安全簡潔。
| 維度 | C 語言 | Java |
|---|---|---|
| 內存管理 | 手動 malloc/free,易洩漏/野指針 | 自動 GC,專注業務邏輯 |
| 指針/引用 | 顯式指針運算,靈活但危險 | 安全引用,不可算術運算 |
| 錯誤處理 | 需檢查 NULL 返回值 | 異常機制,或返回 null |
| 代碼冗長度 | 高(含類型轉換、邊界檢查) | 低(泛型、封裝、簡潔語法) |
| 性能特點 | 極致可控,零開銷 | 略有開銷,但 JIT 優化後極接近 |
java.util.LinkedList,無需手寫。手寫僅為面試/教學理解原理。List 抽象數據類型(ADT),隱藏指針細節,提供統一 API。tail 指針/引用,將尾插從 O(n) 降為 O(1);引入 size 字段快速獲取長度。