mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
2783 字
14 分钟
树学习笔记
2026-03-22

参考资料

树的定义#

树(Tree)由若干节点(Node)和两两连接节点的边(Edge)组成。

递归定义: 树是空集;或由根节点及0个或多个子树组成,其中子树亦是树,每个子树的根到根节点具有边相连 。

节点(Node)是树的基本部分

每个节点具有名称,或“键值”,节点还可以保存额外数据项,数据项根据不同的应用而改变

边(Edge)是组成树的另一个基本部分

每条边恰好连接两个节点,表示节点之间具有关联,边具有出入方向;每个节点(除了根节点)恰有一条来自另一节点的入边;每个节点可以有多条连到其他节点的出边。

树的性质#

  • 其中一个节点被指定为根节点(Root)
  • 每个节点(除了根节点)恰连接一条来自节点p的边,p是n的父节点
  • 每个节点从根开始的路径是唯一的
  • 如果每个节点最多有两个子节点,这样的树被称为二叉树

树的嵌套列表实现#

Tree = [root,left,right]
def BinaryTree(r): #创建一个二叉树
return [r, [], []]
def insertLeft(root, newBranch): #插入左子树
t = root.pop(1)
if len(t) > 1:
root.insert(1, [newBranch, t, []])
else:
root.insert(1, [newBranch, [], []])
return root
def insertRight(root, newBranch): #插入右子树
t = root.pop(2)
if len(t) > 1:
root.insert(2, [newBranch, [], t])
else:
root.insert(2, [newBranch, [], []])
return root
def getRootVal(root): #获取根节点
return root[0]
def setRootVal(root, newVal): #设置根节点
root[0] = newVal
def getLeftChild(root): #获取左子树
return root[1]
def getRightChild(root): #获取右子树
return root[2]

实例

root = BinaryTree('a')
insertRight(root, 'c')
insertLeft(root, 'b')
insertRight(root, 'e')
# 与实例 2 相同
left = getLeftChild(root) # b 子树
right = getRightChild(root) # e 子树
insertLeft(left, 'x') # b 的左侧:空 → 挂 x
insertRight(left, 'y') # b 的右侧:空 → 挂 y
insertLeft(right, 'm') # e 的左侧:空 → 挂 m(c 仍在 e 的右侧)
c = getRightChild(right) # c 子树
insertLeft(c, 'p') # c 左侧挂 p
insertRight(c, 'q') # c 右侧挂 q
flowchart TB a(("a")) b(("b")) e(("e")) x(("x")) y(("y")) m(("m")) c(("c")) p(("p")) q(("q")) a --> b a --> e b --> x b --> y e --> m e --> c c --> p c --> q

树的链表实现#

class BinaryTree:
def __init__(self, rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None
def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t
def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t
def getRightChild(self):
return self.rightChild
def getLeftChild(self):
return self.leftChild
def setRootVal(self,obj):
self.key = obj
def getRootVal(self):
return self.key

树的应用#

表达式解析#

实例

从图示过程可以发现,创建树的关键是对当前节点的跟踪

  • 创建左右子树可以用insertLeft和insertRight方法
  • 当前节点设定值可以用setRootVal方法
  • 下降到左右子树可以用getLeftChild和getRightChild方法

但是上升到父节点没有方法支持

因此我们可以用一个栈来记录跟踪父节点

当前节点下降时,将下降前的节点push入栈; 当前节点上升时,上升到pop出栈的节点即可

def buildParseTree(fpexp): #构建解析树
fplist = fpexp.split()
pStack = Stack()
eTree = BinaryTree('')
pStack.push(eTree) #将根节点压入栈
currentTree = eTree
for i in fplist:
if i == '(': #左括号
currentTree.insertLeft('')
pStack.push(currentTree) #入栈下降
currentTree = currentTree.getLeftChild()
elif i not in ['+','-','*','/',')']: #操作数
currentTree.setRootVal(int(i))
parent = pStack.pop() #出栈上升
currentTree = parent
elif i in ['+','-','*','/']: #操作符
currentTree.setRootVal(i)
currentTree.insertRight('')
pStack.push(currentTree) #入栈下降
currentTree = currentTree.getRightChild()
elif i == ')': #右括号
currentTree = pStack.pop() #出栈上升
else:
raise ValueError
return eTree
import operator
def evaluate(parseTree): #计算解析树(递归)
opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}
leftC = parseTree.getLeftChild() #缩小规模
rightC = parseTree.getRightChild()
if leftC and rightC:
return opers[parseTree.getRootVal()](evaluate(leftC), evaluate(rightC)) #递归调用
else:
return parseTree.getRootVal() #叶子节点直接返回值(基本结束条件)
''' # 后序遍历实现
res1 = None
res2 = None
if tree:
res1 = evaluate(parseTree.getLeftChild())
res2 = evaluate(parseTree.getRightChild())
if res1 and res2:
return opers[parseTree.getRootVal()](res1, res2)
else:
return parseTree.getRootVal()
'''

