雙向鏈表也叫雙鏈表,是鏈表的一種,它的每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。因此,從雙向鏈表中的任意一個結點開始,均可以很方便地訪問它的前驅結點和後繼結點。通常咱們都構造雙向循環鏈表。html
這裏記錄一下本身學習理解的過程git
1.首先看一下鏈表中存儲的元素(Element)的定義:github
// 雙向鏈表的一個元素 type Element struct { // 前驅指針和後繼指針 prev, next *Element // 該元素屬於哪一個鏈表list list *List // 該元素存儲的值 Value interface{} } 複製代碼
2.爲Element這個結構體定義兩個方法:golang
// 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定義一個初始化方法markdown
// Init 初始化一個鏈表,或者重置一個鏈表 func (l *List) Init() (*List) { l.root.prev = &l.root l.root.next = &l.root l.len = 0 return l } 複製代碼
5.爲鏈表List定義一個工廠方法,用來生成一個鏈表:數據結構
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 } 複製代碼
插入操做:oop
刪除操做:學習
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不能爲nil func (l *List) Remove(e *Element) interface{} { if e.list == l { l.remove(e) } return e.Value } 複製代碼
在鏈表頭部或尾部插入元素:
// PushFront 插入一個包含值v的新元素e到鏈表l的頭部,並返回該元素e func (l *List) PushFront(v interface{}) *Element { l.lazyInit() return l.insertValue(v, &l.root) } // PushBack 插入一個包含值v的新元素e到鏈表l的尾部,並返回這個新元素e func (l *List) PushBack(v interface{}) *Element { l.lazyInit() return l.insertValue(v, l.root.prev) } 複製代碼
在某個元素以前或以後插入一個新元素:
// InsertBefore 在元素mark以前插入一個值爲v的新元素 // 若是mark不屬於鏈表l,則不會更新鏈表l,mark也不能爲nil func (l *List) InsertBefore(v interface{}, mark *Element) *Element { if mark.list != l { return nil } return l.insertValue(v, mark.prev) } // InsertAfter 在元素mark以後插入一個值爲v的新元素 // 若是mark不屬於鏈表l,則不會更新鏈表l,mark也不能爲nil func (l *List) InsertAfter(v interface{}, mark *Element) *Element { if mark.list != l { 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
雙向鏈表並不難理解,只要了理解了其數據結構和插入、刪除的原理,就能迅速掌握。
參考: