如何在 Go 中實現各種類型的鏈表?

鏈表是動態內存分配中最常見的數據結構之一。它由一組有限的元素組成,每個元素(節點)至少占用兩塊內存:一塊用于存放數據,另一塊用于存放指向下一個節點的指針。本文教程將說明在 Go 語言中如何借助指針和結構體類型來實現各種鏈表

Go 中的數據結構

隨機存取存儲器(RAM)可以想象成一張由許多地址單元組成的表格、矩陣或網格。為了在這張表中存放數據,Go 程序員必須先把內存劃分成可定位的結構,并為它們起一個便于識別的名字——變量名。需要注意的是,變量名只是為了方便程序員閱讀;編譯后,名字會被實際的內存引用(例如 0x78BA 這樣的地址)替換

最簡單的情況下,變量名只對應單個內存單元;復雜時,它可以代表一段連續空間,或是具有行和列的二維區域,也就是數組。數組可通過下標尋址,例如array_name[2][4]表示第二行第四列的元素。

再復雜一些,數據元素之間的結構關系可能并非連續存儲,而是隨機分布,比如用來表示層級關系的樹、分支結構,或含有多重連接的復雜網絡結構。

因此,為了存儲這些結構化關系,Go 開發者必須根據具體需求自行設計內存布局與訪問策略。

靜態 vs. 動態內存分配

在內存分配中,有兩個關鍵特性:靜態和動態。

  • 靜態數據結構的大小和存儲位置在編譯期就已確定;
  • 動態數據結構的大小和位置則未預定義,而是在運行時決定。

舉例來說,當 Go 開發者聲明一個數組時,需要提前給出固定長度,這樣編譯器才能在你使用下標時準確地定位內存地址。而在動態數據結構(例如鏈表)中,下一個數據節點的地址只有在程序執行、節點被創建時才會確定,因此整個結構可在運行期間自由增長或收縮。由于靜態結構存放在連續內存中,元素呈線性排列;動態結構則無此限制。

眾多動態數據結構的基礎——雖然動態分配并不限于此——就是鏈表。鏈表的各數據節點散布在內存的任意位置,通過指針相互連接。因此,一個鏈表節點至少包含兩部分:

  1. 存放實際數據的元素
  2. 指向下一節點的鏈接

順序存儲與鏈式存儲對比

與順序存儲結構(如數組)不同,鏈式存儲除了保存數據本身,還需要額外的內存來存放指向下一節點的鏈接。這在某些場景下會增加開銷,但鏈式存儲帶來的靈活性通常更具優勢。比如,數組的內存大小在創建時就固定,因此可能出現大量未被利用的空間;而鏈表只有在需要時才創建節點,不會浪費內存。

在鏈表中刪除元素非常容易,而順序存儲往往要移動大量數據才能完成刪除。同樣,鏈表插入元素也很高效。不過,如果要隨機訪問某個位置的元素,順序存儲則更快。

兩種存儲方式各有利弊,Go 程序員應根據具體需求選擇合適的數據結構。

鏈表的 4 種基本形態

鏈表在內存中的組織方式主要有四種:單向(線性)、循環、雙向以及雙向循環。

  • 單向(線性)鏈表:只有一個 next 指針指向下一個節點;最后一個節點的 nextnil。遍歷時一旦遇到 nil 就表示到達鏈表末尾;
  • 循環鏈表:結構與單向鏈表相同,但最后一個節點的 next 指向頭節點,因此尾部再向后訪問就回到起點,可形成“環形”遍歷;
  • 雙向鏈表:每個節點同時擁有 prevnext 兩個指針,分別指向前驅和后繼節點。這樣即可正向也可反向遍歷,查找元素更靈活;
  • 雙向循環鏈表:在雙向鏈表的基礎上,讓尾節點的 next 指向頭節點,頭節點的 prev 指向尾節點,于是可以向前或向后進行環形遍歷。

從單向到雙向、從線性到循環,鏈表的靈活性依次增強。下面的示例將演示在 Go 中實現這幾種鏈表(示例僅涵蓋鏈表的創建與遍歷,以保持簡潔)。

一、單向鏈表示例

下面是一個在 Go 中創建單向鏈表的示例:

