資料結構入門-C語言實作環狀串列教學

更新於 發佈於 閱讀時間約 12 分鐘

前導

環狀鏈結串列是一種特殊的鏈結串列,其最後一個節點的指標指向第一個節點,而非 NULL,形成一個循環結構。如下圖所示:

raw-image

一、環狀鏈結串列的插入(Insertion)

插入新節點時,可能發生在:

  1. 空串列時的插入
  2. 頭部插入(Head insertion)
  3. 尾部插入(Tail insertion)
  4. 中間插入(After a given node)

1. 插入至空的環狀鏈結串列

當鏈結串列為空時,插入的節點既是 head 也是 tail,並且它的 next 指向自己。

  1. 創建新節點 newNode
  2. 設定 newNode->next = newNode 形成環狀
  3. head = newNodetail = newNode

2. 在頭部插入節點

  1. 創建新節點 newNode
  2. newNode->next = head(新節點指向當前頭部)
  3. tail->next = newNode(尾部節點指向新頭部)
  4. 更新 head = newNode

👉 效果:新節點變成 head,但環狀結構保持不變。

raw-image

3. 在指定節點後插入

  1. 找到目標節點 X
  2. newNode->next = X->next
  3. X->next = newNode

👉 效果:新節點插入 X 之後,環狀結構保持。

raw-image

