Golang數據結構 - 4 - 鏈表

 __       __  .__   __.  __  ___  _______  _______      __       __       _______.___________.
|  |     |  | |  \ |  | |  |/  / |   ____||       \    |  |     |  |     /       |           |
|  |     |  | |   \|  | |  '  /  |  |__   |  .--.  |   |  |     |  |    |   (----`---|  |----`
|  |     |  | |  . `  | |    <   |   __|  |  |  |  |   |  |     |  |     \   \       |  |     
|  `----.|  | |  |\   | |  .  \  |  |____ |  '--'  |   |  `----.|  | .----)   |      |  |     
|_______||__| |__| \__| |__|\__\ |_______||_______/    |_______||__| |_______/       |__|     

上一章中我們學習了隊列以及相關的基本操作,并有數組切片和鏈表兩種不同的實現方式,本章我們將對鏈表進行單獨介紹。

如果我們需要存儲操作一系列的數據,使用數組(或列表)是最常用的數據結構。但是在大多數編程語言中數組的長度是固定的(雖然Go有切片),如果我們使用數組存取數據,遇到需要移動數據的操作其實成本是很高的,有時候也會碰到浪費大量內存的情況。使用鏈表可以解決這些問題。

本章內容包括:

  • 鏈表數據結構
  • 向鏈表添加元素
  • 從鏈表移除元素
  • 雙向鏈表
  • 循環鏈表
  • 排序鏈表
  • 通過鏈表實現棧

鏈表定義

鏈表主要包含以下接口:

  • Push(e ...Element):向鏈表尾部添加若干新元素
  • Insert(element Element, position int):在指定位置插入元素
  • GetElementAt(index int) Element:獲取特定位置元素
  • Remove(element Element):刪除特定元素
  • RemoveAt(position int):刪除特定位置元素
  • IndexOf(element Element) int:返回特定元素索引
  • Size() int:返回鏈表包含元素個數
  • IsEmpty() bool:若鏈表為空返回 true
type Element interface {
}

type List interface {

    // 向鏈表尾部添加若干新元素
    Push(e ...Element)

    // 在指定位置插入元素
    Insert(element Element, position int)

    // 獲取特定位置元素
    GetElementAt(index int) Element

    // 刪除特定元素
    Remove(element Element)
    RemoveAt(position int)

    // 返回特定元素索引
    IndexOf(element Element) int

    // 返回鏈表包含元素個數
    Size() int

    // 若鏈表為空返回 true
    IsEmpty() bool
}

type Node struct {
    Value Element
    Next  *Node
}

type LinkedList struct {
    Head *Node
    size int
}

向鏈表尾部添加元素

// 在末端插入若干新元素
func (s *LinkedList) Push(e ...Element) {
    iter := s.Head
    for _, v := range e {
        node := &Node{
            Value: v,
            Next:  nil,
        }
        for iter.Next != nil {
            iter = iter.Next
        }
        // 此時iter為最后一項
        iter.Next = node
        s.size++
    }
}

向鏈表指定位置插入元素

// 在指定位置插入新元素
func (s *LinkedList) Insert(element Element, position int) {
    if position > s.Size()-1 {
        return
    }
    iter := s.Head
    for step := position; step > 0; step-- {
        iter = iter.Next
    }
    node := &Node{
        Value: element,
        Next:  iter.Next,
    }
    iter.Next = node
    s.size++
}

從鏈表中移除元素

// 刪除指定元素
func (s *LinkedList) Remove(element Element) {
    last := s.Head
    for iter := s.Head; iter.Next != nil; iter = iter.Next {
        if iter.Value == element {
            last.Next = iter.Next
            return
        }
        last = iter
    }
}

// 刪除指定位置的元素
func (s *LinkedList) RemoveAt(position int) {
    if position > s.Size()-1 {
        return
    }
    iter := s.Head
    last := s.Head
    for step := position; step > 0; step-- {
        last = iter
        iter = iter.Next
    }
    last.Next = iter.Next
    s.size--
}

IsEmpty方法和Size方法

// 鏈表大小
func (s *LinkedList) Size() int {
    return s.size
}

// 鏈表是否為空
func (s *LinkedList) IsEmpty() bool {
    return s.Size() == 0
}

完整實現

package main

import "fmt"

type Element interface {
}

type List interface {

    // 向鏈表尾部添加若干新元素
    Push(e ...Element)

    // 在指定位置插入元素
    Insert(element Element, position int)

    // 獲取特定位置元素
    GetElementAt(index int) Element

    // 刪除特定元素
    Remove(element Element)
    RemoveAt(position int)

    // 返回特定元素索引
    IndexOf(element Element) int

    // 返回鏈表包含元素個數
    Size() int

    // 若鏈表為空返回 true
    IsEmpty() bool
}

type Node struct {
    Value Element
    Next  *Node
}

type LinkedList struct {
    Head *Node
    size int
}

// 在末端插入若干新元素
func (s *LinkedList) Push(e ...Element) {
    iter := s.Head
    for _, v := range e {
        node := &Node{
            Value: v,
            Next:  nil,
        }
        for iter.Next != nil {
            iter = iter.Next
        }
        // 此時iter為最后一項
        iter.Next = node
        s.size++
    }
}

// 在指定位置插入新元素
func (s *LinkedList) Insert(element Element, position int) {
    if position > s.Size()-1 {
        return
    }
    iter := s.Head
    for step := position; step > 0; step-- {
        iter = iter.Next
    }
    node := &Node{
        Value: element,
        Next:  iter.Next,
    }
    iter.Next = node
    s.size++
}

// 返回指定位置的元素
func (s *LinkedList) GetElementAt(index int) Element {
    if index > s.Size()-1 {
        return nil
    }
    iter := s.Head
    for step := index; step > -1; step-- {
        iter = iter.Next
    }
    return iter.Value
}

// 刪除指定元素
func (s *LinkedList) Remove(element Element) {
    last := s.Head
    for iter := s.Head; iter.Next != nil; iter = iter.Next {
        if iter.Value == element {
            last.Next = iter.Next
            return
        }
        last = iter
    }
}

// 刪除指定位置的元素
func (s *LinkedList) RemoveAt(position int) {
    if position > s.Size()-1 {
        return
    }
    iter := s.Head
    last := s.Head
    for step := position; step > 0; step-- {
        last = iter
        iter = iter.Next
    }
    last.Next = iter.Next
    s.size--
}

// 獲取指定元素位置
func (s *LinkedList) IndexOf(element Element) int {
    idx := -1
    iter := s.Head
    for iter.Next != nil {
        idx++
        iter = iter.Next
        if iter.Value == element {
            return idx
        }
    }
    return -1
}

// 鏈表大小
func (s *LinkedList) Size() int {
    return s.size
}

// 鏈表是否為空
func (s *LinkedList) IsEmpty() bool {
    return s.Size() == 0
}

func NewLinkedList() List {
    return &LinkedList{
        Head: &Node{},
        size: 0,
    }
}

func main() {
    list := NewLinkedList()

    fmt.Println("Push:1,2,3,4,5,6,7,8,9")
    list.Push(1, 2, 3, 4, 5, 6, 7, 8, 9)
    fmt.Println("Size:", list.Size())

    fmt.Println("Get idx=8:", list.GetElementAt(8))

    fmt.Println("Index of 9:", list.IndexOf(1))

    fmt.Println("Insert 20 at 3")
    list.Insert(20, 3)
    fmt.Println("Get idx=3:", list.GetElementAt(3))

    fmt.Println("Remove idx=3: done")
    list.RemoveAt(3)
    fmt.Println("Get idx=3:", list.GetElementAt(3))

    list.Remove(4)
    fmt.Println("Remove element=4: done")

    fmt.Println("Get idx=3:", list.GetElementAt(3))

}

運行結果

Push:1,2,3,4,5,6,7,8,9
Size: 9
Get idx=8: 9
Index of 9: 0
Insert 20 at 3
Get idx=3: 20
Remove idx=3: done
Get idx=3: 4
Remove element=4: done
Get idx=3: 5

Process finished with exit code 0

雙向鏈表

循環鏈表

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380