Container/list-golang-標準庫閱讀

鏈表實現的雙向鏈表git

主要的 struct 有兩個, 雙向鏈表 ListElement (Node)github

type Element struct {
    next, prev *Element

    list *List

    Value interface{}
}

type List struct {
    root Element // list 的 哨兵
    len  int
}

複製代碼

Golang 的 List 是存在哨兵的, 它自帶一個 root, 做爲起始ui

咱們從 New 方法開始spa

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

func (l *List) Init() *List {
    l.root.next = &l.root
    l.root.prev = &l.root
    l.len = 0
    return l
}
複製代碼

使用 new 關鍵字給 list 分配內存, 而後調用 Init 初始化哨兵, 講哨兵的先後指針都指向本身, 並初始化 listlen.設計

哨兵先後都指向本身, 作成一個環, 而後能夠馬上拿到本身的頭和尾, 這個設計真的很棒指針

insert

func (l *List) insert(e, at *Element) *Element {
    /** 這樣在判斷的時候會認爲是指向了 nil 且這樣會很容易的找到尾巴 ┌----------┐ ∨ ∨ root ----> at ^ to e OR ┌---------------------------┐ ∨ ∨ root ----> at <---> b <---> c ^ to e */

    /** n | root <---> at <---> b <---> c e */
	n := at.next

	/** n | root ----> at <---- b <---> c | | └------->e */
	at.next = e

	/** n | root ----> at <---- b <---> c |↑ |└-------┐ └------->e */
	e.prev = at

	/** ┌---------┐ n | | | root ----> at <---- b <---> c | |↑ | |└-------┐ | └------->e---------┘ */
	e.next = n
	/** ┌---------┐ n-┤ | | ↓ | root ----> at | b <---> c | |↑ └--┐ | |└-------┐↓ | └------->e---------┘ */
	n.prev = e

	e.list = l
	l.len++
	return e
}
複製代碼

remove

func (l *List) remove(e *Element) *Element {
    /** ┌---------------------------┐ ∨ ∨ root ----> at <---> e <---> c */

    /** ┌---------------------------┐ ∨ ∨ root ----> at ------------> c ^ ^ └------- e <-----┘ */
	e.prev.next = e.next

    /** ┌---------------------------┐ ∨ ∨ root ----> at <-----------> c ^ ^ └------- e ------┘ */
	e.next.prev = e.prev
	/** ┌---------------------------┐ ∨ ∨ root ----> at <-----------> c ^ └------- e */
	e.next = nil // avoid memory leaks
	/** ┌---------------------------┐ ∨ ∨ root ----> at <-----------> c e */
	e.prev = nil // avoid memory leaks
	e.list = nil
	l.len--
	return e
}
複製代碼

move

func (l *List) move(e, at *Element) *Element {
    /** ┌-----------------------------------┐ ∨ ∨ root ----> at <---> c <---> e <---> d */
	if e == at {
		return e
	}
    /** ┌-----------------------------------┐ ∨ ∨ root ----> at <---> c ------------> d ^ ^ └------ e <-----┘ */
	e.prev.next = e.next
    /** ┌-----------------------------------┐ ∨ ∨ root ----> at <---> c <-----------> d ^ ^ └------ e ------┘ */
	e.next.prev = e.prev

    /** ┌-----------------------------------┐ ∨ n| ∨ root ----> at <---> c <-----------> d ^ ^ └------ e ------┘ */
	n := at.next
    /** ┌-----------------------------------┐ ∨ n| ∨ root ----> at <---- c <-----------> d | ^ ^ | └------ e ------┘ └---------------^ */
	at.next = e
    /** ┌-----------------------------------┐ ∨ n| ∨ root ----> at <---- c <-----------> d ^ ^ | e ------┘ └---------------^ */
	e.prev = at
	/** ┌-----------------------------------┐ ∨ n| ∨ root ----> at <---- c <-----------> d ^ ^ ^ | └------ e ------┘ └---------------^ */
	e.next = n
	/** ┌-----------------------------------┐ ∨ n| ∨ root ----> at c <-----------> d ^ ^ | └------>e └---------------^ */
	n.prev = e

    /** ┌-----------------------------------┐ ∨ ∨ root ----> at <---> e <---> c <---> d */
	return e
}

