==双向链表python实现==

双向链表

双向链表简介

双向链表是一种数据结构,它由一系列节点组成,每个节点包含两个指针:一个指向前一个节点,一个指向后一个节点。这使得在双向链表中可以从任意节点开始,通过前后指针访问相邻节点。

优缺点:双向链表相比单向链表可以更方便地进行前后遍历和删除操作,但也因为多了一个指针而增加了内存消耗。

双向链表的结构

每一个节点新增加了一个prev属性即指向其前一个结点

特殊的:

头节点的前驱节点=None

尾节点的前驱节点=None

在这里插入图片描述

框架建立

每一个节点新增加了一个prev属性即指向其前一个结点

class Node():
    def __init__(self,data,_next = None,_prev = None):
        self.prev = _prev   # 新增
        self.data = data
        self.next = _next

class Doublelinklist():
    def __init__(self):
        self.head = None
        self.lenth = 0

双向链表具体的操作

在这里插入图片描述

链表是否为空

class Doublelinklist():
    def __init__(self):
        self.head = None
        self.lenth = 0

    def is_empty(self):
        return self.lenth == 0

在链表的头部添加结点

在这里插入图片描述

    def add(self,data):
        '''
        在链表的头部添加结点
        正常情况下
        1.创建新的结点
        2.将原来的头节点的前驱属性指向新的节点
        3.将新节点的next属性指向下一节点
        4.头节点指向新的结点
        特殊的
        当链表为空的时候
        只用将head指向结点即可
        :param data:
        :return:
        '''
        node = Node(data)
        if self.is_empty():
            self.head = node
        else:
            self.head.prev = node   # 让链表中原本的头结点的prev指向新建的节点
            node.next = self.head  
            self.head = node
        self.lenth += 1

将链表中的所有的节点的值装进列表

两种思路如下1,2

这里的循环遍历可以有两种方法(在此我们两种方法都采用以下)
1.通过链表本身的长度进行遍历
2.通过节点是否有内容进行遍历

    def nodes_list_1(self):
        '''
        将链表中的所有的data添加到列表中
        1.遍历循环所有的链表结点
        2.将每个节点的data属性添加到列表中
        3.返回列表
        附加:
        这里的循环遍历可以有两种方法(在此我们两种方法都采用以下)
        1.通过链表本身的长度进行遍历
        2.通过节点是否有内容进行遍历
        :return:
        '''
        res = []
        a = self.lenth
        cur = self.head
        while a:
            res.append(cur.data)
            cur = cur.next
            a -= 1
        return res

    def nodes_list_2(self):
        '''
        通过节点是否有内容进行遍历
        将链表中的所有的data添加到列表中
        1.遍历循环所有的链表结点
        2.将每个节点的data属性添加到列表中
        3.返回列表
        :return:
        '''
        res = []
        cur = self.head
        while cur:
            res.append(cur.data)
            cur = cur.next
        return res

尾部添加结点

在这里插入图片描述

同样的在此处的循环遍历方式依旧给出两种

两种方法的本质区别就在于寻找尾部结点的循环方法不同

  • 一种判断next属性是否为空值
  • 根据长度确定稳定的循环次数
    def append1(self,data):
        # 在链表的尾部添加结点
        '''
        1.创建新的结点node
        2.循环遍历找到最后一个结点
        3.将新节点的prev属性指向尾部结点
        4.将尾部节点的next属性指向新节点
        5.长度加1
        附加:注意判断当链表是空链表的时候如何进行操作
        创建新的结点
        直接将self.head = node即可
        :return:
        '''
        node = Node(data)
        cur = self.head
        if cur:
            while cur.next:  
                '''
                尾结点cur可以进入循环,但是尾结点的next属性不可进入循环,脱离循环的时候保证cur是尾结点,故 循环条件为下:
                while cur.next:  
                '''
                cur = cur.next
            node.prev = cur
            cur.next = node
        else:
            self.head = node
        self.lenth += 1

    def append2(self,data):
        node = Node(data)
        cur = self.head
        if cur:
            a = self.lenth - 1
            while a:
                cur = cur.next
                a -= 1
            node.prev = cur
            cur.next = node
        else:
            self.head = node
        self.lenth += 1

往链表中的指定的位置插入结点值为data

在这里插入图片描述

如上图所示:

正常情况下

  1. 创建新的结点
  2. 找到索引为pos-1的结点
  3. 将新节点的next属性指向原来的pos结点(3和4的位置不可更换)
  4. 将pos-1结点的next属性指向新的结点
  5. 将原来的pos结点的prev属性指向新的结点
  6. 新节点的prev属性指向pos-1结点
  7. 长度加1

非正常情况下

