Go源碼學習之雙向鏈表

來源:互聯網
上載者:User

雙向鏈表的定義

雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個資料結點中都有兩個指標,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。一般我們都構造雙向迴圈鏈表。

這裡記錄一下自己學習理解的過程

圖解

[圖片上傳失敗...(image-afe880-1531019243291)]

Go的源碼實現

1.首先看一下鏈表中儲存的元素(Element)的定義:

// 雙向鏈表的一個元素 type Element struct {  // 前驅指標和後繼指標  prev, next *Element  // 該元素屬於哪個鏈表list  list *List  // 該元素儲存的值  Value interface{} }

2.為Element這個結構體定義兩個方法:

// Next 返回元素e的後一個元素 func (e *Element) Next() *Element {  if p := e.next; e.list != nil && &e.list.root != p {    return p  }  return nil }// Prev 返回元素e的前一個元素 func (e *Element) Prev() *Element {  if p := e.prev; e.list != nil && &e.list.root != p {    return p  }  return nil }

3.再看鏈表list的定義:

// List 代表一個雙向鏈表 // List的零值是一個空的列表 type List struct {  // 根節點  root Element    // 當前鏈表的長度  len int }

4.為鏈表List定義一個初始化方法

// Init 初始化一個鏈表,或者重設一個鏈表 func (l *List) Init() (*List) {  l.root.prev = &l.root  l.root.next = &l.root  l.len = 0  return l }

5.為鏈表List定義一個Factory 方法,用來產生一個鏈表:

func New() *List {  return new(List).Init() }

6.下面看鏈表核心的兩個方法:插入和刪除,鏈表的其他動作方式基本都是基於這兩個方法

// insert 在元素at後面插入元素e,將list的長度遞增,返回該元素 func (l *List) insert(e, at *Element) *Element {  n := at.next  at.next = e  e.prev = at  e.next = n  n.prev = e  e.list = l  l.len ++  return e }// remove 從雙向鏈表中移除一個元素e,遞減鏈表的長度,返回該元素e func (l *List) remove(e *Element) *Element {  e.prev.next = e.next  e.next.prev = e.prev  e.next = nil // 防止記憶體流失  e.prev = nil // 防止記憶體流失  e.list = nil  l.len --  return e }
doubly linked list insert operate

插入操作:

  • 先將元素b的後繼指標和元素c的前驅指標刪除
  • 然後將元素b的後繼指標指向新元素new,將新元素new 的前驅指標指向元素b
  • 再講元素c的前驅指標指向新元素new,將新元素new 的後繼指標指向元素c
  • 最後將鏈表長度加一
double linked list remove operate

刪除操作:

  • 現將元素b的後繼指標指向元素d,將元素d的前驅指標指向b
  • 再講元素c的前驅指標和後繼指標刪除
  • 將鏈表長度減一

7.理解了鏈表的插入和刪除操作,就可以在此基礎上封裝出豐富的鏈表操作函數:

// insertValue 是對l.insert(&Element{Value:v}, at)的封裝func (l *List) insertValue(v interface{}, at *Element) *Element {    return l.insert(&Element{Value: v}, at)}// Remove 如果元素e是鏈表l的一個元素,則移除e// 返回元素e的值e.Value// 該元素e不能為nilfunc (l *List) Remove(e *Element) interface{} {    if e.list == l {        l.remove(e)    }    return e.Value}

在鏈表頭部或尾部插入元素:

// PushFront 插入一個包含值v的新元素e到鏈表l的頭部,並返回該元素efunc (l *List) PushFront(v interface{}) *Element {    l.lazyInit()    return l.insertValue(v, &l.root)}// PushBack 插入一個包含值v的新元素e到鏈表l的尾部,並返回這個新元素efunc (l *List) PushBack(v interface{}) *Element {    l.lazyInit()    return l.insertValue(v, l.root.prev)}

在某個元素之前或之後插入一個新元素:

// InsertBefore 在元素mark之前插入一個值為v的新元素// 如果mark不屬於鏈表l,則不會更新鏈表l,mark也不能為nilfunc (l *List) InsertBefore(v interface{}, mark *Element) *Element {    if mark.list != nil {        return nil    }    return l.insertValue(v, mark.prev)}// InsertAfter 在元素mark之後插入一個值為v的新元素// 如果mark不屬於鏈表l,則不會更新鏈表l,mark也不能為nilfunc (l *List) InsertAfter(v interface{}, mark *Element) *Element {    if mark.list != nil {        return nil    }    return l.insertValue(v, mark)}

將某個元素移動到鏈表頭部或尾部:

// MoveToFront 將元素e移動到鏈表頭部// 如果元素e不是鏈表的元素,則不會更新鏈表func (l *List) MoveToFront(e *Element) {    if e.list != l || l.root.next == e {        return    }    l.insert(l.remove(e), &l.root)}// MoveToBack 將元素e移動到鏈表尾部// 如果元素e不是鏈表的元素,則不會更新鏈表func (l *List) MoveToBack(e *Element) {    if e.list != l || e == l.root.prev {        return    }    l.insert(l.remove(e), l.root.prev)}

將元素A移動到元素B之前 或 之後:

// MoveBefore 移動元素e到元素mark之前func (l *List) MoveBefore(e, mark *Element) {    if e.list != l || mark.list != l || e == mark {        return    }    l.insert(l.remove(e), mark.prev)}// MoveAfter 移動元素e到元素mark之後func (l *List) MoveAfter(e, mark *Element) {    if e.list != l || e == mark || mark.list != l {        return    }    l.insert(l.remove(e), mark)}

上述代碼均來自golang源碼,詳見Go Doc

總結

雙向鏈表並不難理解,只要了理解了其資料結構和插入、刪除的原理,就能迅速掌握。

參考:

  • Doubly linked list - wikipedia
  • container/list - Go Doc
  • 數組、單鏈表和雙鏈表介紹 以及 雙向鏈表的C/C++/Java實現

原文地址:https://popwalker.github.io/article/c78375f9/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.