1.单链表的缺点:
成都创新互联公司成立与2013年,先为丰南等服务建站,丰南等地企业,进行企业商务咨询服务。为丰南企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。(1)remove操作要从头到尾遍历,时间复杂度是O(n)
(2)只能单向遍历,不能反向遍历
2.使用双链表可以克服以上两个缺点
双链表相对于单链表来说,双链表的节点(Node)多了一个指针:

这样一来就能指向前一个节点,而且也可以指向后一个节点。
同样root节点也有一个prev和next,root节点的next指向head节点,head节点的prev指向root节点,这样就能实现一个双端链表。

循环双端链表:
比如要反向遍历的时候,都是从root节点作为一个入口,把root节点的prev反过来指向tail节点,这样就能实现从头向尾节点遍历,然后从root节点的prev反过来指向上一个节点,对比正向遍历从next指向下一个节点,这样就实现循环双端链表。
双链表的属性:
data { root 有个根节点
maxsize 控制它的大长度
length 记录长度的属性
双链表
method { headnode 获取头节点的方法
tailnode 获取尾节点的方法
append 最后添加新节点
appendleft 在头节点前面,根节点后面添加新节点
remove 删除节点,时间复杂度为O(1);
比如,有3个节点,要删除中间节点,就可以让前面和后面节点互指,最后再del掉中间的节点。
iter_node 遍历节点的操作
iter_node_reverse 反向遍历节点的操作
实现方式:
class Node(object):
    def __init__(self, value=None, prev=None, next=None):
        self.value, self.prev, self.next = value, prev, next
class CircualDoubleLinkedList(object):
    def __init__(self, maxsize=None):
        self.maxsize = maxsize
        node =Node()                        #这两行代码,用于构建一个根节点,
        node.next, node.prev = node, node   #这个根节点是自己指向自己的默认是一个闭环。
        self.root = node                    #把node赋值给根节点
        self.length = 0                     #长度属性默认是0,root节点是不计算在链表长度里面的
    def __len__(self):
        return self.length                  #返回长度值
    def headnode(self):                     #定义头节点
        return self.root.next               #也就是root节点的下一个节点
    def tailnode(self):                     #定义尾节点
        return self.root.prev               #也就是root节点的上一个节点
    """
        假设有一条几个节点的链表,插入一个新的节点前,要先构造这个新的节点,
        然后再让链表原来尾节点的next指向新节点,并且新节点的prev指向原来的
        尾节点,root节点的prev也要指向新节点,新节点的next指向root节点,
        这样就形成了一个闭环,实现了append新增节点。
    """
    def append(self, value):
        if self.maxsize is not None \
            and len(self) > self.maxsize:       #判断是否已经超长,如果是就报异常。
            raise Exception("The LinkedList is Full")
        node = Node(value=value)                           #构造新节点
        tailnode = self.tailnode()              #尾节点
        tailnode.next = node                    #尾节点的next指向新节点
        node.prev = tailnode                    #新节点的prev指向尾节点
        node.next = self.root                   #新节点的next指向root节点
        self.root.prev = node                   #root节点的prev指向新节点
        self.length += 1                        #最后将长度+1
    def appendleft(self, vlaue):
        if self.maxsize is not None \
            and len(self) > self.maxsize:       #判断是否已经超长,如果是就报异常。
            raise Exception("The LinkedList is Full")
        node = Node(value=vlaue)
        if self.root.next is self.root:         #判断这个链表是空的情况
            node.next = self.root
            node.prev = self.root               #新节点的next和prev都指向root节点,形成一个闭环。
            self.root.next = node               #同理,将root节点的next指向新节点
            self.root.prev = node               #将root节点的prev指向新节点
        else:                                   #否则,如果链表不是空的话
            headnode = self.root.next           #定义root节点的next节点是链表的头节点
            node.prev = self.root               #将新节点的prev指向root节点
            node.next = headnode                #将新节点的next指向原头节点
            headnode.prev = node                #最后将头节点的prev指向新节点
        self.length += 1                        #链表长度加1
    def remove(self, node):                     #node是要删除的节点,是O(1)的时间复杂度,注意是node不是value
        if node is self.root:                   #如果只有根节点,啥都不返回
            return
        else:                                   #否则是非根节点
            node.prev.next = node.next          #将要删除节点的前一个节点的next指针指向要删除节点的下一个节点
            node.next.prev = node.prev          #将要删除节点的后一个节点的prev指针指向要删除节点的上一个节点
        self.length -= 1                        #链表长度-1
        return node                             #返回删除的节点
    def iter_node(self):                        #遍历节点
        if self.root.next is self.root:         #防止链表是空的
            return
        curnode = self.root.next                #否则,不是空的,从头开始遍历
        while curnode.next is not self.root:    #当curnode不是尾节点
            yield curnode                       #一直把curnode节点给yield出来
            curnode = curnode.next              #更新curnode节点,让curnode一直往下一个节点走
        yield curnode                         #最后别忘了把最后一个curnode给yield出来
                                                #因为遍历到最后一个节点,但并没有去yield这个节点
                                                #当while循环终止时,当前curnode已经到达了tailnode节点,
                                                #所以要把它yield出来才完整。
    def iter_node_reverse(self):
        if self.root.prev is self.root:
            return
        curnode = self.root.prev                #和正向遍历相反,这个是tailnode节点
        while curnode.prev is not self.root:
            yield curnode
            curnode = curnode.prev              #前移
        yield curnode
#单元测试
def test_double_link_list():
    dll = CircualDoubleLinkedList()
    assert len(dll) == 0
    dll.append(0)
    dll.append(1)
    dll.append(2)
    assert list(dll) == [0, 1, 2]
    assert [node.value for node in dll.iter_node_reverse()] == [2, 1, 0]
    assert [node.value for node in dll.iter_node()] == [0, 1, 2]
    headnode = dll.headnode()           #取头节点
    assert headnode.value == 0          #断言头节点的值为0,因为0是第一个被添加的
    dll.remove(headnode)                #O(1)
    assert len(dll) == 2
    assert [node.value for node in dll.iter_node()] == [1, 2]
    dll.appendleft(0)
    assert [node.value for node in dll.iter_node()] == [0, 1, 2]执行测试:
# pytest double_link_list.py
平均时间复杂度:
| 循环双端链表操作 | 平均时间复杂度 | 
|---|---|
| cdll.append(value) | O(1) | 
| cdll.appendleft(value) | O(1) | 
| cdll.remove(node),注意这里参数是 node | O(1) | 
| cdll.headnode() | O(1) | 
| cdll.tailnode() | O(1) | 
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
分享标题:5.双链表-创新互联
标题网址:http://www.cqwzjz.cn/article/cdjeis.html

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询 
 