python-链表

1. 链表

"""
背景:
    列表在存储数据的时候, 需要使用到连续的空间, 如果空间不够, 就会导致扩容失败, 针对于这种情况, 可以通过链表实现
    链表在内存中存储的时候, 可以不是连续的空间, "有地儿就行", 所以: 增删相对更好操作

链表介绍:
    概述:
        属于线性结构, 即: 每个节点(除头没有前置节点,尾部没有后置节点)都只有1个前置节点 和 1个后置节点
    组成:
        链表是由节点组成的, 根据节点的不同, 链表又分为: 单向链表, 单向循环链表, 双向链表, 双向循环链表
        节点划分:
            单向链表, 单向循环链表:
                节点是由 1个数值域 和 1个地址域组成, 也叫: 元素域 和 链接域组成
            双向链表, 双向循环链表:
                节点是由 1个数值域 和 2个地址域组成, 也叫: 元素域 和 链接域组成
    划分:
        单向链表:         节点是由 1个数值域 和 1个地址域组成, 最后1个节点的地址域为: None
        单向循环链表:      节点是由 1个数值域 和 1个地址域组成, 最后1个节点的地址域为: 第1个节点的 地址
        双向链表:         节点是由 1个数值域 和 2个地址域组成, 分别指向前一个节点 和 后一个节点的地址, 第1个节点的前地址域 和 最后1个节点的后地址域为 None
        双向循环链表:      1个数值域, 2个地址域.  第1个节点的前地址域指向最后1个节点的地址, 最后1个节点的后地址域指向第1个节点的 地址
"""

1.1 单向链表


"""
需求: 通过面向对象思维, 实现自定义链表

分析流程:
    节点类: SingleNode
        属性:
            item    代表: 数值域
            next    代表: (下个节点的)地址域

    单向链表类: SingleLinkedList:
        属性:
            head    代表: 链表的第一个节点(头结点), 如无, 则为: None
        行为:
            isEmpty(self) 链表是否为空
            length(self) 链表长度
            showAll(self) 遍历整个链表
            addFront(self, item) 链表头部添加元素
            append(self, item) 链表尾部添加元素
            insert(self, idx, item) 指定位置添加元素
            remove(self, item) 删除节点
            contains(self, item) 查找节点是否存在
"""

class SingleNode():
    """
    创建 单向链表 节点 对象
    """

    def __init__(self, item):
        """
        创建SingleNode对象时,自动调用该方法,初始化对象属性
        :param item:
        """
        # 节点数据
        self.item = item
        # next 指针指向下一个节点
        self.next = None

    def __str__(self):
        """
        print(SingleNode)对象时自动调用,返回对象属性值
        :return:
        """
        return f'item={self.item}'