无非就是pos超出范围加上if条件判断即可

    def insert(self,pos,data):
        # 往链表中的指定的位置添加一个节点值为data
        if pos <= 0:
            self.add(data)
        elif pos > self.lenth:
            self.append2(data)
        else:
            node = Node(data)
            a = pos - 1
            cur = self.head
            while a :  # 循环出来就是pos-1结点
                cur = cur.next
                a -= 1
            # 将新节点的next属性指向原来的pos结点
            node.next = cur.next
            # 将pos-1结点的next属性指向新的结点
            cur.next = node
            # 将原来的pos结点的prev属性指向新的结点
            cur.next.prev = node
            # 新节点的prev属性指向pos - 1结点
            node.prev = cur
            self.lenth += 1    # 记得缩进

值得注意的是:上述代码中22行self.lenth += 1必须要缩进到else:中

因为两个if内层调用的方法本身就会带有self.lenth += 1故不用一同执行该行

删除链表中第一个值为data的结点

在这里插入图片描述

正常情况下:

  1. 找到值为data的结点cur
  2. 将cur的前一个结点的next属性指向cur的后一个节点
  3. 将cur的后一个节点的prev属性指向cur的前一个结点
  4. 长度减1

特殊情况下:

  • 如果链表的data在头部结点

    那么将self.head属性指向cur的下一个结点即可(第二个节点)

    将cur的下一个结点的prev属性指向None(这步千万别忘了,虽然说不会影响程序的运行,但是会造成程序资源的浪费)

  • 如果删除的结点是尾部结点的话

    只用将他的前一个结点的next属性换成None即可

    def remove(self,data):
        # 删除第一个值为data的结点
        cur = self.head
        while cur:
            if cur.data == data:
                # 进行删除操作
                if cur == self.head:   # 头节点的删除方式
                    self.head = cur.next
                else:
                    cur.prev.next = cur.next
                if cur.next:           # 尾结点的话不执行下面的代码不执行
                    cur.next.prev = cur.prev
                self.lenth -= 1
                return 0
            cur = cur.next
        return -1

修改链表中指定位置的值

正常情况下

  1. 循环遍历找到指定位置的结点
  2. 将指定位置的结点的data属性改为指定的data

非正常情况下

无非就是给定的索引不在范围中(加上if条件判断即可)

    def modify(self,pos,data):
        if 0 <= pos < self.lenth:
            cur = self.head
            while pos:
                cur = cur.next
                pos -= 1
            cur.data = data
        else:
            print('索引不在范围内')

判断链表中是否有值为data的结点

通过结点对象循环遍历每一个结点 判断其值是否为data如果是 返回索引值 否则 返回None

如果对象为None,即到达尾结点,结束循环。

代码如下

    def search(self,data):
        # 查找链表中是否有值为data的结点
        cur = self.head
        count = 0
        while cur:
            if cur.data == data:
                return f'找到该数据了,其索引为{count}'
            else:
                cur = cur.next
                count += 1
        return '未找到data的结点'

双向循环链表的python实现完整代码

# @Author  :泰敢人辣
# Date     :2023-07-25 18:34

class Node():
    def __init__(self,data,_next = None,_prev = None):
        self.prev = _prev
        self.data = data
        self.next = _next

