Pyhton知识分享第二十四天-模拟二叉树

"""
树相关的概述:
    概述:
        它属于数据结构的一种, 属于 非线性结构, 即: 每个节点可以有1个父节点(前驱) 和 n个子节点(后继)
    特点:
        1. 有且只能有1个根节点.
        2. 每个节点都只有1个父节点 及 若干个子节点,   根节点除外(它没有父节点)
        3. 没有子节点的节点称之为: 叶子节点.
    名词解释:
        树的度: 树结构中, 最大的 节点的度 称之为: 树的度
        树的层: 根节点为第1层, 以此类推, 最大的层数就是 树的层.
        森系结构: 由多棵树组成的结构.
    分类:
        完全二叉树: 除了最后1层, 其它层都是满的.
        满二叉树:  树的所有叶子节点, 都在最后一层.
        平衡二叉树: 任意节点的两个子树的高度差不超过1, 目的: 防止树退化成链表.
        排序二叉树: 按照 中序(左根右) 的顺序获取数据, 是一个有序的序列.
    存储:
        顺序表:
            只存储节点的数据, 相对节省空间.  有索引, 方便查找数据, 但是不存储节点之间的关系.
        链表存储:
            不仅存储节点的数据, 还存储节点的关系, 相对更占用空间.
            但是方便我们维护和管理节点(的关系).     推荐.
            把树转成二叉树, 每个二叉树都有: data(存数据的) + next(左子树, left child) + next(右子树 right child) 组成.

案例:
    自定义代码 模拟 二叉树.

分析流程:
    定义节点类Node,
        属性: item(元素域), lchild(左子树), rchild(右子树)
    定义二叉树类BinaryTree:
        属性: root    -> 充当根节点.
        行为:
            add()    -> 往二叉树中, 添加元素的.
            breadth_travel() -> 广度优先, 逐层遍历.
"""
python 复制代码
# 1. 定义节点类Node
class Node:
    # 初始化属性
    def __init__(self, item):
        self.item = item        # 元素域
        self.lchild = None      # 左子树
        self.rchild = None      # 右子树

