数据结构学习——05—双向链表

在这里插入图片描述
双向链表的知识点很多都和单向链表相同,函数实现方法也差不多,只是需要注意每个节点的prev需要指向上一个节点。

双向链表的节点实现:

'''双向链表'''
# 节点实现
class Node(object):
    def __init__(self, item):
        self.item = item
        
        # 前后指向地址暂定为None
        self.prev = None
        self.next = None

需要注意的就是地址指向有两个prev和next,初始值都设为None

双向链表实现:

# 双向链表实现
class DoubleLinkList(object):
    def __init__(self, node=None):
        self.__head = node          

因为双向链表中的首地址指向None,所以__init__函数中参量默认为node=None,假如传入参数node,不是空链表了,此时self.__head指向node。

双向链表的增删改查等函数实现:

1、判空函数:

 def is_empty(self):
        if self.__head == None:
            return True
        else:
            return False

2、链表长度函数:

# 链表长度
    def length(self):
        # 考虑空链表情况
        if self.is_empty():
            return 0
        
        # cur游标  指向首节点,用来遍历  相当于指针
        cur = self.__head  # 游标指向首地址
        count = 0          # 注意此时的初始值和非空情况的判断条件的关系

        # 非空情况下
        while cur != None:     # 初始值为0 的判断条件
            count += 1
            cur = cur.next
        return count
        
        # 另一种判空情况
        # while cur.next != None:         # 找到尾节点
        #     count += 1
        #     cur = cur.next              # 继续循环
        # return count

这里注意的是while虚表还条件的选择,这个与count的初始值设定有关,还关乎尾节点是否在循环内的情况。
代码中注释掉的一部分代码,就是另外一种的while循环条件,相对应的count=1,此时尾节点的数据没有进入到循环内,需要单独考虑。

3、遍历链表

# 遍历链表
    def travel(self):
        cur = self.__head
    
        # 考虑空链表情况
        if self.is_empty():
            return None
        
        # 非空链表的情况下:
        while cur != None:
            print(cur.item, end=' ')
            cur = cur.next           # 因为判空条件的不同,尾节点已经在循环中
        # while cur.next != None:
        #     print(cur.item)
        #     cur = cur.next
        #
        # print(cur.next)  # 输出结尾结点

和上面的函数实现相同,while循环条件的选择,和输出语句相关,注释掉的代码是另一种实现函数的方法。

4、链表头部插入元素

    def add(self, item):
        node = Node(item)
        
        # 空链表
        if self.is_empty():
            self.__head = node
        else:
            # 非空链表
            # 第一步:将新的节点的地址指向初始链表的第一个元素
            node.next = self.__head
            
            # 第二步:将__head的头节点的prev指向node
            # cur.prev = node
            self.__head.prev = node
            
            # 第三步:将__head指向node
            # node.prev = None
            self.__head = node

这里需要注意的是:注释掉的代码是一种错误的思路,需要区分开。注释掉的代码是没有明确cur,prev和next代表的含义是什么,这些都是指向节点的数据地址的“指针”。如下图所示:
图中箭头代表的是指向的地址,代码编写的顺序应该是从右往左编写代码,否则会出现地址指向错误,顺序很重要!!!
在这里插入图片描述

5、链表尾部插入数据

    # 尾部插入新节点
    def append(self, item):
        node = Node(item)
        
        # 空链表
        if self.is_empty():
            self.__head = node
        else:
            # 非空链表
            cur = self.__head
            while cur.next != None:     # 这里不同于之前的临界条件
                # 找到最后一个节点
                cur = cur.next
            # 循环结束之后,cur指向尾部结点
            cur.next = node
            node.prev = cur

这里的while循环条件特殊一点:因为是在尾部插入,找到的节点需要是最后一个节点,节点的next为None。
链表尾部插入数据
图中箭头代表的是指向的地址,代码编写的顺序应该是从右往左编写代码,否则会出现地址指向错误,顺序很重要!!!

6、链表任意位置插值

# 任意位置插值
    def insert(self, pos, item):
        
        # pos负数,头部插入
        if pos <= 0:
            self.add(item)
        # 尾部节点插入
        elif pos > self.length() - 1:
            self.append(item)
        
        # 中间位置插入
        else:
            node = Node(item)
            cur = self.__head
            count = 0
            
            # 在链表长度内循环
            while count < (pos - 1):
                count += 1
                cur = cur.next
            # 循环结束
            # 插入的节点next指向游标所指的下一个节点
            node.next = cur.next
            # 新节点的prev指向游标所指的节点
            node.prev = cur
            # 游标所指下一个节点的prev指向新结点
            cur.next.prev = node
            # 游标所在的节点的next指向新节点
            cur.next = node

示意图如下:
图中箭头代表的是指向的地址,代码编写的顺序应该是从右往左编写代码,这里最主要的是两个指向需要先写:1、插入的节点next指向游标所指的下一个节点;2、新节点的prev指向游标所指的节点;这两个顺序需要在(游标所指下一个节点的prev指向新结点)(游标所在的节点的next指向新节点)之前
在这里插入图片描述

7、查找节点是否存在

# 查找结点是否存在
    def search(self, item):
        cur = self.__head
        # 循环查找
        while cur != None:
            if cur.item == item:
                return True
            else:
                # 游标继续执行
                cur = cur.next
        return False

8、删除链表节点