class Doublelinklist():
    def __init__(self):
        self.head = None
        self.lenth = 0

    def is_empty(self):
        return self.lenth == 0

    def nodes_list_1(self):
        '''
        将链表中的所有的data添加到列表中
        1.遍历循环所有的链表结点
        2.将每个节点的data属性添加到列表中
        3.返回列表
        附加:
        这里的循环遍历可以有两种方法(在此我们两种方法都采用以下)
        1.通过链表本身的长度进行遍历
        2.通过节点是否有内容进行遍历
        :return:
        '''
        res = []
        a = self.lenth
        cur = self.head
        while a:
            res.append(cur.data)
            cur = cur.next
            a -= 1
        return res

    def nodes_list_2(self):
        '''
        通过节点是否有内容进行遍历
        将链表中的所有的data添加到列表中
        1.遍历循环所有的链表结点
        2.将每个节点的data属性添加到列表中
        3.返回列表
        :return:
        '''
        res = []
        cur = self.head
        while cur:
            res.append(cur.data)
            cur = cur.next
        return res



    def add(self,data):
        '''
        在链表的头部添加结点
        正常情况下
        1.创建新的结点
        2.将原来的头节点的前驱属性指向新的节点
        3.将新节点的next属性指向下一节点
        4.头节点指向新的结点
        特殊的
        当链表为空的时候
        只用将head指向结点即可
        :param data:
        :return:
        '''
        node = Node(data)
        if self.is_empty():
            self.head = node
        else:
            self.head.prev = node   # 让链表中原本的头结点的prev指向新建的节点
            node.next = self.head
            self.head = node
        self.lenth += 1

    def append1(self,data):
        # 在链表的尾部添加结点
        '''
        1.创建新的结点node
        2.循环遍历找到最后一个结点
        3.将新节点的prev属性指向尾部结点
        4.将尾部节点的next属性指向新节点
        5.长度加1
        附加:注意判断当链表是空链表的时候如何进行操作
        创建新的结点
        直接将self.head = node即可
        :return:
        '''
        node = Node(data)
        cur = self.head
        if cur:
            while cur.next:
                cur = cur.next
            node.prev = cur
            cur.next = node
        else:
            self.head = node
        self.lenth += 1

    def append2(self,data):
        node = Node(data)
        cur = self.head
        if cur:
            a = self.lenth - 1
            while a:
                cur = cur.next
                a -= 1
            node.prev = cur
            cur.next = node
        else:
            self.head = node
        self.lenth += 1

    def insert(self,pos,data):
        # 往链表中的指定的位置添加一个节点值为data
        '''
        1. 创建新的结点
        2. 找到索引为pos-1的结点
        3. 将pos-1结点的next属性指向新的结点
        4. 将新节点的next属性指向原来的pos结点
        5. 将原来的pos结点的prev属性指向新的结点
        6. 新节点的prev属性指向pos-1结点
        7. 长度加1
        注意3456的顺序有的时候需要进行更换呦
        :param pro:
        :param data:
        :return:
        '''

        if pos <= 0:
            self.add(data)
        elif pos > self.lenth:
            self.append2(data)
        else:
            node = Node(data)
            a = pos - 1
            cur = self.head
            while a :  # 循环出来就是pos-1结点
                cur = cur.next
                a -= 1
            # 将新节点的next属性指向原来的pos结点
            node.next = cur.next
            # 将pos-1结点的next属性指向新的结点
            cur.next = node
            # 将原来的pos结点的prev属性指向新的结点
            cur.next.prev = node
            # 新节点的prev属性指向pos - 1结点
            node.prev = cur
            '''
            注意这里需要缩进带else中,因为上面两个if的内层调用的方法会自动带有self.lenth+=1的代码
            所以在这个插入方法中不用再进行执行了!!!
            '''
            self.lenth += 1

    def remove(self,data):
        # 删除第一个值为data的结点
        '''
        正常情况下:
        1. 找到值为data的结点cur
        2. 将cur的前一个结点的next属性指向cur的后一个节点
        3. 将cur的后一个节点的prev属性指向cur的前一个结点
        4. 长度减1
        特殊情况下:
        - 如果链表的data在头部结点
            那么将self.head属性指向cur的下一个结点即可(第二个节点)
            将cur的下一个结点的prev属性指向None(这步千万别忘了,虽然说不会影响程序的运行,但是会造成程序资源的浪费)
        - 如果删除的结点是尾部结点的话
            只用将他的前一个结点的next属性换成None即可
        :param data:
        :return:
        '''
        cur = self.head
        while cur:
            if cur.data == data:
                # 进行删除操作
                if cur == self.head:   # 头节点的删除方式
                    self.head = cur.next
                else:
                    cur.prev.next = cur.next
                if cur.next:           # 尾结点的话不执行下面的代码
                    cur.next.prev = cur.prev
                self.lenth -= 1
                return 0
            cur = cur.next
        return -1

    def modify(self,pos,data):
        if 0 <= pos < self.lenth:
            cur = self.head
            while pos:
                cur = cur.next
                pos -= 1
            cur.data = data
        else:
            print('索引不在范围内')

    def search(self,data):
        # 查找链表中是否有值为data的结点
        '''
        循环遍历每一个结点  判断其值是否为data
        如果是 返回索引值 否则 返回None
        :param data:
        :return:
        '''
        cur = self.head
        count = 0
        while cur:
            if cur.data == data:
                return f'找到该数据了,其索引为{count}'
            else:
                cur = cur.next
                count += 1
        return '未找到data的结点'

if __name__ == '__main__':
    l1 = Doublelinklist()
    l1.add(1)
    l1.add(2)
    l1.add(3)
    l1.add(4)
    l1.add(5)
    print(l1.nodes_list_1())
    l1.remove(1)
    print(l1.nodes_list_1())
    l1.modify(0,11111)
    print(l1.nodes_list_1())
    print(l1.search(11111))

附件

附件1:双向链表寻找尾结点(同单向)

cur = self.head
while cur.next:
	cur = cur.next

附件2:通过索引寻找指定的结点

'''
pos为想要找的结点的索引
'''
pos = 10    # 将10替换成指定的索引
cur = self.head
while pos:
    cur = cur.next
    pos -= 1

其他有关链表的技巧详见博主原创文章==附件内容

链接1: 单向链表Python===>寻找尾结点

链接2: 单向循环链表====>>>找循环链表的尾部节点

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TAGRENLA

您的打赏是我创作的动力,谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值