在 C 語言中使用鏈結串列的優勢

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


在 C 語言中,陣列的大小是固定的,並且使用連續的記憶體空間。如果要在陣列中間插入新元素,可能會變得麻煩。例如,當我要在 1, 2, 3 後插入 4 時,若後面的記憶體區塊已被佔用,就無法直接插入。此時,必須找到足夠大的空間來重新複製資料。這樣的過程不僅耗時,也不靈活。

這時,鏈結串列就派上用場了!

插入4,要先找到其他的儲存格。圖片來源:CS50。

插入4,要先找到其他的儲存格。圖片來源:CS50。

鏈結串列是什麼?

鏈結串列是一種更靈活的資料結構,它不需要連續的記憶體空間,。鏈結串列由一連串的節點組成,每個節點包含資料和一個指向下一個節點的指標。當需要插入新元素時,只需更新指標來連接新節點,這樣就避免了重新分配大區塊記憶體的麻煩。

程式碼的顯示

指到下一個節點

之前的文章提過,C 語言可以自己建立資料型態:

演算法 | Big O 複雜度| Pseudocode 偽代碼

typedef struct
{
string name;
string number;
}person;

而鏈結串列會有一個 node 節點,而他會指向下個地址

#include <stdio.h>

typedef struct node
{
int number;
struct node *next; //指向下個 node
} node;
node *life; //建立一個指標變數life,他指向的資料型別是node

但是隨便指向沒有資料的地方,會出現電腦隨便丟垃圾訊息。所以我們要分配記憶體給他,

node *n = malloc(sizeof(node));// n是個指標變數,指向的值的資料型別是node
(*n).number = 1: // *指向下個node,"."的意思是在資料型別為number的地方

(*n).number = 1 可以簡化成 n->number =1

圖像化鏈結串列

命令列參數中的數字插入一個鏈結串列 (linked list) ,插到最前面

圖片來源:CS50

raw-image

接下來,如何把 n 連起來換給 list,把 n 賦值給 list。

raw-image


raw-image

接下來如何把1和2串在一起?不是直接 list = n !

raw-image
raw-image
raw-image


數字插入一個鏈結串列 (linked list) 中

新的節點 (數字) 插入到兩個已有節點之間的程式碼如下,往下還有圖片說明:

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

// 定義一個節點的結構,包含數字和指向下一個節點的指標
typedef struct node
{
int number; // 節點中的數字
struct node *next; // 指向下一個節點的指標
}
node;

int main(int argc, char *argv[])
{
// 初始化鏈結串列為空
node *list = NULL;

// 逐一處理每個命令列參數
for (int i = 1; i < argc; i++)
{
// 將參數轉換成整數
int number = atoi(argv[i]);

// 為該數字分配一個新的節點
node *n = malloc(sizeof(node));
if (n == NULL) // 檢查是否成功分配記憶體
{
return 1; // 若失敗,返回錯誤代碼 1
}
n->number = number; // 將數字儲存在節點中
n->next = NULL; // 新節點的下一個指標先設為空

// 如果鏈結串列為空,將這個節點作為鏈結串列的第一個節點
if (list == NULL)
{
list = n;
}

// 如果該數字應該放在鏈結串列的開頭
else if (n->number < list->number)
{
n->next = list; // 將新節點的指標指向原本的第一個節點
list = n; // 新節點成為鏈結串列的第一個節點
}

// 如果該數字應該放在串列的中間或末端
else
{
// 遍歷鏈結串列,尋找適當的位置插入
for (node *ptr = list; ptr != NULL; ptr = ptr->next)
{
// 如果已經到了鏈結串列的末端
if (ptr->next == NULL)
{
ptr->next = n; // 將新節點加在末端
break;
}

// 如果該數字應該插入在中間某個位置
if (n->number < ptr->next->number)
{
n->next = ptr->next; // 新節點指向後續節點
ptr->next = n; // 前一個節點指向新節點
break;
}
}
}
}

// 印出鏈結串列中的數字
for (node *ptr = list; ptr != NULL; ptr = ptr->next)
{
printf("%i\n", ptr->number);
}

// 釋放記憶體
node *ptr = list;
while (ptr != NULL)
{
node *next = ptr->next; // 保存下一個節點的位置
free(ptr); // 釋放當前節點
ptr = next; // 移動到下一個節點
}
}


raw-image


raw-image


從中間插入新的數值

raw-image


陣列與鏈結串列的 Big O 複雜度