package mainimport ("fmt""math/rand"
)type Node struct {info interface{}next *Node
}type List struct {head *Node
}func (l *List) Insert(d interface{}) {node := &Node{info: d}if l.head == nil {l.head = nodereturn}p := l.headfor p.next != nil {p = p.next}p.next = node
}func Show(l *List) {for p := l.head; p != nil; p = p.next {fmt.Printf("-> %v ", p.info)}
}func main() {sl := List{}for i := 0; i < 5; i++ {sl.Insert(rand.Intn(100))}Show(&sl)
}

示例輸出:

-> 81 -> 87 -> 47 -> 59 -> 81

二、循環單向鏈表

我們可以輕松地把單向鏈表轉換為循環鏈表。無需修改上述代碼,只需再添加兩個函數:ConvertSinglyToCircularShowCircular,并在 main 函數中調用它們即可。以下是這兩個函數:

func ConvertSinglyToCircular(l *List) {if l.head == nil {return}p := l.headfor p.next != nil {p = p.next}p.next = l.head
}func ShowCircular(l *List) {p := l.headfor {fmt.Printf("-> %v ", p.info)if p.next == l.head {break}p = p.next}
}

注意:雖然假設該鏈表已經是循環的(即 p.next 最終會指回 l.head),但如果 l.headnil(空鏈表),此函數將發生空指針解引用錯誤并崩潰。

現在,在 main 函數中按如下方式調用這兩個函數:

func main() {sl := List{}for i := 0; i < 5; i++ {sl.Insert(rand.Intn(100))}ConvertSinglyToCircular(&sl)ShowCircular(&sl)
}

三、雙向鏈表示例

下面是一個演示如何在 Go 中創建雙向鏈表的代碼示例:

package mainimport ("fmt""math/rand""time"
)type Node struct {info interface{}prev *Nodenext *Node
}type List struct {head *Nodetail *Node
}func (l *List) Insert(d interface{}) {node := &Node{info: d}if l.head == nil {l.head, l.tail = node, nodereturn}l.tail.next = nodenode.prev = l.taill.tail = node
}func Show(l *List) {for p := l.head; p != nil; p = p.next {fmt.Printf("-> %v ", p.info)}
}func ReverseShow(l *List) {for r := l.tail; r != nil; r = r.prev {fmt.Printf("-> %v ", r.info)}
}func main() {sl := List{}rnd := rand.New(rand.NewSource(time.Now().UnixNano()))for i := 0; i < 10; i++ {sl.Insert(rnd.Intn(100))}Show(&sl)fmt.Println("\n----------------------------")ReverseShow(&sl)
}

示例輸出:

-> 11 -> 17 -> 56 -> 71 -> 39 -> 44 -> 18 -> 78 -> 25 -> 19 
----------------------------
-> 19 -> 25 -> 78 -> 18 -> 44 -> 39 -> 71 -> 56 -> 17 -> 11

四、雙向循環鏈表

與循環鏈表類似,雙向循環鏈表也可以很容易地由雙向鏈表轉換而來。我們只需在上述代碼中再添加兩個函數即可。其余代碼保持不變,只需在 main 函數中進行輕微修改,就像在前面的循環鏈表示例中所做的那樣:

func ConvertDoublyToDoublyCircular(l *List) {if l.head == nil || l.tail == nil {return}l.head.prev = l.taill.tail.next = l.head
}func ShowDoublyCircular(l *List) {p := l.headfor {fmt.Printf("-> %v ", p.info)if p.next == l.head {break}p = p.next}
}func ReverseShowDoublyCircular(l *List) {r := l.tailfor {fmt.Printf("-> %v ", r.info)if r.prev == l.tail {break}r = r.prev}
}

main 示例:

func main() {sl := List{}rnd := rand.New(rand.NewSource(time.Now().UnixNano()))for i := 0; i < 10; i++ {sl.Insert(rnd.Intn(100))}ConvertDoublyToDoublyCircular(&sl)ShowDoublyCircular(&sl)fmt.Println("\n----------------------------")ReverseShowDoublyCircular(&sl)
}

關于在 Go 中實現鏈表的最后思考

正如我們所見,在 Go 語言中實現鏈表相當簡單。鏈式分配可以用來表示各種類型的數據——無論是單個值,還是擁有眾多字段的復雜數據結構。