class SingleLinkedList():
    """
    创建 单向链表 对象
    """

    # 初始化属性, 用 head属性, 指向: 链表的头结点
    def __init__(self, node=None):
        self.head = node

    # isEmpty(self) 链表是否为空
    def isEmpty(self):
        """
        判断链表是否为空.
        :return: True => 为空, False => 不为空
        """
        return self.head is None

    # length(self) 链表长度
    def length(self):
        # 定义变量 count = 0 表示链表的长度
        count = 0
        # 定义变量 cur, 表示: 当前节点, 它是从 头结点开始 往下逐个获取的
        cur = self.head
        # 循环往后获取每个节点即可, 只要 cur不为None, 就说明还有节点
        while cur is not None:
            # 走这里, 说明有节点, 计数器加1, 然后设置 cur为 当前节点的下个节点即可
            count += 1
            cur = cur.next
        return count

    # showAll(self) 遍历整个链表, 打印每个节点的 数值域(的值)
    def showAll(self):
        # 定义变量 cur, 表示: 当前节点, 它是从 头结点开始 往下逐个获取的
        cur = self.head
        # 循环往后获取每个节点即可, 只要 cur不为None, 就说明还有节点
        while cur is not None:
            # 走这里, 说明有节点, 保存当前节点的 元素域(数值域) item属性
            yield cur.item
            # 重新设置 cur 为: 当前节点的下个节点
            cur = cur.next

    # addFront(self, item) 链表头部添加元素
    def addFront(self, item):
        # 把 item 封装成 节点
        new_node = SingleNode(item)
        # 设置 新节点的地址域 指向 之前旧的头结点
        new_node.next = self.head
        # 设置新节点为: 新的头结点
        self.head = new_node

    # append(self, item) 链表尾部添加元素
    def append(self, item):
        # 把 item 封装成 节点
        new_node = SingleNode(item)
        # 判断链表是否为空, 如果为空, 则: 新节点充当头结点即可
        if self.isEmpty():
            # 为空, 新节点充当头结点即可
            self.head = new_node
        else:
            # 不为空, 找到 最后1个节点, 设置它的 地址域为 新节点即可
            # cur代表当前的节点, 从头结点往后 逐个获取
            cur = self.head
            while cur.next is not None:
                # 获取当前节点的 下个节点
                cur = cur.next
            # 走这里, cur就是最后1个节点, 设置它的地址域为: 新节点即可
            cur.next = new_node

    # insert(self, idx, item) 指定位置添加元素
    def insert(self, idx, item):
        """
        往指定的位置, 添加元素
        :param idx: 要添加元素到的 位置(索引)
        :param item: 具体的要添加的元素
        :return: 无
        """
        # 如果 idx 的值 <= 0, 就往: 头部添加
        if idx <= 0:
            self.addFront(item)
        # 如果 idx 的值 >= 链表的长度, 就往: 尾部添加
        elif idx >= self.length():
            self.append(item)
        # 走到这里, 说明, 插入位置, 是在链表中间. 需要找到 插入位置前的哪个节点
        else:
            # 定义变量 count 用于表示: 插入位置前的那个元素的 索引
            count = 0
            # 定义变量 cur, 表示: 当前元素(节点), 找到: 插入位置前的哪个节点
            cur = self.head
            # 遍历, 判断: 只要 count的值 < idx - 1要小, 就一直循环  循环结束后: count = idx - 1
            while count < idx - 1:
                count += 1
                cur = cur.next
            # 设置 新节点的地址域为: cur节点的地址域
            new_node = SingleNode(item)
            new_node.next = cur.next
            # 设置 cur节点的地址域为: 新节点(的地址)
            cur.next = new_node

    # remove(self, item) 删除节点
    def remove(self, item):
        # 定义变量 cur, 记录: 当前的节点, 默认: 从头结点开始
        cur = self.head
        # 定义变量 pre, 记录: 当前节点的 前1个节点(前置节点)
        pre = None
        # 遍历, 获取到每个节点
        while cur is not None:
            # 判断当前节点是否是要被删除的节点, 即: 当前节点的 数值域 是否等于 item参数
            if cur.item == item:
                # 走这里, 说明 cur就是要被删除的节点
                # 有可能要删除的节点是: 头结点
                if cur == self.head:
                    # cur的下个节点作为新的 头结点即可
                    self.head = cur.next
                    # 断开旧的头结点 和 后续节点的链接
                    cur = None
                else:
                    # 要删除的节点不是头结点
                    pre.next = cur.next
                    # 断开当前节点(要被删除的节点) 和 后续节点的链接
                    cur = None
                # 核心细节: 删除之后, 程序结束
                break
            else:
                # 走这里, 说明 cur不是要被删除的节点, 继续往后拿
                # pre表示刚才判断过的节点
                pre = cur
                # 更新cur为它的下个节点
                cur = cur.next

    # contains(self, item) 查找节点是否存在
    def contains(self, item):
        # 定义cur变量, 从头结点开始往后找
        cur = self.head
        # 循环获取到每个节点
        while cur is not None:
            # 判断当前节点的 元素域(数值域) 是否 和 要查找的值 一致
            if cur.item == item:
                # 找到了
                return True
            # 不匹配, 就开始校验下个节点
            cur = cur.next
        # 走到这里, 整个while都结束了, 即: 没有找到
        return False


if __name__ == '__main__':

    sll = SingleLinkedList()
    sll.append('张飞')
    sll.append('刘备')
    sll.append('关羽')
    sll.addFront('曹操')
    sll.insert(2, '郭嘉')
    print(f'链表中是否存在刘备: {sll.contains("刘备")}')
    sll.remove('刘备')
    print(f'链表中是否存在刘备: {sll.contains("刘备")}')
    for i in sll.showAll():
        print(i)

控制台输出

链表中是否存在刘备: True
链表中是否存在刘备: False
曹操
张飞
郭嘉
关羽