4. 在尾部插入節點

  1. 創建新節點 newNode
  2. tail->next = newNode(當前尾部指向新節點)
  3. newNode->next = head(新節點指向 head
  4. 更新 tail = newNode
  5. 讀者可以自行思考並繪圖。

👉 效果:新節點變成 tail,環狀結構保持。


二、環狀鏈結串列的刪除(Deletion)

刪除節點時,可能發生在:

  1. 刪除頭部節點
  2. 刪除中間節點
  3. 刪除尾部節點
  4. 刪除最後一個節點(使串列變空)

1. 刪除頭部節點

  1. head = head->next(更新 head 指向下一個節點)
  2. tail->next = head(尾部保持指向新的 head

👉 效果:原head 被移除,環狀結構保持。

raw-image

2. 刪除中間節點

  1. 找到 X 的前一個節點 prev
  2. prev->next = X->next

👉 效果:節點 X 被移除,環狀結構保持。

raw-image

3. 刪除尾部節點

  1. 找到 tail 的前一個節點 prev
  2. prev->next = head
  3. 更新 tail = prev
  4. 讀者可以自行思考並繪圖。

👉 效果:新 tail 成為 prev,環狀結構保持。

C語言實作

#include <stdio.h>
#include <stdlib.h>

// 定義節點結構
typedef struct Node {
int data;
struct Node* next;
} Node;

// 創建新節點
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = newNode; // 初始指向自己
return newNode;
}

// 插入到空的環狀鏈結串列
void insertEmpty(Node** head, int data) {
if (*head != NULL) return;
*head = createNode(data);
}

// 在頭部插入節點
void insertHead(Node** head, int data) {
if (*head == NULL) {
insertEmpty(head, data);
return;
}
Node* newNode = createNode(data);
newNode->next = *head;

// 找到 tail
Node* temp = *head;
while (temp->next != *head) temp = temp->next;
temp->next = newNode;
*head = newNode; // 更新 head
}

// 在尾部插入節點
void insertTail(Node** head, int data) {
if (*head == NULL) {
insertEmpty(head, data);
return;
}
Node* newNode = createNode(data);
Node* temp = *head;

while (temp->next != *head) temp = temp->next;

temp->next = newNode;
newNode->next = *head;
}

// 在指定節點後插入
void insertAfter(Node* prevNode, int data) {
if (prevNode == NULL) return;

Node* newNode = createNode(data);
newNode->next = prevNode->next;
prevNode->next = newNode;
}

// 刪除頭部節點
void deleteHead(Node** head) {
if (*head == NULL) return;

// 只有一個節點
if ((*head)->next == *head) {
free(*head);
*head = NULL;
return;
}

Node* temp = *head;
while (temp->next != *head) temp = temp->next;

Node* toDelete = *head;
*head = (*head)->next;
temp->next = *head;
free(toDelete);
}

// 刪除尾部節點
void deleteTail(Node** head) {
if (*head == NULL) return;

// 只有一個節點
if ((*head)->next == *head) {
free(*head);
*head = NULL;
return;
}

Node* temp = *head;
Node* prev = NULL;

// 找到尾部與前一個節點
while (temp->next != *head) {
prev = temp;
temp = temp->next;
}

prev->next = *head; // 讓前一個節點變成新的尾部
free(temp);
}

// 刪除指定值的節點
void deleteNode(Node** head, int key) {
if (*head == NULL) return;

Node *curr = *head, *prev = NULL;
while (curr->data != key) {
if (curr->next == *head) return; // 沒找到
prev = curr;
curr = curr->next;
}

if (curr == *head) { // 如果刪除的是頭部
deleteHead(head);
return;
}

prev->next = curr->next;
free(curr);
}

// 列印環狀鏈結串列
void printList(Node* head) {
if (head == NULL) {
printf("什麼都沒有啦~哥!!");
return;
}
Node* temp = head;
do {
printf("%d -> ", temp->data);
temp = temp->next;
} while (temp != head);
printf("(head)\n");
}

// 主程式
int main() {
Node* head = NULL;

insertHead(&head, 10);
insertHead(&head, 20);
insertTail(&head, 30);
insertTail(&head, 40);
printList(head); // 20 -> 10 -> 30 -> 40 -> (head)

deleteHead(&head);
printList(head); // 10 -> 30 -> 40 -> (head)

deleteTail(&head);
printList(head); // 10 -> 30 -> (head)

insertAfter(head, 25); // 在 10 之後插入 25
printList(head); // 10 -> 25 -> 30 -> (head)

deleteNode(&head, 25);
printList(head); // 10 -> 30 -> (head)

deleteHead(&head); // 30 -> (head)
printList(head);

//deleteHead(&head); // 什麼都沒有啦~哥!!
//deleteTail(&head); // 什麼都沒有啦~哥!!
deleteNode(&head, 30); // 什麼都沒有啦~哥!!
printList(head);

return 0;
}

輸出:

20 -> 10 -> 30 -> 40 -> (head)
10 -> 30 -> 40 -> (head)
10 -> 30 -> (head)
10 -> 25 -> 30 -> (head)
10 -> 30 -> (head)
30 -> (head)
什麼都沒有啦~哥!!

主程式使用的函式總結

插入

  • insertHead(&head, data)在頭部插入,當鏈結串列為空時呼叫insertEmpty(&head, data)
  • insertTail(&head, data)在尾部插入
  • insertAfter(Node* prevNode, data)在指定節點後插入

刪除

  • deleteHead(&head)刪除頭部節點
  • deleteTail(&head)刪除尾部節點
  • deleteNode(&head, key)刪除指定值的節點

額外

  • printList(head)列印環狀鏈結串列
在當今數位時代,電資領域人才需求爆發式成長,不論是前端網頁設計、嵌入式開發、人工智慧、物聯網還是軟硬體整合,這些技術都在改變世界。而掌握 C/C++、Python、數位邏輯、電路學與嵌入式開發等大學電資領域的課程,正是進入這個高薪、高需求產業的關鍵!
留言
avatar-img
留言分享你的想法!
稀疏矩陣(Sparse Matrix)是指在大多數元素為零的矩陣。由於存儲完整的稀疏矩陣會浪費大量內存,因此通常使用特殊的數據結構來存儲和操作稀疏矩陣。在 C 語言中,可以將稀疏矩陣的非零元素以row、column、value的方式存放。 本章節將會帶領你認識此資工科中會教的重要觀念並實作程式碼。
今天如果要你印出1-100之間的不重複隨機數排列,你該怎麼做? 本章節將從程式碼開始,讓你直接了解解題的思維與觀念。
遞迴就是函式在執行過程中呼叫自身,並通過結束條件和呼叫堆疊來解決問題。 這種方式通常用於解決可以分解為相同問題的子問題的情況。 本章節將以最容易理解的方式解說這個核心概念,並且邁入較艱深的應用範例,提升程式思考邏輯力。
內插搜尋法(Interpolation Search)是一種改進版的 二分搜尋法,但它不是直接取中間值,而是 根據目標值的位置,預測索引的範圍,類似於人類在 查找電話簿 或 字典 時的方式。 本章節將帶你了解此演算法概念,並透過C語言實作。
二分搜尋法(Binary Search)是一種 高效的搜尋演算法,適用於 已排序 的資料結構。 它的核心思想是 每次將搜尋範圍減半,直到找到目標值或範圍縮小到無法繼續搜尋。 本章節將帶領讀者學會這個演算法,並透過C語言實作。
稀疏矩陣(Sparse Matrix)是指在大多數元素為零的矩陣。由於存儲完整的稀疏矩陣會浪費大量內存,因此通常使用特殊的數據結構來存儲和操作稀疏矩陣。在 C 語言中,可以將稀疏矩陣的非零元素以row、column、value的方式存放。 本章節將會帶領你認識此資工科中會教的重要觀念並實作程式碼。
今天如果要你印出1-100之間的不重複隨機數排列,你該怎麼做? 本章節將從程式碼開始,讓你直接了解解題的思維與觀念。
遞迴就是函式在執行過程中呼叫自身,並通過結束條件和呼叫堆疊來解決問題。 這種方式通常用於解決可以分解為相同問題的子問題的情況。 本章節將以最容易理解的方式解說這個核心概念,並且邁入較艱深的應用範例,提升程式思考邏輯力。
內插搜尋法(Interpolation Search)是一種改進版的 二分搜尋法,但它不是直接取中間值,而是 根據目標值的位置,預測索引的範圍,類似於人類在 查找電話簿 或 字典 時的方式。 本章節將帶你了解此演算法概念,並透過C語言實作。
二分搜尋法(Binary Search)是一種 高效的搜尋演算法,適用於 已排序 的資料結構。 它的核心思想是 每次將搜尋範圍減半,直到找到目標值或範圍縮小到無法繼續搜尋。 本章節將帶領讀者學會這個演算法,並透過C語言實作。
你可能也想看
Google News 追蹤
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
Append Characters to String to Make Subsequence 給定兩個字串s和字串t。 請計算最少的字元串接數量是多少,串接在s的尾端,使得t是s的子序列。 測試範例 Example 1: Input: s = "coaching", t =
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 輸入給定一個鏈結串列的head node。 要求我們進行化簡,只要某個節點的右手邊存在比較大的節點,就刪除掉。 例如 5->2->13->3 5的右手邊有13,所以5刪除掉。 2的右手邊有13,所以2刪除掉。 13的右手邊沒有更大的節點,所以13留著。 3的右手邊沒有更大
Thumbnail
這篇文章,會帶著大家複習以前學過的 區間DP框架, 並且以回文子字串、回文子序列的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 回文字串的基本定義 s = s[::-1] 也就是說字串s的正序 和 逆序完全相同。 回文字串的基本結構 空字串"
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定我們一條鏈結串列Linked list的起始節點,要求我們刪除Linked List正中央的節點。 註: 正中央的節點,題目定義為索引為floor( 串列長度 / 2 ) 的節點,索引從零(Head Node)出發開始數。 例如 1 -> 2 -> 3 -> 4 鏈結
Thumbnail
巢狀迴圈For loop介紹結構及範例說明 巢狀迴圈 巢狀迴圈是在一個迴圈內包含另一個迴圈的結構 簡單來說,就是內迴圈做完,才會在跑到外迴圈,接著在做內迴圈
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
Append Characters to String to Make Subsequence 給定兩個字串s和字串t。 請計算最少的字元串接數量是多少,串接在s的尾端,使得t是s的子序列。 測試範例 Example 1: Input: s = "coaching", t =
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 輸入給定一個鏈結串列的head node。 要求我們進行化簡,只要某個節點的右手邊存在比較大的節點,就刪除掉。 例如 5->2->13->3 5的右手邊有13,所以5刪除掉。 2的右手邊有13,所以2刪除掉。 13的右手邊沒有更大的節點,所以13留著。 3的右手邊沒有更大
Thumbnail
這篇文章,會帶著大家複習以前學過的 區間DP框架, 並且以回文子字串、回文子序列的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 回文字串的基本定義 s = s[::-1] 也就是說字串s的正序 和 逆序完全相同。 回文字串的基本結構 空字串"
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定我們一條鏈結串列Linked list的起始節點,要求我們刪除Linked List正中央的節點。 註: 正中央的節點,題目定義為索引為floor( 串列長度 / 2 ) 的節點,索引從零(Head Node)出發開始數。 例如 1 -> 2 -> 3 -> 4 鏈結
Thumbnail
巢狀迴圈For loop介紹結構及範例說明 巢狀迴圈 巢狀迴圈是在一個迴圈內包含另一個迴圈的結構 簡單來說,就是內迴圈做完,才會在跑到外迴圈,接著在做內迴圈