# 2. 定义二叉树类BinaryTree
class BinaryTree:
    # 初始化属性.
    def __init__(self, root=None):
        self.root = root        # 二叉树的根节点.

    # 定义函数, 实现往二叉树中, 添加元素.
    def add(self, item):
        # 1. 判断根节点是否为空.
        if self.root is None:
            # 走这里, 根节点为空, 把 item封装成节点, 作为根节点即可.
            self.root = Node(item)
            return   # 添加完毕后, 记得结束 添加动作即可.

        # 2. 定义列表 -> 充当队列(先进先出), 用于记录: 二叉树中已经存在的节点.
        queue = []
        # 3. 把根节点添加到 队列 中.
        queue.append(self.root)
        # 4. 循环查找队列中的元素, 直至找到: 某个节点的左子树, 或者右子树为空的情况.
        while True:
            # 5. 获取(弹出) 队列中的第1个元素, 即: 从 根节点开始.
            node = queue.pop(0)
            # 6. 判断节点的左子树是否为空.
            if node.lchild is None:
                # 6.1 走到这里, 说明当前节点的左子树为空, 把新元素添加到这里即可, 并记得返回.
                node.lchild = Node(item)
                return
            else:
                # 6.2 走到这里, 说明当前节点的左子树不为空, 就把左子树添加到 队列中.
                queue.append(node.lchild)

            # 7. 走到这里, 说明当前节点的左子树不为空, 继续判断右子树是否为空.
            if node.rchild is None:
                # 7.1 走到这里, 说明当前节点的右子树为空, 把新元素添加到这里即可, 并记得返回.
                node.rchild = Node(item)
                return
            else:
                # 7.2 走到这里, 说明当前节点的右子树不为空, 就把右子树添加到 队列中.
                queue.append(node.rchild)

    # 定义函数, 实现广度优先, 逐层遍历.
    def breadth_travel(self):
        # 1. 判断根节点是否为空, 如果为空, 直接返回即可.
        if self.root is None:
            return

        # 2. 走这里, 说明根节点不为空, 我们逐个获取节点, 打印信息即可.
        queue = []  # 定义队列, 用于记录: 二叉树中已经存在的节点.
        queue.append(self.root) # 把根节点添加到队列中.

        # 3. 开始循环, 直至队列为空.
        while len(queue) > 0:
            # 4. 获取(弹出) 队列中的第1个元素, 即: 从 根节点开始.
            node = queue.pop(0)
            # 5. 打印当前节点的信息(元素域)
            print(node.item)
            # 6. 判断当前节点的左子树是否为空, 不为空就添加到队列中.
            # if node.lchild != None:
            if node.lchild is not None:
                queue.append(node.lchild)

            # 7. 判断当前节点的右子树是否为空, 不为空就添加到队列中.
            if node.rchild is not None:
                queue.append(node.rchild)


    # 定义函数, 实现深度优先 -> 先序(前序), 根左右
    def preorder_travel(self, root):    # root是传入的节点
        # 1. 判断传入的节点是否不为空.
        if root is not None:
            # 2. 走这里, 说明有元素, 根左右顺序拿即可.
            print(root.item, end=' ')
            # 3. 递归拿左子树.
            self.preorder_travel(root.lchild)
            # 4. 递归拿右子树.
            self.preorder_travel(root.rchild)

    # 定义函数, 实现深度优先 -> 中序, 左根右
    def inorder_travel(self, root):    # root是传入的节点
        # 1. 判断传入的节点是否不为空.
        if root is not None:
            # 2. 递归拿左子树.
            self.inorder_travel(root.lchild)
            # 3. 走这里, 说明有元素, 左根右顺序拿即可.
            print(root.item, end=' ')
            # 4. 递归拿右子树.
            self.inorder_travel(root.rchild)

    # 定义函数, 实现深度优先 -> 后序, 左右根
    def postorder_travel(self, root):    # root是传入的节点
        # 1. 判断传入的节点是否不为空.
        if root is not None:
            # 2. 递归拿左子树.
            self.postorder_travel(root.lchild)
            # 3. 递归拿右子树.
            self.postorder_travel(root.rchild)
            # 4. 走这里, 说明有元素, 左右根顺序拿即可.
            print(root.item, end=' ')


# 3. 定义测试方法.
def dm01_测试节点类和二叉树类():
    # 1. 创建节点对象.
    n1 = Node('天龙八部')
    # 2. 打印节点的: 元素域, 左子树, 右子树.
    print(n1.item)      # 天龙八部
    print(n1.lchild)    # None
    print(n1.rchild)    # None
    print('-' * 22)

    # 3. 创建二叉树对象.
    # bt = BinaryTree()
    bt = BinaryTree(n1)
    print(bt)           # 打印二叉树对象.
    print(bt.root)      # 打印二叉树的根节点
    print(bt.root.item) # 打印二叉树的根节点的元素域

def dm02_测试队列添加和弹出队头元素():
    # 1. 创建队列(先进先出), 其实: 还是列表模拟的.
    queue = []

    # 2. 演示队列添加元素.
    queue.append('a')
    queue.append('b')
    queue.append('c')

    # 3. 弹出队头元素.
    print(queue.pop(0))   # ['a', 'b', 'c'] -> a
    print(queue.pop(0))   # ['b', 'c'] -> b
    print(queue.pop(0))   # ['c'] -> c

    # 4. 打印队列.
    # print(queue)        # ['a', 'b', 'c']

def dm03_广度优先遍历():
    # 1. 定义二叉树.
    bt = BinaryTree()
    # 2. 往二叉树中添加元素.
    bt.add('a')
    bt.add('b')
    bt.add('c')
    bt.add('d')
    bt.add('e')
    bt.add('f')
    bt.add('g')
    bt.add('h')
    bt.add('i')
    bt.add('j')
    # 3. 遍历二叉树 -> 广度优先.
    bt.breadth_travel()


