4. 單鏈表

鏈式結構--單鏈表node

1.線性結構和鏈式結構的區別:python

線性結構內存是連續的,能夠經過下標訪問的app

鏈式結構內存是不連續的,沒法經過下標訪問ide

(鏈式結構append時會很方便,find的時候會很麻煩,由於要從頭至尾的遍歷、尋找)
單元測試

image.png

經過連接指針的方式,指向下一個。測試


2.單鏈表(LinkedList)
spa

image.png

由一個一個節點經過指針的方式,把節點給串起來,root節點是一個入口,能夠去遍歷。指針

每一塊區域都會表明一個值,節點稱之爲node。blog

每一個節點除了value外,還要一個指針指向下一個元素,如圖:節點索引

image.png

這裏經過class來定義一個node屬性,它包含值,還包含指向下一個元素的指針,

單鏈表的結構以下:

                    data:root(入口)、length(長度)

linkedlist

                    method:init(初始化的方法)、append(追加元素)、appendleft(左側追加元素)、iter_node(遍歷節點)、remove(刪除及誒單)、find(查找節點)、popleft(刪掉頭節點)




代碼實現:

class Node(object):                                 #Node節點
    def __init__(self, value=None, next=None):
        self.value, self.next = value, next         #多重賦值


class LinkedList(object):
    def __init__(self, maxsize=None):               #maxsize表示最大容量,None表示無限擴容
        self.maxsize = maxsize                      #賦值
        self.root = Node()                          #須要一個根節點,是一個空節點
        self.length = 0                             #須要一個長度,默認是0
        self.tailnode = None

    def __len__(self):                              #魔術方法,定義len方法
        return self.length                          #這裏返回記錄的長度值

    def append(self, value):
        if self.maxsize is not None and \
            len(self) > self.maxsize:               #條件
            raise Exception('LinkedList Is Full')   #拋出異常
        node = Node(value)                          #構造新加入的節點,並賦值爲value
        tailnode = self.tailnode
        if tailnode is None:                        #這個代表尾節點是None,則說明鏈表只有root節點這一個節點
            self.root.next = node                   #把root的next指針指向新加入的節點
        else:
            tailnode.next = node                    #當尾節點不是None的時候,將尾節點的next指針指向新加入節點
        self.tailnode = node                        #更新鏈表,如今新加入的節點是鏈表的尾節點
        self.length += 1                            #將鏈表長度值加1

    def appendleft(self, vlaue):                    #定義往左邊插入節點,位置爲root節點的後面,實現往左邊插入的操做
        headnone = self.root.next                   #定義頭節點爲root節點的下一個節點
        node = Node()                               #構造新加入的節點
        self.root.next = node                       #把root的next節點指向新加入的節點
        node.next = headnone                        #把新加入的節點的next節點指向原來的頭節點
                                                    #這兩步就把鏈表串起來了,實如今root節點後面和原頭節點前面添加節點

    def iter_node(self):                            #遍歷,從頭節點,遍歷到尾節點
        curnode = self.root.next                    #從第一個節點遍歷,curnode爲root節點的下一個節點
        while curnode is not self.tailnode:         #只要curnode不是尾節點
            yield curnode                           #就一直yield當前節點
            curnode = curnode.next                  #更新curnode爲下一節點
        yield curnode                               #這個while循環只移動到了最後一個節點,而後還要把當前節點更新到最後一個節點,
                                                    #也要把他yield出來,這樣就實現了完整的遍歷操做。

    def __iter__(self):
        for node in self.iter_node():
            yield node.value


    def remove(self, value):                        #查找並刪除這個節點,這裏是O(n),必須從頭查到尾,無法經過下標查找到
        prevnode = self.root                        #定義prevnode爲根節點
        #curnode = self.root.next
        for curnode in self.iter_node():            #遍歷鏈表,當前節點爲curnode
            if curnode.value == value:              #若是當前遍歷到的curnode的值和要刪除的值一致
                prevnode.next = curnode.next        #將prevnode的next指針指向到當前要刪除的curnode節點的下一個節點
                if curnode is self.tailnode:        #若是這個curnode恰好是尾節點
                    self.tailnode = prevnode        #則將prevnode就是尾節點
                del curnode                         #刪除節點
                self.length -= 1                    #長度減1
                return 1                            #代表刪除成功
            else:                                   #不然若是當前遍歷到的curnode的值不是要刪除的值
                prevnode = curnode                  #則更新prevnode的位置到遍歷的這個非刪除的節點,並繼續遍歷下一個curnode節點
        return 1                                    #刪除完成返回1

    def find(self, value):                          #查找操做,也是要從頭遍歷,這也是單鏈表查找比較慢的緣由,時間複雜度O(n)
        index = 0                                   #索引從0開始
        for node in self.iter_node():               #遍歷鏈表
            if node.value == value:                 #若是要查找的值等於這個node的值
                return index                        #返回這個node的索引
            index += 1                              #索引+1
        return -1                                   #沒有找到指定的值則返回-1

    def popleft(self):                              #至關於把第一個節點刪掉
        if self.root.next is None:                  #若是root節點的下一個節點是None
            raise Exception('Pop From Empty')       #拋出異常
        headnode = self.root.next                   #將root的下一個節點定義爲頭節點
        self.root.next = headnode.next              #再將root的next指針指向上面頭節點的下一個節點
        self.length -= 1                            #將長度減1
        value = headnode.value                      #同pop操做最後會返回一個值,並打印出來
        del headnode                                #刪除原來的頭節點
        return value                                #返回被popleft掉的值

    def clean(self):                                #清空操做,清空鏈表
        for node in self.iter_node():               #遍歷節點,並刪除遍歷到的節點
            del node
        self.root.next = None                       #將root節點的下一個節點指向空
        self.length = 0                             #將長度值置0


#單元測試
def test_linked_list():
    lst = LinkedList()
    lst.append(0)
    lst.append(1)
    lst.append(2)
    assert len(lst) == 3
    assert lst.find(2) == 2
    assert lst.find(3) == -1
    lst.remove(0)
    assert len(lst) == 2
    assert lst.find(0) == -1
    assert list(lst) == [1, 2]   #斷言list只有[1, 2]
    lst.appendleft(0)
    assert list(lst) == [0 ,1 ,2]
    assert len(lst) == 3
    headvalue = lst.popleft()   #驗證popleft方法
    assert headvalue == 0
    assert len(lst) == 2
    assert list(lst) == [1, 2]
    lst.clean()                 #清空鏈表
    assert len(lst) == 0


# pytest linked_list.py

執行測試


平均時間複雜度:

鏈表操做 平均時間複雜度
linked_list.append(value) O(1)
linked_list.appendleft(value) O(1)
linked_list.find(value) O(n)
linked_list.remove(value) O(n)
相關文章
相關標籤/搜索