树的遍历#

深度优先搜索 (DFS)#

  1. 前序遍历 先访问根节点,再递归前序访问左子树,最后前序访问右子树
def preorder(tree):
if tree != None:
print(tree.getRootVal())
preorder(tree.getLeftChild())
preorder(tree.getRightChild())
  1. 中序遍历 先递归中序访问左子树,再访问根节点,最后中序访问右子树
def inorder(tree):
if tree != None:
inorder(tree.getLeftChild())
print(tree.getRootVal())
inorder(tree.getRightChild())
  1. 后序遍历 先递归后序访问左子树,再后序访问右子树,最后访问根节点
def postorder(tree):
if tree != None:
postorder(tree.getLeftChild())
postorder(tree.getRightChild())
print(tree.getRootVal())

广度优先搜索 (BFS)#

层序遍历

先访问根节点,再访问左子树,最后访问右子树

def levelorder(tree):
if tree != None:
print(tree.getRootVal())
levelorder(tree.getLeftChild())
levelorder(tree.getRightChild())

优先队列和二叉堆#

ADT BinaryHeap的操作定义如下

  • BinaryHeap() 创建一个空二叉堆对象
  • insert(k) 添加一个新key到堆中
  • findMin() 返回堆中最小的元素
  • delMin() 返回堆中最小的元素,并从堆中删除
  • isEmpty() 返回堆是否为空
  • size() 返回堆中key的个数
  • buildHeap(list) 从一个key列表中创建一个堆

python实现