1.2 双向链表


class DualNode():
    """
    创建 双向链表 节点 对象
    """
    def __init__(self, item):
        """
        创建DualNode对象时,自动调用该方法,初始化对象属性
        :param item:
        """
        # 节点数据
        self.item = item
        # next 指针指向下一个节点
        self.next = None
        # pre 指针指向上一个节点
        self.pre = None

    def __str__(self):
        """
        print(DualNode)对象时自动调用,返回对象属性值
        :return:
        """
        return f'item={self.item}'


class DualLinkedList():
    """
    创建 双向链表 节点 对象
    """
    def __init__(self, node=None):
        self.head = node
        self.tail = node
        self.size = 0 if node is None else 1

    # isEmpty(self) 链表是否为空
    def isEmpty(self):
        return self.head is None

    # length(self) 链表长度
    def length(self):
        return self.size

    # showAll(self) 遍历整个链表, 打印每个节点的 数值域(的值)
    def showAll(self, reverse=False):
        """
        reverse True 从尾部开始遍历, False 从头部开始遍历
        :param reverse:
        :return: 链表数据生成器
        """
        if reverse:
            # 定义变量 cur, 表示: 当前节点, 它是从 尾结点开始 往下逐个获取的
            cur = self.tail
            # 循环往前获取每个节点即可, 只要 cur不为None, 就说明还有节点
            while cur is not None:
                # 走这里, 说明有节点, 保存当前节点的 元素域(数值域) item属性
                yield cur.item
                # 重新设置 cur 为: 当前节点的上个节点
                cur = cur.pre
        else:
            # 定义变量 cur, 表示: 当前节点, 它是从 头结点开始 往下逐个获取的
            cur = self.head
            # 循环往后获取每个节点即可, 只要 cur不为None, 就说明还有节点
            while cur is not None:
                # 走这里, 说明有节点, 保存当前节点的 元素域(数值域) item属性
                yield cur.item
                # 重新设置 cur 为: 当前节点的下个节点
                cur = cur.next

    # addFront(self, item) 链表头部添加元素
    def addFront(self, item):
        # 把 item 封装成 节点
        new_node = DualNode(item)
        if self.isEmpty():
            # 为空, 新节点充当头尾结点即可
            self.head = new_node
            self.tail = new_node
        else:
            new_node.next = self.head
            self.head.pre = new_node
            # 设置 新节点的地址域 指向 之前旧的头结点
            self.head = new_node
        self.size += 1

    # append(self, item) 链表尾部添加元素
    def append(self, item):
        # 把 item 封装成 节点
        new_node = DualNode(item)
        if self.isEmpty():
            # 为空, 新节点充当头尾结点即可
            self.head = new_node
            self.tail = new_node
        else:
            new_node.pre = self.tail
            self.tail.next = new_node
            # 设置 新节点的地址域 指向 之前旧的尾结点
            self.tail = new_node
        self.size += 1

    # insert(self, idx, item) 指定位置添加元素
    def insert(self, idx, item):
        """
        往指定的位置, 添加元素
        :param idx: 要添加元素到的 位置(索引)
        :param item: 具体的要添加的元素
        :return: 无
        """
        # 如果 idx 的值 <= 0, 就往: 头部添加
        if idx <= 0:
            self.addFront(item)
        # 如果 idx 的值 >= 链表的长度, 就往: 尾部添加
        elif idx >= self.length():
            self.append(item)
        # 走到这里, 说明, 插入位置, 是在链表中间. 需要找到 插入位置前的哪个节点
        else:
            # 定义变量 count 用于表示: 插入位置前的那个元素的 索引
            count = 0
            # 定义变量 cur, 表示: 当前元素(节点), 找到: 插入位置前的哪个节点
            cur = self.head
            # 遍历, 判断: 只要 count的值 < idx - 1要小, 就一直循环  循环结束后: count = idx - 1
            while count < idx - 1:
                count += 1
                cur = cur.next
            new_node = DualNode(item)
            # 设置 新节点的地址域为: cur节点的地址域
            new_node.next = cur.next
            cur.next.pre = new_node
            # 设置 cur节点的地址域为: 新节点(的地址)
            cur.next = new_node
            new_node.pre = cur
        self.size += 1
        
    # remove(self, item) 删除节点
    def remove(self, item):
        # 定义变量 cur, 记录: 当前的节点, 默认: 从头结点开始
        cur = self.head
        # 定义变量 pre, 记录: 当前节点的 前1个节点(前置节点)
        preNode = None
        # 遍历, 获取到每个节点
        while cur is not None:
            # 判断当前节点是否是要被删除的节点, 即: 当前节点的 数值域 是否等于 item参数
            if cur.item == item:
                # 走这里, 说明 cur就是要被删除的节点
                # 有可能要删除的节点是: 头结点
                if cur == self.head:
                    # cur的下个节点作为新的 头结点即可
                    self.head = cur.next
                    # 断开旧的头结点 和 后续节点的链接
                    if self.head is not None:
                        cur.next.pre = None
                    else:
                        # 头节点为空 也把尾节点置空
                        self.tail = None
                elif cur == self.tail:
                    self.tail = preNode
                    preNode.next = None
                else:
                    # 要删除的节点不是头结点
                    preNode.next = cur.next
                    cur.next.pre = preNode
                # 核心细节: 删除之后, 程序结束
                break
            else:
                # 走这里, 说明 cur不是要被删除的节点, 继续往后拿
                # pre表示刚才判断过的节点
                preNode = cur
                # 更新cur为它的下个节点
                cur = cur.next
        self.size -= 1

    # contains(self, item) 查找节点是否存在
    def contains(self, item):
        # 定义cur变量, 从头结点开始往后找
        cur = self.head
        # 循环获取到每个节点
        while cur is not None:
            # 判断当前节点的 元素域(数值域) 是否 和 要查找的值 一致
            if cur.item == item:
                # 找到了
                return True
            # 不匹配, 就开始校验下个节点
            cur = cur.next
        # 走到这里, 整个while都结束了, 即: 没有找到
        return False


