雙向鏈表的GO語言實現

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

一、什麼是雙向鏈表

和單鏈表比較,雙向鏈表的元素不但知道自己的下線,還知道自己的上線(越來越像傳銷組織了)。小煤車開起來,圖裡面可以看出,每個車廂除了一個指向後面車廂的箭頭外,還有一個指向前面車廂的箭頭(車頭、車尾除外)。車頭只有指向後面車廂的箭頭,車尾只有指向前面車廂的箭頭。

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

1、節點

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

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

2、雙向鏈表

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

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

結構講完了,下面講講如何增加、減少車廂,也就是雙向鏈表的介面。

三、介面說明及實現

介面主要分為這幾類。一個是雙向鏈表本身的,還有一類是節點的。鏈表本身的還分為公開和私人兩種。下面我們就詳細聊聊這些介面。

1、初始化鏈表Init

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

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

2、新增資料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++;}

3、在節點後面插入資料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}

5、在節點前面插入資料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就是判斷節點是否是車頭,後面大衛哥會介紹。
6、刪除一個節點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() }

卸下來後,車廂裡的資料還是要保留的。

7、尋找指定資料所在的節點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就返回正數,小於就返回負數。

8、擷取鏈表長度GetSize

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

9、擷取頭部節點GetHead

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

10、擷取尾部節點GetTail

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

11、節點是否是頭部節點isHead

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

12、節點是否是列表尾部isTail

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

13、擷取節點內資料GetData

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

這個是節點的方法,不是鏈表的。用來擷取車廂內裝的是什麼。
14、擷取下一個節點GetNext

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

這個也是節點的方法,協助車廂找到下一節車廂。
15、擷取前一個節點GetPrev

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

這裡同樣是節點的方法。用來找到上一節車廂。

代碼下載

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.