從效能角度來看,陣列和鏈結串列在不同操作上的表現各有所長:

  • 插入或刪除元素:對於陣列來說,插入或刪除元素可能需要移動大部分的元素,因此是 O(n) 的時間複雜度;而鏈結串列只需要調整指標,因此是 O(1)。
  • 找特定位置的元素:陣列可以直接通過索引快速找到任意位置的元素,這是 O(1) 的操作;而鏈結串列則需要遍歷所有節點來找到目標位置,要從第一個開始找陣列,因此是 O(n)。


留言0
查看全部
avatar-img
發表第一個留言支持創作者!
延續上一篇的指標,補充 pass by value 和 pass by reference 的差別。 C 語言指標-程式碼圖解 當我們在程式中呼叫函數時,變數的傳遞方式有兩種:pass by value 和 pass by reference。這兩者的區別主要在於傳遞的是「值」還是「記憶體位置的
大魔王指標:初學者的天堂路。 指標(Pointer)是 C 語言裡的「大魔王」,是資工系學生花了至少 9 小時上的課。我們一起用 18 分鐘文章快速了解指標的基本概念,中間在字串的部分我將結果和程式碼做對照。最後,我會將我跟 ChatGPT 對話放上來跟大家一起學習。
本篇文章探討計算機理論中的 P/NP 問題,分析其在演算法和理論計算中的重要性。透過回溯法及圖靈機的介紹,讀者將更瞭解 P 問題與 NP 問題的區別,以及它們在解決複雜問題中的挑戰。最後,我們將提及停機問題,揭示計算機在面對某些問題時的侷限性。
上次我們提到了演算法(algorithm),它是一種解決問題的方式。但演算法只是資料結構與演算法(Data Structures and Algorithms, DSA)這個領域的一部分。今天,我們要進一步探索這個主題,了解它的核心概念。 什麼是資料結構與演算法呢?簡單來說,資料結構是用來組織和存
上回提到,演算法是一種解決問題的方法。光是簡單的將數字有小排到大就有很多種不同的排序演算法可以選擇。這次,我們來介紹幾個常見的排序演算法,看看它們是怎麼運作的。
演算法是一種解決問題的虛擬邏輯,他不像 C 語言有直接的程式碼,而是一種虛擬的問題解決方式。 想像一下,今天要在字典裡面找到 Zoo,有幾種方法: 逐頁查找:如果字典有 1000 頁,最糟情況下需要翻 1000 次 才能找到。 兩頁兩頁找:這樣的話,1000 頁最多要翻 500 次。 二分查
延續上一篇的指標,補充 pass by value 和 pass by reference 的差別。 C 語言指標-程式碼圖解 當我們在程式中呼叫函數時,變數的傳遞方式有兩種:pass by value 和 pass by reference。這兩者的區別主要在於傳遞的是「值」還是「記憶體位置的
大魔王指標:初學者的天堂路。 指標(Pointer)是 C 語言裡的「大魔王」,是資工系學生花了至少 9 小時上的課。我們一起用 18 分鐘文章快速了解指標的基本概念,中間在字串的部分我將結果和程式碼做對照。最後,我會將我跟 ChatGPT 對話放上來跟大家一起學習。
本篇文章探討計算機理論中的 P/NP 問題,分析其在演算法和理論計算中的重要性。透過回溯法及圖靈機的介紹,讀者將更瞭解 P 問題與 NP 問題的區別,以及它們在解決複雜問題中的挑戰。最後,我們將提及停機問題,揭示計算機在面對某些問題時的侷限性。
上次我們提到了演算法(algorithm),它是一種解決問題的方式。但演算法只是資料結構與演算法(Data Structures and Algorithms, DSA)這個領域的一部分。今天,我們要進一步探索這個主題,了解它的核心概念。 什麼是資料結構與演算法呢?簡單來說,資料結構是用來組織和存
上回提到,演算法是一種解決問題的方法。光是簡單的將數字有小排到大就有很多種不同的排序演算法可以選擇。這次,我們來介紹幾個常見的排序演算法,看看它們是怎麼運作的。
演算法是一種解決問題的虛擬邏輯,他不像 C 語言有直接的程式碼,而是一種虛擬的問題解決方式。 想像一下,今天要在字典裡面找到 Zoo,有幾種方法: 逐頁查找:如果字典有 1000 頁,最糟情況下需要翻 1000 次 才能找到。 兩頁兩頁找:這樣的話,1000 頁最多要翻 500 次。 二分查
你可能也想看
Google News 追蹤
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
給定兩個輸入整數陣列, 若在兩個陣列遇到相同的數字可以連成一線, 但是有規定連線不可和別的連線有交叉, 請問最多可以形成幾條連線? 解答中探討了演算法化簡的技巧和DP模型, 可以透過演算法化簡的技巧, 把這題映射到原本已經學會的Longest Common Subsequence的DP模型來解開。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
給定一個字串s,以s擁有的字元製造迴文字串。要能製造出的迴文字串長度最長是多少,觀察迴文字串不外乎兩種模式對稱部分 + 核心字元 + 對稱部分,其中,核心字元在正中央出現一次,或者 對稱部分 + 對稱部分。使用演算法統計出現次數並推理出盡可能充分利用每個字元的迴文字串製造方法。
Thumbnail
Append Characters to String to Make Subsequence 給定兩個字串s和字串t。 請計算最少的字元串接數量是多少,串接在s的尾端,使得t是s的子序列。 測試範例 Example 1: Input: s = "coaching", t =
Thumbnail
題目敘述 題目會給我們一個輸入陣列nums,和一個指定的k值。 請問,在輸入陣列nums中,有幾個子陣列的元素總合恰好為k ? 例如: nums = [1,2,3], k = 3 則有兩個子陣列的元素總合為3,分別是[1,2] 和 [3] 如果是第一次聽到或接觸前綴和prefix的同學
Thumbnail
題目敘述 題目會給定我們一個字串s,和一組字庫wordDict。 問我們能不能透過字串串接的方式,從字庫裡面的字拼成原本的字串s? 可以的話,返回True。 無解的話,返回False。 註: 題目還允許重複使用字庫裡面的字去串接。
Thumbnail
這題也是滿經典的DP動態規劃教學案例和題目,就順便複習一下吧。 題目敘述 題目會給我們兩個字串text1, text2。 要求我們找出兩個字串的最長共同子序列,並且返回最長共同子序列的長度。 如果彼此沒有共同子序列,則返回0。 題目的原文敘述 測試範例 Example 1: In
Thumbnail
題目敘述 題目會給定我們兩個字串word1 和 word2。 允許我們不限制次數進行下列兩種操作: 任意調換其中兩個字元的位置。 把字串中的某個字元全部置換成另一個字元,同時把另一個字元同時置換成某個字元。(例如把字串中原本的a都換成b,把原本的b都換成a) 問我們能不能通過上述兩項操作,
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
給定兩個輸入整數陣列, 若在兩個陣列遇到相同的數字可以連成一線, 但是有規定連線不可和別的連線有交叉, 請問最多可以形成幾條連線? 解答中探討了演算法化簡的技巧和DP模型, 可以透過演算法化簡的技巧, 把這題映射到原本已經學會的Longest Common Subsequence的DP模型來解開。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
給定一個字串s,以s擁有的字元製造迴文字串。要能製造出的迴文字串長度最長是多少,觀察迴文字串不外乎兩種模式對稱部分 + 核心字元 + 對稱部分,其中,核心字元在正中央出現一次,或者 對稱部分 + 對稱部分。使用演算法統計出現次數並推理出盡可能充分利用每個字元的迴文字串製造方法。
Thumbnail
Append Characters to String to Make Subsequence 給定兩個字串s和字串t。 請計算最少的字元串接數量是多少,串接在s的尾端,使得t是s的子序列。 測試範例 Example 1: Input: s = "coaching", t =
Thumbnail
題目敘述 題目會給我們一個輸入陣列nums,和一個指定的k值。 請問,在輸入陣列nums中,有幾個子陣列的元素總合恰好為k ? 例如: nums = [1,2,3], k = 3 則有兩個子陣列的元素總合為3,分別是[1,2] 和 [3] 如果是第一次聽到或接觸前綴和prefix的同學
Thumbnail
題目敘述 題目會給定我們一個字串s,和一組字庫wordDict。 問我們能不能透過字串串接的方式,從字庫裡面的字拼成原本的字串s? 可以的話,返回True。 無解的話,返回False。 註: 題目還允許重複使用字庫裡面的字去串接。
Thumbnail
這題也是滿經典的DP動態規劃教學案例和題目,就順便複習一下吧。 題目敘述 題目會給我們兩個字串text1, text2。 要求我們找出兩個字串的最長共同子序列,並且返回最長共同子序列的長度。 如果彼此沒有共同子序列,則返回0。 題目的原文敘述 測試範例 Example 1: In
Thumbnail
題目敘述 題目會給定我們兩個字串word1 和 word2。 允許我們不限制次數進行下列兩種操作: 任意調換其中兩個字元的位置。 把字串中的某個字元全部置換成另一個字元,同時把另一個字元同時置換成某個字元。(例如把字串中原本的a都換成b,把原本的b都換成a) 問我們能不能通過上述兩項操作,