# 删除链表节点
    def remove(self, item):
        if self.is_empty():
            return None
        else:
            cur = self.__head
            # 首结点的元素就是要删除的元素
            if cur.item == item:
                # 如果链表只有一个结点
                if cur.next == None:
                    self.__head = None          # 首地址指向None
                else:                           # 链表中不止一个节点数据
                    cur.next.prev = None        # 下一个节点的prev指向None
                    self.__head = cur.next      # 首地址只想下个节点
                return None

            while cur != None:    # 循环到尾部节点数据之前
                if cur.item == item:     # 循环过程中找到需要的数据
                    cur.prev.next = cur.next         # 当前要删除节点的prev,即上一个节点,上一个节点的next指向删除后的节点
                    if cur.next:                     # 如果删除节点的下一个节点不是尾部节点
                        cur.next.prev = cur.prev     # 下一个节点的prev指向删除节点的前一个节点
                    break                            # 跳出循环
                cur = cur.next                       # 继续循环

示意图如下:
在这里插入图片描述

代码整体的实现:

# -*- encoding: utf-8 -*-
"""
@File    : double_link_list.py
@Time    : 2019/11/13 15:03
@Author  : chen

"""

'''双向链表'''

# 节点实现
class Node(object):
    def __init__(self, item):
        self.item = item
        
        # 前后指向地址暂定为None
        self.prev = None
        self.next = None
        

# 双向链表实现
class DoubleLinkList(object):
    def __init__(self, node=None):
        self.__head = node              #
        
            
    def is_empty(self):
        if self.__head == None:
            return True
        else:
            return False
        
    # 链表长度
    def length(self):
        # 考虑空链表情况
        if self.is_empty():
            return 0
        
        # cur游标  指向首节点,用来遍历  相当于指针
        cur = self.__head  # 游标指向首地址
        count = 0          # 注意此时的初始值和非空情况的判断条件的关系

        # 非空情况下
        while cur != None:     # 初始值为0 的判断条件
            count += 1
            cur = cur.next
        return count
        
        # 另一种判空情况
        # while cur.next != None:         # 找到尾节点
        #     count += 1
        #     cur = cur.next              # 继续循环
        # return count
    
    # 遍历链表
    def travel(self):
        cur = self.__head
    
        # 考虑空链表情况
        if self.is_empty():
            return None
        
        # 非空链表的情况下:
        while cur != None:
            print(cur.item, end=' ')
            cur = cur.next           # 因为判空条件的不同,尾节点已经在循环中
        # while cur.next != None:
        #     print(cur.item)
        #     cur = cur.next
        #
        # print(cur.next)  # 输出结尾结点
    
    # 头部插入元素
    def add(self, item):
        node = Node(item)
        
        # 空链表
        if self.is_empty():
            self.__head = node
        else:
            # 非空链表
            # 第一步:将新的节点的地址指向初始链表的第一个元素
            node.next = self.__head
            
            # 第二步:将__head的头节点的prev指向node
            # cur.prev = node
            self.__head.prev = node
            
            # 第三步:将__head指向node
            # node.prev = None
            self.__head = node
        
    # 尾部插入新节点
    def append(self, item):
        node = Node(item)
        
        # 空链表
        if self.is_empty():
            self.__head = node
        else:
            # 非空链表
            cur = self.__head
            while cur.next != None:     # 这里不同于之前的临界条件
                # 找到最后一个节点
                cur = cur.next
            # 循环结束之后,cur指向尾部结点
            cur.next = node
            node.prev = cur
            
    # 任意位置插值
    def insert(self, pos, item):
        
        # pos负数,头部插入
        if pos <= 0:
            self.add(item)
        # 尾部节点插入
        elif pos > self.length() - 1:
            self.append(item)
        
        # 中间位置插入
        else:
            node = Node(item)
            cur = self.__head
            count = 0
            
            # 在链表长度内循环
            while count < (pos - 1):
                count += 1
                cur = cur.next
            # 循环结束
            # 插入的节点next指向游标所指的下一个节点
            node.next = cur.next
            # 新节点的prev指向游标所指的节点
            node.prev = cur
            # 游标所指下一个节点的prev指向新结点
            cur.next.prev = node
            # 游标所在的节点的next指向新节点
            cur.next = node
    
    # 查找结点是否存在
    def search(self, item):
        cur = self.__head
        # 循环查找
        while cur != None:
            if cur.item == item:
                return True
            else:
                # 游标继续执行
                cur = cur.next
        return False
    
    # 删除链表节点
    def remove(self, item):
        if self.is_empty():
            return None
        else:
            cur = self.__head
            # 首结点的元素就是要删除的元素
            if cur.item == item:
                # 如果链表只有一个结点
                if cur.next == None:
                    self.__head = None          # 首地址指向None
                else:                           # 链表中不止一个节点数据
                    cur.next.prev = None        # 下一个节点的prev指向None
                    self.__head = cur.next      # 首地址只想下个节点
                return None

            while cur != None:    # 循环到尾部节点数据之前
                if cur.item == item:     # 循环过程中找到需要的数据
                    cur.prev.next = cur.next         # 当前要删除节点的prev,即上一个节点,上一个节点的next指向删除后的节点
                    if cur.next:                     # 如果删除节点的下一个节点不是尾部节点
                        cur.next.prev = cur.prev     # 下一个节点的prev指向删除节点的前一个节点
                    break                            # 跳出循环
                cur = cur.next                       # 继续循环
    
  
    
if __name__ == '__main__':
    d = DoubleLinkList()
    print(d.is_empty())
    print(d.length())
    d.add(1)
    d.add(2)
    d.append(0)
    d.insert(2, 5)
    d.insert(-1, 9)
    d.insert(10,100)
    d.travel()
    print(d.is_empty())
    print(d.length())
        
        
 
发布了50 篇原创文章 · 获赞 9 · 访问量 2101

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/103084560