當配合指針進行順序查找時,訪問速度非常快。與鏈表相關的優化技巧也有不少。與單向鏈表相比,雙向鏈表效率更高,而且能夠在兩個方向上快速遍歷。

  • 知識星球:云原生AI實戰營。10+ 高質量體系課( Go、云原生、AI Infra)、15+ 實戰項目,P8 技術專家助你提高技術天花板,入大廠拿高薪;
  • 公眾號:令飛編程,分享 Go、云原生、AI Infra 相關技術。回復「資料」免費下載 Go、云原生、AI 等學習資料;
  • 嗶哩嗶哩:令飛編程 ,分享技術、職場、面經等,并有免費直播課「云原生AI高新就業課」,大廠級項目實戰到大廠面試通關;

例行海報

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81213.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81213.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81213.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

新一代機載相控陣雷達的發展

相控陣雷達以其優越的性能在軍事領域中有著廣闊的應用前景&#xff0c;但由于復雜的技術、昂貴的造價使其應用范圍還存在一定的局限性。然而&#xff0c;國內外對相控陣技術的研究非常重視&#xff0c;并取得了豐碩的成果。 軍用相控陣雷達主要分為陸基、海基和空基幾種類型。 …

多數元素題解(LC:169)

169. 多數元素 核心思想&#xff08;Boyer-Moore 投票算法&#xff09;&#xff1a; 解題思路&#xff1a;可以使用 Boyer-Moore 投票算法、該算法的核心思想是&#xff1a; 維護一個候選元素和計數器、初始時計數器為 0。 遍歷數組&#xff1a; 當計數器為 0 時、設置當前元…

數據庫 AI 助手測評:Chat2DB、SQLFlow 等工具如何提升開發效率?

一、引言:數據庫開發的 “效率革命” 正在發生 在某互聯網金融公司的凌晨故障現場,資深 DBA 正滿頭大汗地排查一條執行超時的 SQL—— 該語句涉及 7 張核心業務表的復雜關聯,因索引缺失導致全表掃描,最終引發交易系統阻塞。這類場景在傳統數據庫開發中屢見不鮮:據 Gartne…

【中間件】bthread效率為什么高?

bthread效率為什么更高&#xff1f; 1 基本概念 bthread是brpc中的用戶態線程&#xff08;也可稱為M:N線程庫&#xff09;&#xff0c;目的是&#xff1a;提高程序的并發度&#xff0c;同時降低編碼難度&#xff0c;在多核cpu上提供更好的scalability和cache locality。其采用…

DeepSeek V2:引入MLA機制與指令對齊

長上下文革命:Multi-Head Latent Attention(MLA)機制 傳統 Transformer 的多頭注意力需要緩存所有輸入token的 Key 和 Value,這對長文本推理時的內存開銷極為龐大。DeepSeek V2 針對這一難題提出了“Multi-Head Latent Attention”(MLA)機制。MLA 的核心思想是對多頭注意…

Druid監控sql導致的內存溢出--內存分析工具MemoryAnalyzer(mat)

問題 druid監控sql在網頁端顯示&#xff0c;我的服務插入sql比較大&#xff0c;druid把執行過的sql保存在DruidDataSource類的成員變量JdbcDataSourceStat dataSourceStat&#xff1b; JdbcDataSourceStat類中的LinkedHashMap<String, JdbcSqlStat> sqlStatMap中&#…

《Python實戰進階》No45:性能分析工具 cProfile 與 line_profiler

Python實戰進階 No45&#xff1a;性能分析工具 cProfile 與 line_profiler 摘要 在AI模型開發中&#xff0c;代碼性能直接影響訓練效率和資源消耗。本節通過cProfile和line_profiler工具&#xff0c;實戰演示如何定位Python代碼中的性能瓶頸&#xff0c;并結合NumPy向量化操作…

計算機操作系統知識集合

主要來自小林coding 硬件結構 cpu位寬 如果用 32 位 CPU 去加和兩個 64 位大小的數字&#xff0c;就需要把這 2 個 64 位的數字分成 2 個低位 32 位數字和 2 個高位 32 位數字來計算&#xff0c;先加個兩個低位的 32 位數字&#xff0c;算出進位&#xff0c;然后加和兩個高位…

電機常用易混淆概念說明(伺服、舵機、多輪)

1. 概述 基礎動力需求 &#xff1a;普通電機&#xff08;如水泵、風扇&#xff09;。 高精度控制 &#xff1a;優先伺服系統或伺服電機&#xff08;如數控機床&#xff09;。 微型化場景 &#xff1a;舵機&#xff08;如遙控模型&#xff09;。 移動底盤 &#xff1a;單舵輪成…

