Python單向雙端鏈表

和單向單端鏈表不同, 除了頭端,尾端也會維護一個指針(能夠加快在尾端添加節點的速度).

由於有着對最後一個鏈結點的直接引用.所以雙端鏈表比傳統鏈表在某些方面要方便.比如在尾部插入一個鏈結點.雙端鏈表可以進行直接操作

但傳統鏈表只能通過next節點循環找到最後鏈結點操作.所以雙端鏈表適合製造隊列(先進先出).

雙端鏈表可以插入表尾,但是仍然不能方便的刪除表尾,因爲我們沒有方法快捷地獲取倒數第二個鏈結點,雙端鏈表沒有逆向的指針,這一點就要靠雙向鏈表來解決了

實現參考:

class Node(object):
    def __init__(self, value=None, next=None):   # 這裏我們 root 節點默認都是 None,所以都給了默認值
        self.value = value
        self.next = next

    def __str__(self):
        """方便你打出來調試,複雜的代碼可能需要斷點調試"""
        return '<Node: value: {}, next={}>'.format(self.value, self.next)

    __repr__ = __str__


class LinkedList(object):
    """ 鏈接表 ADT
    [root] -> [node0] -> [node1] -> [node2]
    """

    def __init__(self, maxsize=None):
        """
        :param maxsize: int or None, 如果是 None,無限擴充
        """
        self.maxsize = maxsize
        self.root = Node()     # 默認 root 節點指向 None
        self.tailnode = None
        self.length = 0

    def __len__(self):
        return self.length

    def append(self, value):    # O(1)
        if self.maxsize is not None and len(self) >= self.maxsize:
            raise Exception('LinkedList is Full')
        node = Node(value)    # 構造節點
        tailnode = self.tailnode
        if tailnode is None:    # 還沒有 append 過,length = 0, 追加到 root 後
            self.root.next = node
        else:     # 否則追加到最後一個節點的後邊,並更新最後一個節點是 append 的節點
            tailnode.next = node
        self.tailnode = node
        self.length += 1

    def appendleft(self, value):
        if self.maxsize is not None and len(self) >= self.maxsize:
            raise Exception('LinkedList is Full')
        node = Node(value)
        if self.tailnode is None:  # 如果原鏈表爲空,插入第一個元素需要設置 tailnode
            self.tailnode = node

        headnode = self.root.next
        self.root.next = node
        node.next = headnode
        self.length += 1

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

    def iter_node(self):
        """遍歷 從 head 節點到 tail 節點"""
        curnode = self.root.next
        while curnode is not self.tailnode:    # 從第一個節點開始遍歷
            yield curnode
            curnode = curnode.next    # 移動到下一個節點
        if curnode is not None:
            yield curnode

    def remove(self, value):    # O(n)
        """ 刪除包含值的一個節點,將其前一個節點的 next 指向被查詢節點的下一個即可

        :param value:
        """
        prevnode = self.root    #
        for curnode in self.iter_node():
            if curnode.value == value:
                prevnode.next = curnode.next
                if curnode is self.tailnode:  # NOTE: 注意更新 tailnode
                    if prevnode is self.root:
                        self.tailnode = None
                    else:
                        self.tailnode = prevnode
                del curnode
                self.length -= 1
                return 1  # 表明刪除成功
            else:
                prevnode = curnode
        return -1  # 表明刪除失敗

    def find(self, value):    # O(n)
        """ 查找一個節點,返回序號,從 0 開始

        :param value:
        """
        index = 0
        for node in self.iter_node():   # 我們定義了 __iter__,這裏就可以用 for 遍歷它了
            if node.value == value:
                return index
            index += 1
        return -1    # 沒找到

    def popleft(self):    # O(1)
        """ 刪除第一個鏈表節點
        """
        if self.root.next is None:
            raise Exception('pop from empty LinkedList')
        headnode = self.root.next
        self.root.next = headnode.next
        self.length -= 1
        value = headnode.value

        if self.tailnode is headnode:   # 勘誤:增加單節點刪除 tailnode 處理
            self.tailnode = None
        del headnode
        return value

    def clear(self):
        for node in self.iter_node():
            del node
        self.root.next = None
        self.length = 0
        self.tailnode = None

    def reverse(self):
        """反轉鏈表"""
        curnode = self.root.next
        self.tailnode = curnode  # 記得更新 tailnode,多了這個屬性處理起來經常忘記
        prevnode = None

        while curnode:
            nextnode = curnode.next
            curnode.next = prevnode

            if nextnode is None:
                self.root.next = curnode

            prevnode = curnode
            curnode = nextnode


def test_linked_list():
    ll = LinkedList()

    ll.append(0)
    ll.append(1)
    ll.append(2)
    ll.append(3)
    print(ll)

    assert len(ll) == 4
    assert ll.find(2) == 2
    assert ll.find(-1) == -1

    assert ll.remove(0) == 1
    assert ll.remove(10) == -1
    assert ll.remove(2) == 1
    assert len(ll) == 2
    assert list(ll) == [1, 3]
    assert ll.find(0) == -1

    ll.appendleft(0)
    assert list(ll) == [0, 1, 3]
    assert len(ll) == 3

    headvalue = ll.popleft()
    assert headvalue == 0
    assert len(ll) == 2
    assert list(ll) == [1, 3]

    assert ll.popleft() == 1
    assert list(ll) == [3]
    ll.popleft()
    assert len(ll) == 0
    assert ll.tailnode is None

    ll.clear()
    assert len(ll) == 0
    assert list(ll) == []


def test_linked_list_remove():
    ll = LinkedList()
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.append(7)
    ll.remove(7)
    print(list(ll))

def test_single_node():
    # https://github.com/PegasusWang/python_data_structures_and_algorithms/pull/21
    ll = LinkedList()
    ll.append(0)
    ll.remove(0)
    ll.appendleft(1)
    assert list(ll) == [1]

def test_linked_list_reverse():
    ll = LinkedList()
    n = 10
    for i in range(n):
        ll.append(i)
    ll.reverse()
    assert list(ll) == list(reversed(range(n)))


def test_linked_list_append():
    ll = LinkedList()
    ll.appendleft(1)
    ll.append(2)
    assert list(ll) == [1, 2]


if __name__ == '__main__':
    test_single_node()
    test_linked_list()
    test_linked_list_append()
    test_linked_list_reverse()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章