数据结构1

数据结构

逻辑结构

线性结构--列表

列表中的元素是顺序存储的,是一块连续的内存

找元素时间复杂度为o(1),插入和删除是o(n)

栈(一端进行插入和删除)后进先出

后进先出(吃糖葫芦)lifo

进栈(压栈):push

出栈:pop

取栈顶:get_top,看一眼不拿出来

只支持从最后加或者删除

 

队列(一端插入一端删除)先进先出

空队列:头front==尾rear

进队列:0-11的列表,头指向front,尾rear+=1

出队列:front+=1,(front+1)%len(s)

队满:rear+1=front(防止无法判断队空还是满)

问题:如何让11+1=0--》取余运算,队列长度是12,rear+1%len(s)

# 队列,先入先出
class Queue:
    def __init__(self, size=100):
        rear = 0  # 队尾
        front = 0  # 队首,队首永远指向空
        queue = [0 for _ in range(size)]
        self.size = size
        self.rear = rear
        self.front = front
        self.queue = queue

    def push(self, element):  # 入队,0下标不进任何数,1下标开始
        if not self.is_fulled():
            self.rear = (self.rear + 1) % self.size
            self.queue[self.rear] = element
        else:
            return IndexError("Queue is fulled.")

    def pop(self):
        if not self.is_empty():
            self.front = (self.front + 1) % self.size
            return self.queue[self.front]  # 返回当前队列中的队首元素,即删了那个元素
        else:
            return IndexError("Queue is empty.")

    def is_empty(self):
        return self.rear == self.front

    def is_fulled(self):
        return (self.rear + 1) % self.size == self.front


q = Queue(5)
for i in range(4):
    q.push(i)
print(q.is_fulled(), "---")
print(q.pop())
print(q.pop())
# print(q.push(8))
print(q.pop())
q.push(8)
print(q.pop())
print(q.is_empty())
print(q.pop())
print(q.is_empty())

双向队列

from collection import deque#双向队列

q=deque([1,2,3],5)--5是设置的最大长度,队满了的话最前面的会自动出队

单向队列:

q.append(4)--队尾进队

q.popleft()--队首出队

双向队列:

q.appendleft(1)--队首进队

q.pop()#队尾出队

# 双向队列
from collections import deque

q = deque([1, 2, 3], 5)
q.append(4)#1234append 尾进
print(q.pop())#123 尾删
print(q.popleft())#23 首删
q.appendleft(5)#523 首进
print(q.popleft())#23 首删

应用:

Linux的tail :打印文件后几行

打印后五行

def tail(n):  # 取后几位数据
    with open('test.txt', 'r') as fp:
        q = deque(fp, n)
        return q


print(tail(5))  # deque(['34\n', 'ef\n', 'wer\n', '3r23\n', '231'], maxlen=5)
for i in tail(5):
    print(i, end='')

拉不拉东笔记:

from collections import deque
​
# 初始化双端队列
lst = deque([1, 2, 3, 4, 5])
​
# 检查是否为空,输出:False
print(len(lst) == 0)
​
# 获取大小,输出:5
print(len(lst))
​
# 在头部插入 0,尾部插入 6
lst.appendleft(0)
lst.append(6)
​
# 获取头部和尾部元素,输出:0 6
print(lst[0], lst[-1])
​
# 删除头部和尾部元素
lst.popleft()
lst.pop()
​
# 在索引 2 处插入 99
lst.insert(2, 99)
​
# 删除索引 1 处的元素
del lst[1]
​
# 遍历双端队列
# 输出:1 99 3 4 5
for val in lst:
    print(val, end=" ")
print()
​
​
​
​
​
​
队列是一种操作受限制的数据结构:只允许在队尾插入元素,在队头删除元素。
​
Python 没有专门的队列类型,但可以使用 deque 来模拟队列,append 相当于入队,popleft 相当于出队。
​
from collections import deque
​
# 初始化队列
q = deque()
​
# 向队尾插入元素
q.append(10)
q.append(20)
q.append(30)
​
# 是否为空,输出:False
print(len(q) == 0)
​
# 大小,输出:3
print(len(q))
​
# 获取队头元素,不出队,输出:10
print(q[0])
​
# 队头元素出队
q.popleft()
​
# 新的队头元素,输出:20
print(q[0])

