1. 概念
父节点,子节点,兄弟节点
根节点,叶子节点
高度:从下往上数的高度,即节点到叶子节点的最长路径
深度:从上往下数,看看有多深。即根节点到该结点经历的边数
层数:深度+1,即从1开始树在第几层
树的高度:根节点的高度
二叉树,满二叉树,完全二叉树
** 存储 **
链式存储
class BiTreeNode{
def __init__(self,data,left,right):
self.data = data
self.left = left
self.right = right
}
顺序存储
根结点i,左子节点2i,右子节点2i+1
(堆其实就是完全二叉树!!)
2. 二叉树遍历
前序:
def preOrder(root):
if root == None:
return
print root
preOrder(root.left)
preOrder(root.right)
中序:
def inOrder(root):
if root == None:
return
inOrder(root.left)
print root
inOrder(root.right)
后序:
def postOrder(root):
if root == None:
return
postOrder(root.left)
postOrder(root.right)
print root
3. 二叉查找树
任意一个节点,其左子树的每个节点的值,都要小于该结点;右子树大于该节点
1. 查找
def find(root,data):
p = root
while not p:
if data < p.data:
p = p.left
elif data > p.data:
p = p.right
else:
return p
return None
2. 插入
def insert(root,data):
if not root:
return Node(data)
p = root
while not p:
if data > p.data:
if not p.right:
p.right = Node(data)
return
p = p.right
else:
if not p.left:
p.left = Node(data)
return
p = p.left
3. 删除
分三种情况:
- 删除的节点没有子节点,直接将父节点中,指向该节点的指针置为null
- 删除的节点只有一个子节点,更新父节点中,指向删除节点的指针为指向其子节点
- 删除的节点有两个子节点:找到节点的右子树中的最小节点,替换到要删除的节点上(最小结点肯定没有左子节点)
def delete(root,data):
p = root
pre = None
// 先查找data
while p!=None and p.data!=data:
pre = p
if data > p.data:
p = p.right
else:
p = p.left
if p==None:
return
// 要删除的节点有两个子节点
if p.left != None and p.right != None:
minP = p.right
preMinP = p
while minP.left != None:
preMinP = minP
minP = minP.left
p.data = minP.data
// 交换后变化为删除minp
p = minP
pre = preMinP
// 删除节点是叶子或只有一个子节点
child = Node()
if p.left != None:
child = p.left
elif p.right != None:
child = p.right
else:
child = None
if pre == None: // 删除根节点
root = child
elif pre.left == p:
pre.left = child
else:
pre.right = child
删除也可标记为已删除,就无需以上操作
3. 支持重复数据的二叉查找树
- 通过链表存储相同的数据
- 把新的插入数据当作大于该节点的值来处理
查找数据时,遇到值相同的节点,需要继续在右子树查找,直至叶子节点
删除数据时,需要将所有的节点删除
4. 散列表和二叉树
- 散列表无序存储,输出有序数据需要先排序;二叉树只需要中序遍历就可有序,时间复杂度为O(n)
- 散列表耗时,且散列冲突时不稳定;二叉树有平衡二叉树解决不稳定的问题,时间复杂度保持在O(logn)
- 由于哈希冲突,实际查找速度可能大于O(logn);且哈希函数耗时大
- 散列表构造复杂,需考虑散列函数的设计、冲突解决办法、扩容、缩容等。平衡二叉查找树只需要考虑平衡性这一个问题,而且这个问题的解决方案比较成熟、固定。
- 为了避免过多的散列冲突,散列表装载因子不能太大,特别是基于开放寻址法解决冲突的散列表,不然会浪费一定的存储空间。
二叉树相关来leetcode
leetcode 104 二叉树的最大深度
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
leftLength = 1
rightLength = 1
p = root
if p.left!=None:
leftLength = 1+self.maxDepth(p.left)
if p.right!=None:
rightLength = 1+self.maxDepth(p.right)
return leftLength if leftLength>=rightLength else rightLength
leetcode 450 删除二叉搜索树中的节点
注意pre的变化;
有两个节点时,取右子树的最小的节点
所以minP = p.right
preMinP = p
class Solution:
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
p = root
pre = None
while p != None and p.val != key:
pre = p
if key > p.val:
p = p.right
else:
p = p.left
if p == None:
return root
# 有两个节点
if p.left != None and p.right != None:
minP = p.right
preMinP = p
while minP.left != None:
preMinP = minP
minP = minP.left
p.val = minP.val
p = minP
pre = preMinP
# 一个或无子节点
child = TreeNode()
if p.left != None:
child = p.left
elif p.right != None:
child = p.right
else:
child = None
if pre == None:
root = child
elif pre.left == p:
pre.left = child
elif pre.right == p:
pre.right = child
return root