zoukankan      html  css  js  c++  java
  • python实现双链表

    介绍

     

    双向链表比之单向链表,多数操作方法的实现都没有什么不同,如is_empty, __len__, traverse, search。这些方法都没有涉及节点的变动,也就可通过继承单向链表来实现即可。

    不同之处一是在于节点实现的不同。因为增加了指向前一个节点的前驱区,因此需要为节点添加一个新属性prev,用以指向前一个节点。

    另外一点就是在做增删的操作时,需要额外考虑节点的前驱区的指向。其中的remove方法更是需要考虑多种特殊情况。

    下面给出代码前,先放一个自己做的图示。(右键选择在新页面打开可看完整图示)

    一、定义一个创建节点的类

    链表是由一个一个的节点组成的,在创建链表之前,要先创建节点,然后把节点“串”到链表上。在同一个链表中,每个节点的结构都相同,只是节点中保存的数据不同和链接域的值不同,所以提前声明一个创建节点的类,需要创建节点时实例化即可。

    # coding=utf-8
    class Node(object):
     
        def __init__(self, data):
            self.prev = None
            self.data = data
            self.next = None

    双向链表的节点包含三个域,一个信息域(元素域)和两个链接域(引用域),一个链接域指向前一个节点,一个链接域指向后一个节点,头节点向前的链接域指向空,尾节点向后的链接域指向空。在实例化一个节点时,传入该节点中保存的数据,保存到信息域中,链接域默认为空,当对节点进行“链接”操作时,再设置具体的链接域。

    二、定义一个双向链表类

    对于链表,在没有将节点“链接”上去时,链表里没有节点和数据。实例化一个双向链表时,这个双向链表是一个空链表,把节点依次“链接”上去后,链表中才有节点和数据。

    在链表中,要找到链表的某个节点,需要从链表的头节点开始,依次寻找,所以在实例化一个链表时,必须定义好链表的“头”,当加入头节点时,将链表的“头”指向头节点。

    定义一个双向链表类 DoubleLinkList,初始化一个双向链表时,链表的“头”指向空值,默认为空链表。

    class DoubleLinkList(object):
     
        def __init__(self):
            self.__head = None

    三、实现双向链表的展示功能

        def is_empty(self):
            return not self.__head
     
        def show(self):
            if self.is_empty():
                print('空链表')
                return
            cur = self.__head
            while cur is not None:
                if cur.next is not None:
                    print(cur.data, end='←→')
                else:
                    print(cur.data)
                cur = cur.next

    先实现判断链表是否为空的方法 is_empty() ,实例化链表时,默认是空的,链表的头指向为空。所以,如果链表的头指向为空(对应布尔值False), is_empty() 的值就为 True ,反之。

    展示链表中的数据,就是将链表中所有的数据依次打印输出。链表不像顺序表有“索引”,链表只能从头节点开始依次往下找,直到尾节点。所以链表不能使用 for 循环进行遍历,只能使用 while 循环进行遍历,并使用一个游标 cur 来记录当前所处的节点,通过游标 cur 向下一个节点移动来遍历,当向后的链接域指向空(尾节点)时停止。

    实现 show() 方法时,为了更形象地展示链表中每个节点的关系,我在相邻两个节点之间使用左箭头加右箭头连接(空链表无效果)。

    四、实现双向链表中添加数据的功能

        def add(self, data):
            node = Node(data)
            if self.is_empty():
                self.__head = node
                return
            node.next = self.__head
            self.__head.prev = node
            self.__head = node
     
        def append(self, data):
            if self.is_empty():
                self.add(data)
                return
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            node = Node(data)
            node.prev = cur
            cur.next = node
     
        def length(self):
            length = 0
            cur = self.__head
            while cur is not None:
                length += 1
                cur = cur.next
            return length
     
        def insert(self, index, data):
            if index <= 0:
                self.add(data)
                return
            if index > self.length() - 1:
                self.append(data)
                return
            cur = self.__head
            for i in range(index - 1):
                cur = cur.next
            node = Node(data)
            node.next = cur.next
            node.prev = cur
            cur.next.prev = node
            cur.next = node

    添加数据到链表中,可以从头部添加、从尾部添加或从指定位置添加。

    无论将数据添加到链表的哪个位置,都要先创建一个新节点,新节点里存放对应的数据,然后将新节点添加到指定的位置。

    add(data):从头部添加时,链表原来的头节点会成为第二个节点,新节点成为头节点。添加分为三步,第一步将新节点向后的链接域指向原来的头节点,第二步将旧的头节点向前的链接域指向新节点,第三步将链表的头指向新节点(注意顺序不能变)。如果原来的链表为空,则链表的头原来是指向空,所以直接将链表的头指向新节点即可。

    append(data):从尾部添加时,找到链表的尾节点,添加分为两步,第一步将尾节点向后的链接域指向新节点,第二步将新节点向前的链接域指向尾节点。如果原来的链表为空,则链表没有尾节点,这时候与从头部添加一样,直接调用即可。

    insert(index, data):在指定位置添加数据时,要使用一个游标 cur 来找到此位置的前一个节点,添加分为四步,第一步将新节点向后的链接域指向此位置原来的节点,第二步将游标记录的节点向后的链接域指向新节点,第三步将此位置原来的节点向前的链接域指向新节点,第四步将新节点向前的链接域指向游标记录的节点,这样就成功将新节点插入到了指定位置。

    如果指定的位置是负数或超过了链表最大长度,则需要特殊处理,上面的处理是负数在头部添加,超过最大长度在尾部添加。也可以直接抛出 IndexError ,这个可以自己按需选择。

    同时,上面实现了获取双向链表长度的方法 length(),返回链表当前的节点个数。

    五、实现双向链表的查询和修改功能

        def is_exist(self, value):
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    return True
                cur = cur.next
            return False
     
        def index(self, value):
            index = 0
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    return index
                cur = cur.next
                index += 1
            return -1
     
        def setitem(self, index, value):
            if index < 0:
                raise IndexError
            if index > self.length() - 1:
                raise IndexError
            cur = self.__head
            for i in range(index):
                cur = cur.next
            cur.data = value

    is_exist(value):判断一个数据是否存在链表中,遍历双向链表的每个节点,如果节点的数据值与目标值相等,则说明链表中存在目标值。

    index(value):返回一个数据在链表中的第几个节点,与判断是否存在的实现方式一样,这里返回的是数据处于第几个节点中,如果链表中不存在这个数据,则返回-1。

    setitem(index, value):修改指定位置的节点的数据,先根据给定的值,找到链表中该位置的节点,然后修改节点中的数据。如果数值小于零或大于链表长度,抛出 IndexError 。

    六、实现双向链表的删除功能

        def remove(self, index):
            if index < 0:
                raise IndexError
            if index > self.length() - 1:
                raise IndexError
            cur = self.__head
            for i in range(index):
                cur = cur.next
            if cur == self.__head:
                self.__head = self.__head.next
                if cur.next:
                    cur.next.prev = None
                return
            if cur.next is None:
                cur.prev.next = cur.next
                return
            cur.prev.next = cur.next
            cur.next.prev = cur.prev
     
        def delete(self, value):
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    if cur == self.__head:
                        self.__head = self.__head.next
                        if cur.next:
                            cur.next.prev = None
                        return
                    if cur.next is None:
                        cur.prev.next = cur.next
                        return
                    cur.prev.next = cur.next
                    cur.next.prev = cur.prev
                    return
                cur = cur.next
     
        def delete_all(self, value):
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    if cur == self.__head:
                        self.__head = self.__head.next
                        if cur.next:
                            cur.next.prev = None
                        self.delete_all(value)
                        return
                    if cur.next is None:
                        cur.prev.next = cur.next
                        self.delete_all(value)
                        return
                    cur.prev.next = cur.next
                    cur.next.prev = cur.prev
                    self.delete_all(value)
                    return
                cur = cur.next

    remove(index):删除指定位置的节点,将节点删除后,要保证链表不断开。通过游标 cur 找到节点,再使用一个游标 prev 来记录当前节点的前一个节点,删除分为两步,第一步将前一个节点向后的链接域指向当前节点的后一个节点,第二步将后一个节点向前的链接域指向前一个节点。如果删除的是头节点,则将链表的头指向第二个节点,然后将第二个节点向前的链接域指向空,如果只有一个节点,则直接将链表的头指向空就行了。如果删除的是尾节点,则将倒数第二个节点向后的链接域指向空。如果指定的位置小于零或超过链表长度,则抛出 IndexError 。

    delete(value):删除指定值的节点,先遍历链表,找到对应值的节点。使用游标 cur 记录要删除的节点,使用另一个游标 prev 来记录当前节点的前一个节点,删除步骤与 remove(index) 相同。

    使用这个方法,如果链表中有多个满足条件的节点,只会删除最前面的一个节点。

    delete_all(value):删除数据等于指定值的所有节点,如果链表中有多个节点的数据与目标值相等,删除第一个节点后,链表的长度发生了改变,继续遍历和删除节点,会出现删除不完全甚至程序出错的情况。所以在删除第一个节点之后,递归调用自身,这样重新遍历时使用的是新的链表长度,不会出现漏删或错误。

    七、所有代码实现

    # coding=utf-8
    class Node(object):
    # 初始化节点
        def __init__(self, data):
            self.prev = None
            self.data = data
            self.next = None
    
    # 创建链表类
    class DoubleLinkList(object):
    # 1、初始化链表
        def __init__(self):
            self.__head = None
    # 2、判断是否为空
        def is_empty(self):
            return not self.__head
    # 3、打印链表
        def show(self):
            if self.is_empty():
                print('空链表')
                return
            cur = self.__head
            while cur is not None:
                if cur.next is not None:
                    print(cur.data, end='←→')
                else:
                    print(cur.data)
                cur = cur.next
    # 4、在头部添加元素
        def add(self, data):
            node = Node(data)
            if self.is_empty():
                self.__head = node
                return
            node.next = self.__head
            self.__head.prev = node
            self.__head = node
    # 5、在尾部添加
        def append(self, data):
            if self.is_empty():
                self.add(data)
                return
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            node = Node(data)
            node.prev = cur
            cur.next = node
    # 6、求链表长度
        def length(self):
            length = 0
            cur = self.__head
            while cur is not None:
                length += 1
                cur = cur.next
            return length
    # 7、按照索引添加元素
        def insert(self, index, data):
            if index <= 0:
                self.add(data)
                return
            if index > self.length() - 1:
                self.append(data)
                return
            cur = self.__head
            for i in range(index - 1):
                cur = cur.next
            node = Node(data)
            node.next = cur.next
            node.prev = cur
            cur.next.prev = node
            cur.next = node
    # 8、判断元素是否在链表重存在
        def is_exist(self, value):
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    return True
                cur = cur.next
            return False
    # 9、遍历链表
        def index(self, value):
            index = 0
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    return index
                cur = cur.next
                index += 1
            return -1
    # 10、根据索引修改值
        def setitem(self, index, value):
            if index < 0:
                raise IndexError
            if index > self.length() - 1:
                raise IndexError
            cur = self.__head
            for i in range(index):
                cur = cur.next
            cur.data = value
    # 11、按照索引删除元素
        def remove(self, index):
            if index < 0:
                raise IndexError
            if index > self.length() - 1:
                raise IndexError
            cur = self.__head
            for i in range(index):
                cur = cur.next
            if cur == self.__head:
                self.__head = self.__head.next
                if cur.next:
                    cur.next.prev = None
                return
            if cur.next is None:
                cur.prev.next = cur.next
                return
            cur.prev.next = cur.next
            cur.next.prev = cur.prev
    # 12、删除指定的元素
        def delete(self, value):
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    if cur == self.__head:
                        self.__head = self.__head.next
                        if cur.next:
                            cur.next.prev = None
                        return
                    if cur.next is None:
                        cur.prev.next = cur.next
                        return
                    cur.prev.next = cur.next
                    cur.next.prev = cur.prev
                    return
                cur = cur.next
    # 13、删除所有元素,即清空链表
        def delete_all(self, value):
            cur = self.__head
            while cur is not None:
                if cur.data == value:
                    if cur == self.__head:
                        self.__head = self.__head.next
                        if cur.next:
                            cur.next.prev = None
                        self.delete_all(value)
                        return
                    if cur.next is None:
                        cur.prev.next = cur.next
                        self.delete_all(value)
                        return
                    cur.prev.next = cur.next
                    cur.next.prev = cur.prev
                    self.delete_all(value)
                    return
                cur = cur.next
    if __name__ == '__main__':
        d = DoubleLinkList()
        print("is_empty: ", d.is_empty())
        d.show()
        d.add(10)
        d.add(100)
        d.append(20)
        d.append(30)
        d.append(40)
        d.show()
        d.insert(1, 200)
        d.show()
        print("链表长度:", d.length())
        print(d.is_exist(200))
        print(d.index(20))
        d.setitem(2, 300)
        d.show()
        d.remove(3)
        d.show()
        d.delete(40)
        d.show()
        d.add(40)
        d.insert(3, 40)
        d.insert(3, 40)
        d.append(40)
        d.append(40)
        d.show()
        d.delete_all(40)
        d.show()

    运行结果:

    C:Anaconda3python.exe "C:Program FilesJetBrainsPyCharm 2019.1.1helperspydevpydevconsole.py" --mode=client --port=53914
    import sys; print('Python %s on %s' % (sys.version, sys.platform))
    sys.path.extend(['C:\app\PycharmProjects', 'C:/app/PycharmProjects'])
    Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)]
    Type 'copyright', 'credits' or 'license' for more information
    IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
    PyDev console: using IPython 7.12.0
    Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)] on win32
    runfile('C:/app/PycharmProjects/DataStructure/LinkedList/Dlist.py', wdir='C:/app/PycharmProjects/DataStructure/LinkedList')
    is_empty:  True
    空链表
    100←→10←→20←→30←→40
    100←→200←→10←→20←→30←→40
    链表长度: 6
    True
    3
    100←→200←→300←→20←→30←→40
    100←→200←→300←→30←→40
    100←→200←→300←→30
    40←→100←→200←→40←→40←→300←→30←→40←→40
    100←→200←→300←→30
  • 相关阅读:
    结构~函数~输入输出
    常用缀名
    结构
    枚举
    int argc char*argv[]
    字符串的操作
    字符串函数#include<string.h>
    指针的应用
    2019.1.25~2019.1.30学习总结
    v-for
  • 原文地址:https://www.cnblogs.com/huanghanyu/p/13404901.html
Copyright © 2011-2022 走看看