迷宫问题

栈解决(深度优先搜索法==回溯法)

# 队列,先入先出
class Queue:
    def __init__(self, size=100):
        rear = 0  # 队尾
        front = 0  # 队首,队首永远指向空
        queue = [0 for _ in range(size)]
        self.size = size
        self.rear = rear
        self.front = front
        self.queue = queue
​
    def push(self, element):  # 入队,0下标不进任何数,1下标开始
        if not self.is_fulled():
            self.rear = (self.rear + 1) % self.size
            self.queue[self.rear] = element
        else:
            return IndexError("Queue is fulled.")
​
    def pop(self):
        if not self.is_empty():
            self.front = (self.front + 1) % self.size
            return self.queue[self.front]  # 返回当前队列中的队首元素,即删了那个元素
        else:
            return IndexError("Queue is empty.")
​
    def is_empty(self):
        return self.rear == self.front
​
    def is_fulled(self):
        return (self.rear + 1) % self.size == self.front
​
​
q = Queue(5)
for i in range(4):
    q.push(i)
print(q.is_fulled(), "---")
print(q.pop())
print(q.pop())
# print(q.push(8))
print(q.pop())
q.push(8)
print(q.pop())
print(q.is_empty())
print(q.pop())
print(q.is_empty())
​
# 双向队列
from collections import deque
​
​
# q = deque([1, 2, 3], 5)
# q.append(4)#1234append 尾进
# print(q.pop())#123 尾删
# print(q.popleft())#23 首删
# q.appendleft(5)#523 首进
# print(q.popleft())#23 首删
​
# 应用:Linux的tail
def tail(n):  # 取后几位数据
    with open('test.txt', 'r') as fp:
        q = deque(fp, n)
        return q
​
​
print(tail(5))  # deque(['34\n', 'ef\n', 'wer\n', '3r23\n', '231'], maxlen=5)
for i in tail(5):
    print(i, end='')