if __name__ == '__main__':

    sll = DualLinkedList()
    sll.append('张飞')
    sll.insert(1, '张飞1')
    sll.append('张飞2')
    sll.append('张飞3')
    sll.append('张飞4')
    sll.addFront('张飞0')
    print(f'链表中是否存在张飞: {sll.contains("张飞")} 链表长度: {sll.length()}')
    sll.remove('张飞')
    print(f'链表中是否存在张飞: {sll.contains("张飞")} 链表长度: {sll.length()}')
    for i in sll.showAll():
        print(i)

    print('*' * 30)

    for i in sll.showAll(True):
        print(i)

控制台输出

链表中是否存在张飞: True 链表长度: 7
链表中是否存在张飞: False 链表长度: 6
张飞0
张飞1
张飞2
张飞3
张飞4
******************************
张飞4
张飞3
张飞2
张飞1
张飞0
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python单向链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表中的节点按照顺序连接在一起,形成一个链式结构。 下面是Python单向链表的基本实现方式: 1. 定义节点类:首先定义一个节点类,包含两个属性:数据元素和指向下一个节点的指针。 ```python class Node: def __init__(self, data): self.data = data self.next = None ``` 2. 定义链表类:链表类包含若干个节点,以及对链表进行操作的方法。 ```python class LinkedList: def __init__(self): self.head = None def append(self, data): new_node = Node(data) if self.head is None: self.head = new_node else: current = self.head while current.next: current = current.next current.next = new_node def insert(self, data, position): new_node = Node(data) if position == 0: new_node.next = self.head self.head = new_node else: current = self.head for _ in range(position - 1): if current.next is None: raise IndexError("Index out of range") current = current.next new_node.next = current.next current.next = new_node def delete(self, position): if self.head is None: raise IndexError("Index out of range") if position == 0: self.head = self.head.next else: current = self.head for _ in range(position - 1): if current.next is None: raise IndexError("Index out of range") current = current.next if current.next is None: raise IndexError("Index out of range") current.next = current.next.next def get(self, position): if self.head is None: raise IndexError("Index out of range") current = self.head for _ in range(position): if current.next is None: raise IndexError("Index out of range") current = current.next return current.data def size(self): count = 0 current = self.head while current: count += 1 current = current.next return count ``` 以上是一个简单的Python单向链表的实现,包括了向链表末尾添加节点、在指定位置插入节点、删除指定位置的节点、获取指定位置的节点值以及获取链表长度等基本操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值