複製代碼

同時, ElementList 對外提供若干個方法code

Element

// 獲取 鏈表 的 當前節點 的 下一個節點
func (e *Element) Next() *Element {
    // 若是下一個節點是 鏈表的哨兵節點, 則當前已經在尾部, 返回 nil, 符合預期
	if p := e.next; e.list != nil && p != &e.list.root {
		return p
	}
	//...
}
// 獲取 鏈表 的 當前節點 的 上一個節點
func (e *Element) Prev() *Element {
    // 若是上一個節點是 鏈表的哨兵節點, 則當前已經在尾部, 返回 nil, 符合預期
	if p := e.prev; e.list != nil && p != &e.list.root {
		return p
	}
	//...
}
複製代碼

List

// 當前鏈表長度
func (l *List) Len() int { return l.len }

// 獲取鏈表頭部元素
func (l *List) Front() *Element {
	if l.len == 0 {
		return nil
	}
	return l.root.next
}

// 獲取鏈表尾部元素
func (l *List) Back() *Element {
	if l.len == 0 {
		return nil
	}
	return l.root.prev
}

// 刪除節點
func (l *List) Remove(e *Element) interface{} {
    // 傳入的這個節點若是屬於當前 list, 則刪除, 不然不刪除
    // 刪除後的節點的 prev , next , list 屬性都爲 nil, 能夠根據這個判斷是否刪除成功
	if e.list == l {
		// if e.list == l, l must have been initialized when e was inserted
		// in l or l == nil (e is a zero Element) and l.remove will crash
		l.remove(e)
	}
	return e.Value
}

// 從頭部 push 節點
func (l *List) PushFront(v interface{}) *Element {
    // 防止由於是本身手動實例化的 結構體而沒有初始化的問題,
    // 檢查是否已經初始化, 也就是哨兵的 尾節點是否指向本身
	l.lazyInit()
	// 在 root 後面插入元素
	return l.insertValue(v, &l.root)
}

// 從尾部插入元素
func (l *List) PushBack(v interface{}) *Element {
	l.lazyInit()
	// 在尾部的前一個元素後插入元素
	return l.insertValue(v, l.root.prev)
}

// 在給定節點前插入節點
func (l *List) InsertBefore(v interface{}, mark *Element) *Element // 在給定節點後插入節點 func (l *List) InsertAfter(v interface{}, mark *Element) *Element // 將給定節點挪到 哨兵 的後面, 也就是鏈表的頭部 func (l *List) MoveToFront(e *Element) // 將給定節點挪到 哨兵 的前面, 也就是鏈表的尾部 func (l *List) MoveToBack(e *Element) // 將給定節點挪到 給定節點 的後面 func (l *List) MoveBefore(e, mark *Element) // 將給定節點挪到 給定節點 的後面 func (l *List) MoveAfter(e, mark *Element) // 合併鏈表 在鏈表頭部哨兵後逐步插入別的鏈表的所有節點, // 步驟以下 /* 往 A(root<->a<->b) 鏈表插入 B(root<->c<->d) root<->c<->d root<->b<->c<->d root<->c<->b<->c<->d */ func (l *List) PushFrontList(other *List) {
	l.lazyInit()
	// 每次運行結束更新 i (other 鏈表的長度), e (other 鏈表的尾節點)
	for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
		l.insertValue(e.Value, &l.root)
	}
}
複製代碼

歡迎到 github.com/Kuri-su/KBl… 開 Issue 討論內存

相關文章
相關標籤/搜索