​
# 0109
print('----0109----')
# 用栈解决迷宫问题--回溯法,一条道走到黑,深度优先搜索
# 假如迷宫为0为通,1为墙壁
# maze = [
#     [1, 1, 1, 1, 1, 1, 1, 1],
#     [1, 0, 0, 0, 1, 0, 0, 1],
#     [1, 0, 1, 0, 1, 1, 0, 1],
#     [1, 0, 1, 0, 1, 0, 0, 1],
#     [1, 0, 1, 1, 1, 1, 0, 1],
#     [1, 0, 0, 0, 0, 1, 1, 1],
#     [1, 1, 1, 1, 0, 0, 0, 1],
#     [1, 1, 1, 1, 1, 1, 1, 1]
# ]
​
maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
    [1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1],
    [1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1],
    [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1],
    [1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1],
    [1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1],
    [1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
dirs = [
    lambda x, y: (x + 1, y),  # 右
    lambda x, y: (x, y - 1),  # 下
    lambda x, y: (x - 1, y),  # 左
    lambda x, y: (x, y + 1)  # 上
]
​
​
def path_root(x1, y1, x2, y2):
    stack = []  # 建立栈,首先把起始坐标压入栈,起始坐标是一个元组
    stack.append((x1, y1))
    # 如果有路就把路压入栈作为栈顶,同时把这个坐标值设为2,如果没有就让栈顶出栈,如果一直没有,栈就会为空,所以循环条件是栈不为空
    while len(stack) > 0:
        nowNode = stack[-1]  # 当前坐标是栈顶,元组(x1,y1),nowNode[0]是横坐标,nowNode[1]是纵坐标
        if nowNode == (x2, y2):
            # return True
            for i in stack:
                print(i)
            return True
        for dir in dirs:  # dir是要走的下一步的位置,也是一个元组
            nextnode = dir(nowNode[0], nowNode[1])
            if maze[nextnode[0]][nextnode[1]] == 0:
                stack.append(nextnode)
                maze[nextnode[0]][nextnode[1]] = 2
                break
        else:
            stack.pop()
    else:
        print('没有路')
        return False
​
​
path_root(1, 1, 10, 10)
# 用队列解决迷宫问题

队列解决(广度优先搜索)

同时考虑两条路,相当于水流,从一个节点开始,寻找所有接下来能继续走的点,知道找到出口。

使用队列存储当前正在考虑的节点,存尾巴尖

需要两列,第一列是出队的序列,写成队列,第二列是哪个点让他来的位置,写成列表

1 2 3 4 5 6 7 8。。。

-1 0 1 2 2 3 4 5。。。

队空:都是死路

 

树结构

图结构

地图

 

物理结构

链表--item,next

创建链表-头插法/尾插法

 

链表的插入和删除

列表insert插入是o(n),append是o(1)

链表不是顺序存的,所以都是o(1)

插入两步:先把4跟2连起来p.next=curNode.next

再把1跟4连起来curNode.next=p

删除三步:先把4用p存放起来p=curNode.next

再把1和2连起来curNode.next=p.next

最后把p删除 del p

 

双链表--前后都能找item,next,prior

双链表的插入

哈希表

字典和集合就是用哈希表实现的

直接寻址表

在直接寻址表上加哈希函数就是哈希表

哈希表

哈希冲突

哈希函数足够好就是每个位置都均匀分布

模拟python集合来实现拉链法哈希表

 

哈希表的应用

 

树结构

树是一种可以递归定义的数据结构

树的实例:模拟文件系统

"""
#树状函数
#树--一种数据结构
#目录--文件
#用递归的方式定义的
n个节点组成的集合
根节点,叶子结点-到头了,不能再分叉
深度:分几层,高度
度:所有节点最大的那个分了几个叉
父亲节点,孩子节点
"""
from os import mkdir
​
​
# 模拟操作系统写一个树
class Node:
    def __init__(self, name):
        self.name = name
        self.type = type  # dir or file
        self.children = []  # 只能往下找不能往回找
        self.parent = None  # 加这个像双链表
​
    def __repr__(self):
        return self.name
​
​
# n = Node('hello')
# n2 = Node('world')
# n.children.append(n2)
# n2.parent = n
​
​
class FileSystemTree:
    def __init__(self):
        self.root = Node("/")
        self.now = self.root
​
    def mkdir(self, name):  # name必须是以斜杠结尾的
        if name[-1] != "/":
            name += "/"
        node = Node(name)
        self.now.children.append(node)
        node.parent = self.now
​
    def ls(self):
        return self.now.children
​
    def cd(self, name):
        # 只支持一层往下走
        if name[-1] != "/":
            name += "/"
        if name == "../":#支持向上返回一级
            self.now = self.now.parent
            return
        for child in self.now.children:
            if child.name == name:
                self.now = child
                return
        raise ValueError("invalid dir")
​
​
tree = FileSystemTree()
tree.mkdir("var/")
tree.mkdir("bin/")
tree.mkdir("user/")
# print(tree.root.children)
tree.cd("bin/")
tree.mkdir("python/")
tree.cd("../")
print(tree.ls())

二叉树的概念

"""
二叉树:度只有两个,每一个父节点只能有两个子节点--左孩子和右孩子
堆:把树存成列表,完全二叉树--左边不会少东西,右边可以少
​
"""
​
​
class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None
​
​
a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")
e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f
root = e
print(root.lchild.rchild.data)

二叉树的遍历

前序遍历:中左右

中序遍历:左中右

后序遍历:左右中

"""
二叉树:度只有两个,每一个父节点只能有两个子节点--左孩子和右孩子
堆:把树存成列表,完全二叉树--左边不会少东西,右边可以少
​
self 是一个约定俗成的参数名,用于表示类的当前实例对象。通过 self,可以访问实例的属性和方法
"""
​
​
class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None
​
​
a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")
e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f
root = e
print(root.lchild.rchild.data)
​
"""
二叉数的遍历:四种
前序遍历
中序遍历
后序遍历
层次遍历
"""
​
​
def pre_order(root):#中左右
    if root:  # 如果不为空
        print(root.data, end=',')#中,就是父节点
        pre_order(root.lchild)#左孩子
        pre_order(root.rchild)#右孩子
​
​
pre_order(root)
print('-------')
​
​
def in_order(root):
    if root:
        in_order(root.lchild)#左孩子
        print(root.data, end=',')#父节点
        in_order(root.rchild)#右孩子
​
​
in_order(root)
print('-------')
​
​
def post_order(root):
    if root:
        post_order(root.lchild)#左孩子
        post_order(root.rchild)#右孩子
        print(root.data, end=',')#父节点
​
​
post_order(root)
print('-------')
​
​
##层次遍历,可以用队列,先进先出
def level_order(root):
    queue = deque()
    queue.append(root)
    while len(queue) > 0:  # 只要队不空,就继续进队
        node = queue.popleft()
        print(node.data, end=',')
        if node.lchild:
            queue.append(node.lchild)
        if node.rchild:
            queue.append(node.rchild)
​
​
level_order(root)

二叉搜索树bst

# 二叉搜索树bst,需要满足所有左节点的值比父节点小,所有有节点的值比父节点大
# 二叉搜索树:查询,插入,删除
# 查询和插入的复杂度o(logn)
class BiTreeNode:
    def __init__(self, data):
        self.data = data       # 节点存储的数据
        self.lchild = None     # 左孩子
        self.rchild = None     # 右孩子
        self.parent = None     # 父节点(双向链表结构)
'''
self.data、self.lchild、self.rchild、self.parent 都是 实例属性,绑定到具体的 BiTreeNode 对象。
每次创建一个 BiTreeNode 对象时(如 node = BiTreeNode(5)),self 就代表这个新创建的 node 对象。
'''
​
print('')
​
​
​
​
#插入
​
class BST:
    def __init__(self, li=None):#li=None 是函数的默认参数,表示如果调用 BST() 时不传入参数,li 会被赋值为 None
        self.root = None       # 初始化根节点为None
        if li:#如果 li 是 None 或 [],则 if li: 为 False,不会执行循环,避免不必要的操作
            for val in li:
                self.insert_no_rec(val)  # 调用非递归插入方法
​
    def insert(self, node, val):  # 递归插入,self 是 BST 类的实例对象,用于调用类的方法或访问实例属性(尽管这里没有直接用到实例属性,但因为是类的方法,仍需保留 self)。node 是当前处理的子树节点,val 是要插入的值。
    if not node:
        node = BiTreeNode(val)  # 如果节点为空,创建新节点
    elif val < node.data:
        node.lchild = self.insert(node.lchild, val)  # 递归处理左子树
        node.lchild.parent = node  # 设置父节点
    elif val > node.data:
        node.rchild = self.insert(node.rchild, val)  # 递归处理右子树
        node.rchild.parent = node  # 设置父节点
    return node  # 返回当前节点
​
    def insert_no_rec(self, val):#二叉搜索树(BST)的非递归插入方法
        p = self.root
        if not p:  # 空树的情况下特殊处理
            self.root = BiTreeNode(val)
            return
        while True:#遍历树,找到插入位置,while True无限循环,直到找到插入位置或发现重复值。
            if val < p.data:  # 要插入的值比当前节点小 → 往左子树找
                if p.lchild:  # 如果左孩子存在,继续往下找
                    p = p.lchild
                else:         # 如果左孩子不存在,插入新节点
                    p.lchild = BiTreeNode(val)
                    p.lchild.parent = p  # 设置父节点
                    return
            elif val > p.data:  # 要插入的值比当前节点大 → 往右子树找
                if p.rchild:    # 如果右孩子存在,继续往下找
                    p = p.rchild
                else:           # 如果右孩子不存在,插入新节点
                    p.rchild = BiTreeNode(val)
                    p.rchild.parent = p  # 设置父节点
                    return
            else:  # val == p.data,值已存在,不插入
                return
'''
总结
非递归插入:相比递归实现(insert 方法),这个方法用 while 循环代替递归,避免了递归的栈开销。
时间复杂度:
平均情况:O(log n)(平衡 BST)。
最坏情况:O(n)(退化成链表,如插入有序数据 1, 2, 3, 4, ...)。
空间复杂度:O(1)(仅用变量 p 遍历,不占用额外空间)。
'''
​
​
​
#查询
    def query(self, node, val):#递归查询方法
        if not node:
            return None
        if val > node.data:
            return self.query(node.rchild, val)  # 去右子树查找
        elif val < node.data:
            return self.query(node.lchild, val)  # 去左子树查找
        else:
            return node  # 找到目标值,返回当前节点
'''
二叉搜索树(BST)的非递归查询方法,即在 BST 中查找值为 val 的节点
'''
    def query_no_rec(self, val):#方法定义self:表示当前 BST 实例(用于访问 self.root)
        p = self.root#初始化指针,p 是一个指针,初始指向 BST 的根节点 self.root
        while p:#只要 p 不是 None(即当前节点存在),就继续查找
            if p.data < val:
                p = p.rchild  # 目标值更大,往右子树找
            elif p.data > val:
                p = p.lchild  # 目标值更小,往左子树找
            else:
                return p  # 找到目标值,返回当前节点
        return None#如果 while 循环结束(即 p 变为 None),说明遍历到叶子节点仍未找到目标值,返回 None。
​
      
 '''
 二叉树的三种深度优先遍历(DFS)方式:前序遍历(pre_order)、中序遍历(in_order)和后序遍历(post_order),if root 判断当前节点是否为空,是递归的终止条件
时间复杂度:都是O(n),因为每个节点恰好访问一次
空间复杂度:平均O(log n)(树高),最坏O(n)(退化成链表)
遍历应用:
​
前序:表达式前缀表示(波兰表示法)
  第一个访问的总是根节点,适合用来复制树的结构(因为先知道父节点)
中序:BST排序输出
​
后序:计算目录大小(先计算子目录)
 '''
    def pre_order(self, root):
        if root:  # 如果不为空
            print(root.data, end=',')  # (1) 先访问根节点
            self.pre_order(root.lchild)  # (2) 递归遍历左子树
            self.pre_order(root.rchild)  # (3) 递归遍历右子树
​
    def in_order(self, root):
        if root:
            self.in_order(root.lchild)  # (1) 先递归遍历左子树
            print(root.data, end=',')   # (2) 访问根节点
            self.in_order(root.rchild)  # (3) 递归遍历右子树
​
    def post_order(self, root):
        if root:
            self.post_order(root.lchild)  # (1) 先递归遍历左子树
            self.post_order(root.rchild)  # (2) 递归遍历右子树
            print(root.data, end=',')     # (3) 最后访问根节点
​
'''
  注意事项
命名约定:
​
双下划线开头(__)表示私有方法
​
这是Python的名称修饰(name mangling)机制
​
时间复杂度:
​
都是O(1)操作,因为只涉及指针修改
  '''
    # 二叉搜索树的删除
    # 三种情况
    '''
    叶子节点:直接删除。
    单子节点:用子节点替代。
    双子节点:用前驱或后继替换,再递归删除前驱/后继。
    保持 BST 性质:删除后仍满足 左 < 根 < 右。
    
    1. 删除叶子节点(无子节点)
    情况描述:要删除的节点没有左孩子和右孩子(node.lchild == None 且 node.rchild == None)。
    处理方法:
    直接断开其父节点对它的引用。
    如果该节点是根节点,则整棵树置空。
    
    2. 删除只有一个子节点的节点
    情况描述:要删除的节点 只有左孩子 或 只有右孩子(node.lchild 或 node.rchild 非空,但另一个为空)。
    处理方法:
    让父节点直接指向它的唯一子节点。
    更新子节点的 parent 指针。
    如果该节点是根节点,则子节点成为新的根。
    
    3. 删除有两个子节点的节点
    情况描述:要删除的节点 既有左孩子,又有右孩子(node.lchild != None 且 node.rchild != None)。
    处理方法(两种常见策略):
      方法 1:用前驱节点(左子树的最大值)替换
    找到 node 的 前驱节点(左子树的最右节点)。
    用前驱节点的值覆盖 node 的值。
    删除前驱节点(它最多只有一个左孩子,可以递归调用删除方法)。
      方法 2:用后继节点(右子树的最小值)替换
    找到 node 的 后继节点(右子树的最左节点)。
    用后继节点的值覆盖 node 的值。
    删除后继节点(它最多只有一个右孩子,可以递归调用删除方法)。
    '''
​
    def __remove_leaf(self, node):
        if not node.parent:  # 是根节点
            self.root = None
        elif node == node.parent.lchild:  # 是左孩子
            node.parent.lchild = None
        else:  # 是右孩子
            node.parent.rchild = None
        node.parent = None  # 可选:帮助垃圾回收
​
    def __remove_node_with_one_child(self, node):
        child = node.lchild if node.lchild else node.rchild  # 获取唯一子节点
​
        if not node.parent:  # 是根节点
            self.root = child
            if child:
                child.parent = None
        elif node == node.parent.lchild:  # 是左孩子
            node.parent.lchild = child
            if child:
                child.parent = node.parent
        else:  # 是右孩子
            node.parent.rchild = child
            if child:
                child.parent = node.parent
​
    def __remove_node_with_two_children(self, node):
        successor = self.__find_min(node.rchild)  # 找到右子树的最小节点(后继)
        node.data = successor.data  # 用后继的值覆盖当前节点
        self.__delete_node(successor)  # 递归删除后继节点(它最多只有一个右孩子)
​
    def __find_min(self, node):
        while node.lchild:
            node = node.lchild
        return node
​
    def delete(self, val):
        node = self.query_no_rec(val)  # 找到要删除的节点
        if not node:
            return False  # 节点不存在
​
        if not node.lchild and not node.rchild:  # 情况1:叶子节点
            self.__remove_leaf(node)
        elif not node.lchild or not node.rchild:  # 情况2:只有一个子节点
            self.__remove_node_with_one_child(node)
        else:  # 情况3:有两个子节点
            self.__remove_node_with_two_children(node)
        return True
​
tree = BST([4, 6, 7, 9, 2, 1, 3, 5, 8])
tree.pre_order(tree.root)  # 4,2,1,3,6,5,7,9,8,
print('')
tree.in_order(tree.root)  # 1,2,3,4,5,6,7,8,9,==有序
print('')
tree.post_order(tree.root)  # 1,3,2,5,8,9,7,6,4,
print('--')
li = list(range(0, 500, 2))
random.shuffle(li)
​
tree = BST(li)
print(tree.query_no_rec(4).data)  # 4
print(tree.query_no_rec(3))  # None
​

AVL树

AVL旋转

插入一个数不平衡的话可以通过AVL旋转来修正

四种旋转方式: 左左--》右旋(左孩子的左子树插入导致不平衡)

右右--》左旋

左右--》先左旋后右旋

右左--》先右旋再左旋

from bst import BiTreeNode, BST
​
#AVL节点类 (AVLNode)
class AVLNode(BiTreeNode):
    def __init__(self, data):
        BiTreeNode.__init__(self, data)  # 继承普通二叉树的节点
        self.bf = 0  # Balance Factor(平衡因子)
'''
功能:在普通二叉树节点基础上增加平衡因子(bf)
​
平衡因子:
bf = 右子树高度 - 左子树高度
合法值:-1, 0, 1(若绝对值>1则需要旋转调整)
'''
​
#AVL树类 (AVLTree)
class AVLTree(BST):
    def __init__(self, li=None):
        BST.__init__(self, li)  # 继承BST的初始化方法
    '''
    应用场景:
    当节点p的bf=2且其右孩子c的bf=1时(右子树更高且继续向右插入)
    '''
    def rotate_left(self, p, c):
        s2 = c.lchild  # 保存c的左子树
        p.rchild = s2  # p的右孩子指向s2
        if s2:
            s2.parent = p  # 更新s2的父节点
        c.lchild = p  # c的左孩子变为p
        p.parent = c  # p的父节点变为c
        p.bf = 0  # 重置平衡因子
        c.bf = 0
    '''
    应用场景:
    当节点p的bf=-2且其左孩子c的bf=-1时(左子树更高且继续向左插入)
    '''
    def rotate_right(self, p, c):
        s2 = c.rchild  # 保存c的右子树
        p.lchild = s2  # p的左孩子指向s2
        if s2:
            s2.parent = p  # 更新s2的父节点
        c.rchild = p  # c的右孩子变为p
        p.parent = c  # p的父节点变为c
        p.bf = 0  # 重置平衡因子
        c.bf = 0
    '''
    应用场景:
    当节点p的bf=2且其右孩子c的bf=-1时(右子树更高但在左子树上插入)
    '''
    def rotate_right_left(self, p, c):
        g = c.lchild  # c的左孩子g是关键节点
        s3 = g.rchild  # 保存g的右子树
        c.lchild = s3  # c的左孩子指向s3
        if s3:
            s3.parent = c
        g.rchild = c  # g的右孩子变为c
        c.parent = g
​
        s2 = g.lchild  # 保存g的左子树
        p.rchild = s2  # p的右孩子指向s2
        if s2:
            s2.parent = p
        g.lchild = p  # g的左孩子变为p
        p.parent = g
​
        # 根据g的原bf更新平衡因子
        if g.bf == 1:  # 原g的右子树更高
            p.bf = -1  # p的新左子树更高
            c.bf = 0
        elif g.bf == -1:  # 原g的左子树更高
            p.bf = 0
            c.bf = 1  # c的新右子树更高
        else:  # g本身就是新插入节点
            p.bf = 0
            c.bf = 0
    '''
    应用场景:
    当节点p的bf=-2且其左孩子c的bf=1时(左子树更高但在右子树上插入)
    '''
    def rotate_left_right(self, p, c):
        g = c.rchild  # c的右孩子g是关键节点
        s2 = g.lchild  # 保存g的左子树
        c.rchild = s2  # c的右孩子指向s2
        if s2:
            s2.parent = c
        g.lchild = c  # g的左孩子变为c
        c.parent = g
​
        s3 = g.rchild  # 保存g的右子树
        p.lchild = s3  # p的左孩子指向s3
        if s3:
            s3.parent = p
        g.rchild = p  # g的右孩子变为p
        p.parent = g
​
        # 根据g的原bf更新平衡因子
        if g.bf < 0:  # 原g的左子树更高
            p.bf = 1  # p的新右子树更高
            c.bf = 0
        elif g.bf > 0:  # 原g的右子树更高
            p.bf = 0
            c.bf = -1  # c的新左子树更高
        else:  # g本身就是新插入节点
            p.bf = 0
            c.bf = 0

算法进阶

1. 贪心算法

# 找零
t = [100, 50, 20, 5, 1]


def change(t, n):
    m = [0 for _ in range(len(t))]  # 初始化结果列表,全0
    for i, money in enumerate(t):    # 遍历每种面额
        m[i] = n // money           # 计算当前面额需要的张数
        n = n % money               # 计算剩余金额
    return m, n                     # 返回张数列表和剩余金额


print(change(t, 377))

 

2. 动态规划

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值