進程與線程:04 內核線程

內核級線程概述 上一講我們學習了用戶級線程&#xff0c;了解了其切換和創建方式。用戶級線程切換核心在于從一個棧變為兩個棧&#xff0c;每個線程有自己的棧和線程控制塊&#xff08;tcb&#xff09;&#xff0c;切換時先切換tcb再切換棧&#xff0c;創建時將切換的pc指針放…

信息系統項目管理師-軟考高級(軟考高項)???????????2025最新(六)

個人筆記整理---僅供參考 第六章項目管理概論 6.1PMBOK的發展 6.2項目基本要素 組織過程資產指的是項目上的&#xff0c;國產數據庫的使用----安保和安全指的是環境因素 6.3項目經理的角色 6.4價值驅動的項目管理知識體系

[藍橋杯 2023 國 Python B] 劃分 Java

import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[] arr new int[41];int sum 0;for (int i 1; i < 40; i) {arr[i] sc.nextInt();sum arr[i];}sc.close();int target sum / 2; // 最接近的兩…

Redis05-進階-主從

零、文章目錄 Redis05-進階-主從 1、搭建主從架構 &#xff08;1&#xff09;概述 單節點Redis的并發能力是有上限的&#xff0c;要進一步提高Redis的并發能力&#xff0c;就需要搭建主從集群&#xff0c;實現讀寫分離。 &#xff08;2&#xff09;集群概況 我們搭建的主從…

小結:ipsec-ike

IPSec 手動配置與自動配置&#xff08;IKE動態協商&#xff09; 手動配置IPSec 邏輯圖 #mermaid-svg-eNMnNEwnoTjF8fkV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-eNMnNEwnoTjF8fkV .error-icon{fill:#552222;}…

瀟灑郎: 100% 成功搭建Docker私有鏡像倉庫并管理、刪除鏡像

1、Registry Web管理界面 2、拉取Registry-Web鏡像 創建配置文件 tee /opt/zwx-registry/web-config.yml <<-EOF registry:url: http://172.28.73.90:8010/v2name: registryreadonly: falseauth:enabled: false EOF 拉取docker-registry-web鏡像并綁定Registry倉庫 …

《機器學習中的過擬合與模型復雜性:理解與應對策略》

《機器學習中的過擬合與模型復雜性&#xff1a;理解與應對策略》 摘要 在機器學習中&#xff0c;過擬合是模型在訓練數據上表現良好但在新數據上泛化能力差的現象。本文深入探討了過擬合與模型復雜性之間的關系&#xff0c;分析了復雜模型導致過擬合的原因&#xff0c;并介紹…

linux中sigint和sigterm的區別

SIGINT 和 SIGTERM 是在 Unix 及類 Unix 系統&#xff08;包括 Linux&#xff09;中用于進程間通信的信號&#xff0c;它們都可以用于請求進程終止&#xff0c;區別如下&#xff1a; 1、信號編號與定義 在信號機制里&#xff0c;每個信號都有對應的編號&#xff0c;這便于系統…

一套SaaS ERP管理系統源碼,支持項目二開商用,SpringBoot+Vue+ElementUI+UniAPP

ERP管理系統源碼&#xff0c;一款適用于小微企業的SaaS ERP管理系統源碼, 采用最新的技術棧開發(SpringBootVueElementUIUniAPP)&#xff0c;讓企業簡單上云。 專注于小微企業的應用需求&#xff0c;如企業基本的進銷存、詢價&#xff0c;報價, 采購、銷售、MRP生產制造、品質…

2025 新生 DL-FWI 培訓

摘要: 本貼給出 8 次討論式培訓的提綱, 每次培訓 1 小時. 1. Basic concepts 主動學習: 提問, 理解, 繼續追問. 通過不斷迭代, 逐步提升問題的質量, 加深理解. 1.1 Seismic exploration 問 DeepSeek (下同): 為什么進行地震勘探? 問: 地震勘探一般的深度是多少? 1.2 Sesmi…

mac電腦pytest生成測試報告

時隔了好久再寫代碼&#xff0c;感覺我之前的積累都白費了&#xff0c;全部忘記了&#xff0c;看來每一步都有記錄對于我來說才是最好的。 最近又要重新搞接口自動化&#xff0c;然而是在mac電腦&#xff0c;對于我長期使用windows的人來說真的是個考驗&#xff0c;對此次過程…