class BinaryHeap:
def __init__(self):
self.heapList = [0]
self.currentSize = 0
def percUp(self,i): #上浮
while i // 2 > 0:
if self.heapList[i] < self.heapList[i // 2]:
tmp = self.heapList[i // 2]
self.heapList[i // 2] = self.heapList[i] #与父节点交换
self.heapList[i] = tmp
i = i // 2
def insert(self,k): #插入
self.heapList.append(k)
self.currentSize = self.currentSize + 1
self.percUp(self.currentSize)
def percDown(self,i): #下沉
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
tmp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = tmp
i = mc
def minChild(self,i): #最小子节点
if i * 2 + 1 > self.currentSize:
return i * 2
else:
if self.heapList[i*2] < self.heapList[i*2+1]:
return i * 2
else:
return i * 2 + 1
def delMin(self): #删除最小值
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize = self.currentSize - 1
self.heapList.pop()
self.percDown(1)
return retval

二叉查找树(BST)#

class BinarySearchTree:
def __init__(self):
self.root = None
self.size = 0
def length(self):
return self.size
def __len__(self):
return self.size
def __iter__(self):
return self.root.__iter__()
def put(self,key,val): #插入节点
if self.root:
self._put(key,val,self.root)
else:
self.root = TreeNode(key,val)
self.size = self.size + 1
def _put(self,key,val,currentNode): #插入节点
if key < currentNode.key:
if currentNode.hasLeftChild():
self._put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
else:
if currentNode.hasRightChild():
self._put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
def __setitem__(self,k,v):
self.put(k,v)
def get(self,key): #获取节点
if self.root:
res = self._get(key,self.root)
if res:
return res.payload
else:
return None
else:
return None
def _get(self,key,currentNode): #获取节点
if not currentNode:
return None
elif currentNode.key == key:
return currentNode
elif key < currentNode.key:
return self._get(key,currentNode.leftChild)
else:
return self._get(key,currentNode.rightChild)
def __getitem__(self,key): #获取节点
return self.get(key)
def __contains__(self,key): #是否包含节点
if self._get(key,self.root):
return True
else:
return False
def delete(self,key): #删除节点
if self.size > 1:
nodeToRemove = self._get(key,self.root)
if nodeToRemove:
self.remove(nodeToRemove)
self.size = self.size - 1
else:
raise KeyError('Error, key not in tree')
elif self.size == 1 and self.root.key == key:
self.root = None
self.size = self.size - 1
else:
raise KeyError('Error, key not in tree')
def __delitem__(self,key):
self.delete(key)
def remove(self,currentNode): #删除节点
if currentNode.isLeaf(): # 1) 叶子节点
if currentNode.isRoot():
self.root = None
elif currentNode.isLeftChild():
currentNode.parent.leftChild = None
else:
currentNode.parent.rightChild = None
elif currentNode.hasBothChildren(): # 2) 两个子节点
succ = currentNode.findSuccessor()
succ.spliceOut()
currentNode.key = succ.key
currentNode.payload = succ.payload
else: # 3) 只有一个子节点
child = currentNode.leftChild if currentNode.hasLeftChild() else currentNode.rightChild
if currentNode.isRoot():
self.root = child
child.parent = None
elif currentNode.isLeftChild():
currentNode.parent.leftChild = child
child.parent = currentNode.parent
else:
currentNode.parent.rightChild = child
child.parent = currentNode.parent
class TreeNode:
def __init__(self,key,val,left=None,right=None,parent=None):
self.key = key
self.payload = val
self.leftChild = left
self.rightChild = right
self.parent = parent
def hasLeftChild(self): #是否有左子节点
return self.leftChild
def hasRightChild(self): #是否有右子节点
return self.rightChild
def isLeftChild(self): #是否是左子节点
return self.parent and self.parent.leftChild == self
def isRightChild(self): #是否是右子节点
return self.parent and self.parent.rightChild == self
def isRoot(self): #是否是根节点
return not self.parent
def isLeaf(self): #是否是叶子节点
return not (self.rightChild or self.leftChild)
def hasAnyChildren(self): #是否有子节点
return self.rightChild or self.leftChild
def hasBothChildren(self): #是否有两个子节点
return self.rightChild and self.leftChild
def replaceNodeData(self,key,value,lc,rc): #替换节点数据
self.key = key
self.payload = value
self.leftChild = lc
self.rightChild = rc
if self.hasLeftChild():
self.leftChild.parent = self
if self.hasRightChild():
self.rightChild.parent = self
def __iter__(self): #中序遍历
if self:
if self.hasLeftChild():
for elem in self.leftChild:
yield elem
yield self.key
if self.hasRightChild():
for elem in self.rightChild:
yield elem
def findSuccessor(self): #删除节点时找到后继节点
succ = None
if self.hasRightChild():
succ = self.rightChild.findMin()
else:
if self.parent:
if self.isLeftChild():
succ = self.parent
else:
self.parent.rightChild = None
succ = self.parent.findSuccessor()
self.parent.rightChild = self
return succ
def findMin(self): #找到最小节点
current = self
while current.hasLeftChild():
current = current.leftChild
return current
def spliceOut(self): #删除节点时摘除当前节点
if self.isLeaf():
if self.isLeftChild():
self.parent.leftChild = None
else:
self.parent.rightChild = None
elif self.hasAnyChildren():
if self.hasLeftChild():
if self.isLeftChild():
self.parent.leftChild = self.leftChild
else:
self.parent.rightChild = self.leftChild
self.leftChild.parent = self.parent
else:
if self.isLeftChild():
self.parent.leftChild = self.rightChild
else:
self.parent.rightChild = self.rightChild
self.rightChild.parent = self.parent

AVL树(平衡二叉查找树)#

复杂度最差为O(log n)

实现方法: 在插入和删除节点时,通过旋转操作保持树的平衡

Mermaid:4 种失衡与旋转示意#

  1. LL(左左):对失衡节点做一次右旋
flowchart TB subgraph Before_LL["LL 失衡(插入到左子树的左侧)"] A1(("A")) --> B1(("B")) A1 --> Ar1(("T3")) B1 --> C1(("C")) B1 --> Br1(("T2")) C1 --> Cl1(("T0")) C1 --> Cr1(("T1")) end subgraph After_LL["右旋 A 后"] B2(("B")) --> C2(("C")) B2 --> A2(("A")) C2 --> Cl2(("T0")) C2 --> Cr2(("T1")) A2 --> Ar2(("T3")) A2 --> Al2(("T2")) end
  1. RR(右右):对失衡节点做一次左旋
flowchart TB subgraph Before_RR["RR 失衡(插入到右子树的右侧)"] A1(("A")) --> Al1(("T0")) A1 --> B1(("B")) B1 --> Bl1(("T1")) B1 --> C1(("C")) C1 --> Cl1(("T2")) C1 --> Cr1(("T3")) end subgraph After_RR["左旋 A 后"] B2(("B")) --> A2(("A")) B2 --> C2(("C")) A2 --> Al2(("T0")) A2 --> Ar2(("T1")) C2 --> Cl2(("T2")) C2 --> Cr2(("T3")) end
  1. LR(左右):先对左子节点左旋,再对失衡节点右旋
flowchart TB subgraph Before_LR["LR 失衡(插入到左子树的右侧)"] A1(("A")) --> B1(("B")) A1 --> Ar1(("T3")) B1 --> Bl1(("T0")) B1 --> C1(("C")) C1 --> Cl1(("T1")) C1 --> Cr1(("T2")) end subgraph After_LR["左旋 B,再右旋 A 后"] C2(("C")) --> B2(("B")) C2 --> A2(("A")) B2 --> Bl2(("T0")) B2 --> Br2(("T1")) A2 --> Al2(("T2")) A2 --> Ar2(("T3")) end
  1. RL(右左):先对右子节点右旋,再对失衡节点左旋
flowchart TB subgraph Before_RL["RL 失衡(插入到右子树的左侧)"] A1(("A")) --> Al1(("T0")) A1 --> B1(("B")) B1 --> C1(("C")) B1 --> Br1(("T3")) C1 --> Cl1(("T1")) C1 --> Cr1(("T2")) end subgraph After_RL["右旋 B,再左旋 A 后"] C2(("C")) --> A2(("A")) C2 --> B2(("B")) A2 --> Al2(("T0")) A2 --> Ar2(("T1")) B2 --> Bl2(("T2")) B2 --> Br2(("T3")) end
def __put(self,key,val,currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self.__put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
self.updateBalance(currentNode.leftChild)
else:
if currentNode.hasRightChild():
self.__put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
self.updateBalance(currentNode.rightChild)
def updateBalance(self,node):
if node.balanceFactor > 1 or node.balanceFactor < -1:
self.rebalance(node) #重新平衡
return
if node.parent != None:
if node.isLeftChild():
node.parent.balanceFactor += 1
elif node.isRightChild():
node.parent.balanceFactor -= 1
if node.parent.balanceFactor != 0:
self.updateBalance(node.parent) #调整父节点因子
def rotateLeft(self,rotRoot):
newRoot = rotRoot.rightChild
rotRoot.rightChild = newRoot.leftChild
if newRoot.leftChild != None:
newRoot.leftChild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newRoot
else:
if rotRoot.isLeftChild():
rotRoot.parent.leftChild = newRoot
else:
rotRoot.parent.rightChild = newRoot
newRoot.leftChild = rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)
newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)
def rebalance(self,node):
if node.balanceFactor < 0:
if node.rightChild.balanceFactor > 0:
#右左旋
self.rotateRight(node.rightChild)
self.rotateLeft(node)
else:
#左旋
self.rotateLeft(node)
elif node.balanceFactor > 0:
if node.leftChild.balanceFactor < 0:
#左右旋
self.rotateLeft(node.leftChild)
self.rotateRight(node)
else:
#右旋
self.rotateRight(node)

红黑树(Red-Black Tree)#

性质

  1. 每个节点要么是红色,要么是黑色
  2. 根节点是黑色
  3. 所有叶子(指空节点 )都是黑色
  4. 不允许出现两个相邻的红色节点(不能出现“红-红相连”)
  5. 从任一节点到其所有叶子节点(空节点)的路径上,黑色节点数相同

直观理解:红黑树允许“局部不平衡”,但通过黑高约束避免退化成链表。

由性质 4 与性质 5 可以推出:任一路径上红节点不会连续出现,最长路径不会超过最短路径的 2 倍,因此高度是O(log n)。

红黑树插入操作#

红黑树删除操作#

  • AVL:更严格平衡,通常查找更快,但插删调整可能更多
  • 红黑:更宽松平衡,插删更“省旋转/更稳定”,工程中常用(如很多语言库的有序映射/集合)
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

树学习笔记
https://chaojixin.ren/posts/树学习笔记/
作者
超級の新人
发布于
2026-03-22
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00