数据结构
逻辑结构
线性结构--列表
列表中的元素是顺序存储的,是一块连续的内存
找元素时间复杂度为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. 动态规划