def dm04_深度优先遍历():
    # 1. 定义二叉树
    bt = BinaryTree()
    # 2. 添加元素.
    bt.add(0)
    bt.add(1)
    bt.add(2)
    bt.add(3)
    bt.add(4)
    bt.add(5)
    bt.add(6)
    bt.add(7)
    bt.add(8)
    bt.add(9)
    # 3. 打印遍历结果.
    print('先序遍历(根左右): ')
    bt.preorder_travel(bt.root) # 传入根节点, 即: 从根节点开始遍历.

    print('\n中序遍历(左根右): ')
    bt.inorder_travel(bt.root)

    print('\n后序遍历(左右根): ')
    bt.postorder_travel(bt.root)

# 4. 在main函数中测试
if __name__ == '__main__':
    # dm01_测试节点类和二叉树类()
    # dm02_测试队列添加和弹出队头元素()
    # dm03_广度优先遍历()
    dm04_深度优先遍历()

模拟迭代器

"""

回顾:

生成器 -> 基于一定的规则来生成数据, 不会一下子生成所有, 而是用一个, 生成1个, 目的: 节约内存空间.

上下文管理器 -> 1个类只要实现了 enter (), exit(), 该类就是上下文管理器.

迭代器:

概述:

1个类只要实现了 iter (), next ()这两个函数, 它就是: 迭代器类.

该类的对象就是 迭代器对象.

目的:

用的时候再迭代, 迭代1个, 获取1个, 节约内存空间.

实现方式:

方式1: iter()函数 -> 可以把容器类型转成迭代器类型

方式2: 自定义类实现 iter (), next ()这两个函数, 即: 自定义代码实现迭代器.

"""

python 复制代码
import sys


# 自定义代码, 模拟迭代器.
class MyIterator:
    # 初始化属性
    def __init__(self, max_value):  # 假设: max_value = 100
        self.max_value = max_value  # 定义变量, 记录: 最大的值
        self.current_value = 0      # 定义变量, 记录: 当前的(迭代到的)数据

    # 实现 __iter__()函数, 返回: 迭代器对象.
    def __iter__(self):
        return self         # MyIterator是迭代器类, 所以它的对象是迭代器对象, 直接返回即可.

    # 实现 __next__()函数, 返回: 下一个值.
    def __next__(self):
        # 非法值校验.
        if self.current_value >= self.max_value:
            raise StopIteration # 抛出异常, 停止迭代.
        # 走到这里, 说明还在合法区间, 定义变量value基础当前值, 返回即可.
        value = self.current_value  # 假设: value = 0            1
        self.current_value += 1     # self.current_value = 1    2
        return value                # return 0                  1


if __name__ == '__main__':
    # 1. 对比 生成器 和 普通列表的内存占用.
    list1 = [i for i in range(1000000) ]
    my_generator = (i for i in range(1000000))

    # 查看内存占用.
    print(sys.getsizeof(list1))         # 8448728
    print(sys.getsizeof(my_generator))  # 192


    # 2. 上述生成器是用一个生成1个, 迭代器就是迭代一次, 才会获取下一个.
    print(next(my_generator))       # 0
    # print(next(list1))              # 报错, 列表不是迭代器.

    # 3. 具体的迭代器演示.
    # 方式1: iter()函数, 可以把容器类型转成迭代器类型.
    list1 = [i for i in range(1000000)]
    my_itr = iter(list1)
    print(type(list1))      # <class 'list'>
    print(type(my_itr))     # <class 'list_iterator'>
    print('-' * 22)

    print(next(my_itr)) # 0
    print(next(my_itr)) # 1
    print(next(my_itr)) # 2
    print(sys.getsizeof(list1))        # 8448728
    print(sys.getsizeof(my_itr))       # 48
    print('-' * 22)

    # 方式2: 自定义代码, 模拟上述的操作.
    # 1. 创建迭代器对象.
    my_itr2 = MyIterator(10)
    # 2. 从迭代器中获取元素.
    print(next(my_itr2))    # 0
    print(next(my_itr2))    # 1
    print(next(my_itr2))    # 2
    print('-' * 22)
    for i in my_itr2:
        print(i)
    print('-' * 22)

    # 总结: 迭代器 和 普通列表对比.
    list1 = [i for i in range(100000)]  # 列表
    myItr1 = iter(list1)                # 把列表 -> 转成迭代器
    myItr2 = MyIterator(100000)         # 自定义代码实现迭代器.

    # 查看空间占用.
    print(sys.getsizeof(list1))     # 800984
    print(sys.getsizeof(myItr1))    # 48
    print(sys.getsizeof(myItr2))    # 48

