雙向鏈表的GO語言實現

1、什麼是雙向鏈表node

%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8.png

和單鏈表比較,雙向鏈表的元素不但知道本身的下線,還知道本身的上線(愈來愈像傳銷組織了)。小煤車開起來,圖裏面能夠看出,每一個車箱除了一個指向後面車箱的箭頭外,還有一個指向前面車箱的箭頭(車頭、車尾除外)。車頭只有指向後面車箱的箭頭,車尾只有指向前面車箱的箭頭。git

2、雙向鏈表與Go的對應結構github

一、節點函數

%E8%8A%82%E7%82%B9.png

咱們先把車箱分解開來。每節車箱都由煤炭、車體、拉前車箱繩索、拉後車箱繩索這4部分組成。車體是咱們的運輸工具,在Go語言裏咱們用結構提DNode表示;煤炭表明運的貨物,用data變量表示;拉前車箱繩索和拉後車箱繩索咱們分別用指針prev和next表示。這樣一節車箱,用Go語言描述以下:工具

type DNode struct {
 data Object
 prev *DNode
 next *DNode
}

二、雙向鏈表ui

%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8%E8%A1%A8%E7%A4%BA.png

一個運煤車隊就是一個雙向鏈表。車隊要有車頭、車箱、車尾,做爲車隊的負責人還得知道車隊有多長。在Go語言裏,車隊用結構體DList表示,車頭用head變量表示,車位用tail變量表示,車隊長度就用size來表示,把這些表示合起來:spa

type DList struct {
 size uint64
 head *DNode
 tail *DNode
}

結構講完了,下面講講如何增長、減小車箱,也就是雙向鏈表的接口。指針

3、接口說明及實現code

%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8%E7%9A%84%E6%8E%A5%E5%8F%A3.png

接口主要分爲這幾類。一個是雙向鏈表自己的,還有一類是節點的。鏈表自己的還分爲公開和私有兩種。下面咱們就詳細聊聊這些接口。blog

一、初始化鏈表Init

雙向鏈表的初始化,能夠理解成大衛哥準備買一個車隊準備運煤。第一步,得得到國家有關部門的批准,有了批准大衛哥就能夠買車箱運煤了。可是,批准下來的時候,大衛哥的車隊啥都沒有,沒有車頭、車尾,連一節車箱也沒有。Go語言代碼實現:

func (dList *DList) Init() {
 _dList := *(dList)
 _dList.size = 0     // 沒車箱
 _dList.head = nil   // 沒車頭
 _dList.tail = nil   // 沒車尾
}

二、新增數據Append

大衛哥新買了車箱,買好的車箱要掛到車隊後面。第一節車箱就是車頭。

func (dList *DList) Append(data Object) {
    newNode := new(DNode)
    (*newNode).data =  data

    if (*dList).GetSize() == 0 { // 買個車頭
        (*dList).head = newNode
        (*dList).tail = newNode
        (*newNode).prev = nil
        (*newNode).next = nil
    } else { //  掛在車隊尾部
        (*newNode).prev = (*dList).tail
        (*newNode).next = nil
        (*((*dList).tail)).next = newNode
        (*dList).tail = newNode
    }

    (*dList).size++;
}

三、在節點後面插入數據InsertNext

有時候,車箱不是放在車隊尾巴,而是要放在中間,好比都是運蘋果的車箱最好放一塊兒。

func (dList *DList) InsertNext(elmt *DNode, data Object) bool {
    if elmt == nil { // apend
        return false   
    }

    if dList.isTail(elmt) { // 剛好在車隊尾巴
        dList.Append(data)
    } else {
        newNode := new(DNode)
        (*newNode).data =  data
        (*newNode).prev = elmt
        (*newNode).next = (*elmt).next

        (*elmt).next = newNode
        (*((*newNode).next)).prev = newNode
        (*dList).size++;
    }

    return true
}

五、在節點前面插入數據InsertPrev

在節點前面插入數據,能夠理解爲在當前節點前一個節點的後面插入數據。

func (dList *DList) InsertPrev(elmt *DNode, data Object) bool {
    if elmt == nil {
        return false
    }

    if dList.isHead(elmt) {  // 若是是新增一個車頭就特殊處理
        newNode := new(DNode)
        (*newNode).data = data
        (*newNode).next = dList.GetHead()
        (*newNode).prev = nil

        (*(dList.head)).prev = newNode
        dList.head = newNode
        dList.size++
        return true
    } else {
        prev := (*elmt).prev
        return dList.InsertNext(prev, data)
    }
}

這裏的isHead就是判斷節點是不是車頭,後面大衛哥會介紹。
六、刪除一個節點Remove
有些車箱出現問題須要維修,就要把它從車隊裏卸下來。

func (dList *DList) Remove(elmt *DNode) Object {
    if elmt == nil {
        return false
    }

    prev := (*elmt).prev
    next := (*elmt).next

    if dList.isHead(elmt) {
        dList.head = next
    } else {
        (*prev).next = next
    }

    if dList.isTail(elmt) {
        dList.tail = prev
    } else {
        (*next).prev = prev
    }

    dList.size--

    return (*elmt).GetData() 
}

卸下來後,車箱裏的數據仍是要保留的。

七、查找指定數據所在的節點Search

好比說,要找到蘋果在哪節車箱。就要用到查找功能了。

func (dList *DList) Search(data Object, yourMatch ...MatchFun) *DNode {
    if dList.GetSize() == 0 {
        return nil
    }

    match := defaultMatch
    if len(yourMatch) > 0 {
        match = yourMatch[0]
    }

    node := dList.GetHead()
    for ; node != nil; node = node.GetNext() {
        if match(node.GetData(), data) == 0 {
            break
        }
    }

    return node
}

match是匹配函數,定義以下:

type MatchFun func (data1 Object, data2 Object) int

若是data1和data2相等就返回0,data1大於data2就返回正數,小於就返回負數。

八、獲取鏈表長度GetSize

func (dList *DList) GetSize() uint64 {
 return (*dList).size
}

九、獲取頭部節點GetHead

func (dList *DList) GetHead() *DNode {
 return (*dList).head
}

十、獲取尾部節點GetTail

func (dList *DList) GetTail() *DNode {
 return (*dList).tail
}

十一、節點是不是頭部節點isHead

func (dList *DList) isHead(elmt *DNode) bool {
 return dList.GetHead() == elmt
}

十二、節點是不是列表尾部isTail

func (dList *DList) isTail(elmt *DNode) bool {
 return dList.GetTail() == elmt
}

1三、獲取節點內數據GetData

func (dNode *DNode) GetData() Object {
 return (*dNode).data
}

這個是節點的方法,不是鏈表的。用來獲取車箱內裝的是什麼。
1四、獲取下一個節點GetNext

func (dNode *DNode) GetNext() *DNode {
 return (*dNode).next
}

這個也是節點的方法,幫助車箱找到下一節車箱。
1五、獲取前一個節點GetPrev

func (dNode *DNode) GetPrev() *DNode {
 return (*dNode).prev
}

這裏一樣是節點的方法。用來找到上一節車箱。

代碼下載

相關文章
相關標籤/搜索