约瑟夫环

"""

需求:

有一天你穿越到了古代, 碰巧约到古代的皇帝在选状元郎, 有两个好消息

1.你在此列.

  1. 你可以选择自己占的位置.

规则: 让所有的人从1开始不断数数, 每次累加1, 只要数到3的倍数, 就去掉这个人, 直至剩下最后1个人, 他站的位置就是: 幸运数字.

参考答案: 10个人玩游戏 -> 幸运数字: 4

"""

python 复制代码
# 思路1: 传统编程思路, 业务逻辑实现.
# 1. 定义变量, 记录参与游戏的人数, 并接收.
n = int(input('请录入参与游戏的人数: '))
# 2. 生成列表.
num_list = [i for i in range(1, n + 1)]
# 3. 定义变量, 分别记录: 当前数到的数字, 当前元素的索引.
current_num = 0  # 当前数到的数字
i = 0            # 当前元素的索引
# 4. 循环, 直到列表中只剩下1个元素.
while len(num_list) != 1:
    # 5. 先数 数字.
    current_num += 1
    # 6. 判断, 当前数字是3的倍数, 就删掉这个元素.
    if current_num % 3 == 0:
        num_list.pop(i)
        # 细节1: 因为删除元素后, 后续元素的索引会-1, 所以: i也要同步-1
        i -= 1
    # 7. 走到这里, 说明上个人已经报过数字了, 我们继续游戏即可.
    i += 1
    # 8. 做越界处理.
    if i >= len(num_list):
        # 细节2: 走到这里, 说明本轮结束, (开启下轮报数)从头开始报数即可.
        i = 0
# 9. 走到这里, 说明幸运数字已经找到了, 返回即可.
print(f'{n}人参与游戏, 幸运数字是: {num_list[0]}')
print('-' * 22)

# 思路2: 数学思维 -> 找规律.
# 1. 定义变量, 记录参与游戏的人数, 并接收.
# n = int(input('请录入参与游戏的人数: '))
# 2. 生成列表.
num_list = [i for i in range(1, n + 1)]
# 3. 定义变量, 分别记录: 当前元素的索引.
i = 0            # 当前元素的索引
# 4. 循环, 直到列表中只剩下1个元素.
while len(num_list) != 1:
    # 5. 通过公式, 直接找到要删除的那个元素的索引.
    i = (i + 2) % len(num_list)
    # 6. 删除该元素.
    num_list.pop(i)

# 7. 走到这里, 说明幸运数字已经找到了, 返回即可.
print(f'{n}人参与游戏, 幸运数字是: {num_list[0]}')

坚持分享 共同进步 如有错误 欢迎指出

相关推荐
小王子102419 分钟前
设计模式Python版 组合模式
python·设计模式·组合模式
利刃大大38 分钟前
【回溯+剪枝】找出所有子集的异或总和再求和 && 全排列Ⅱ
c++·算法·深度优先·剪枝
来恩10031 小时前
C# 类与对象详解
开发语言·c#
komo莫莫da1 小时前
寒假刷题Day19
java·开发语言
Rachela_z1 小时前
代码随想录算法训练营第十四天| 二叉树2
数据结构·算法
细嗅蔷薇@1 小时前
迪杰斯特拉(Dijkstra)算法
数据结构·算法
追求源于热爱!2 小时前
记5(一元逻辑回归+线性分类器+多元逻辑回归
算法·机器学习·逻辑回归
ElseWhereR2 小时前
C++ 写一个简单的加减法计算器
开发语言·c++·算法
Mason Lin2 小时前
2025年1月22日(网络编程 udp)
网络·python·udp
Smark.2 小时前
Gurobi基础语法之 addConstr, addConstrs, addQConstr, addMQConstr
算法