插入一些知识点
大根堆建堆的时间复杂度是Olog(n)
平衡二叉树
find-min O(1)
delete-min O(logn)
insert O(logn)
merge O(n)
i是当前节点,根节点定义为0
- 它的左孩子的位置:2*i + 1
- 它的右孩子的位置:2*i + 2
- 父节点的位置: i-1 / 2
- 层数 logN (N节点总个数)
- 第n层的节点数:最多2^(n-1)
- 节点的度:节点所拥有的子树个数(0,1,2)
- 叶子节点:度为0的节点 (没有子树)
- 树的深度,高度:从上到下为深度1,深度2。。从下到上为高1,高2,。。。
- 一颗深度为k的二叉树,最多有2^k - 1个节点,最多k个
哈夫曼树
对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:
- 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
- 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
- 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。
图 2 哈夫曼树的构建过程
图 2 中,(A)给定了四个结点a,b,c,d,权值分别为7,5,2,4;第一步如(B)所示,找出现有权值中最小的两个,2 和 4 ,相应的结点 c 和 d 构建一个新的二叉树,树根的权值为 2 + 4 = 6,同时将原有权值中的 2 和 4 删掉,将新的权值 6 加入;进入(C),重复之前的步骤。直到(D)中,所有的结点构建成了一个全新的二叉树,这就是哈夫曼树。
以数据集{1,6,8,2,9,4}为权值构造一棵赫夫曼树,其带权路径长度为
一个重要的结论:
- 对于一颗非空二叉树,度为0的节点总是比度为2的节点多一个,即叶子节点的个数为n0, 度为2的节点为n2, n0 = n2 + 1
- 具有n个节点的完全二叉树的深度为 logn+1 如log5 = 2 再加1=34
例1:一颗完全二叉树的根节点有1001个节点, 其中叶子节点的个数为。
完全二叉树的公式: n=n0+n1+n2。 因为 n0 = n2+1 所以 n = n0-1 + n0 + n1 , 所以 2n0-1+n1 = 1001, 又因为是完全二叉树, 所以
n1只能为1或0, 根据1001所以n1=0, n0=501。
例2:X 如果一个二叉树的前序为abcdefg,中序为bcedagf,则该二叉树的后序为?
前序和中序可以确定二叉树的唯一结构。
- 根据前序遍历可知,二叉树的根节点为a,再由中序遍历,a左面的bced为它的左子树, 它右面的gf为它的右子树,确定①。
- 再看bced,看前序遍历它们位置是bcde,所以bced组成的子树,根节点是b,又因为中序bced, b前面什么都i没有,所以断定ced为b的右子树;gf子树的前序是fg,所以f为根节点,中序为gf,所以g是f的左子树。确定②。
- 再看ced,前序为cde,所以c为根节点,再看它的中序为ced,c前面什么都没有,所以ed为c的右子树。确定③。
- 看ed的前序为de,所以d为根节点,中序为ed,所以e为d的左子树,确定④。
- 递归遍历最终的树,得到⑤,比较前中序结果,确定数结构正确,最终确定后序遍历结果。
例3: 先序遍历序列为啊,a,b ,c d的不同二叉树的个数为?
实际再问,以abcd的顺序入栈, 出栈的情况有多少种?
卡特兰数公式 1/(n+1) * C(n, 2n)
n = 4 时 1/5*((8*7*6*5)/(4*3*2*1)) = 14
记住常用的值也好 1, 2, 5 ,14 ,42 , 132
一 前序中序后序递归实现
class BinaryTreeNode(object):
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
class BinaryTree(object):
def __init__(self, root=None):
self.root = root
def is_empty(self):
return self.root is None
def preOrder(self, BinaryTreeNode):
if BinaryTreeNode is None:
return
# 先打印根结点,再打印左结点,后打印右结点
print(BinaryTreeNode.data)
self.preOrder(BinaryTreeNode.left)
self.preOrder(BinaryTreeNode.right)
def inOrder(self, BinaryTreeNode):
if BinaryTreeNode is None:
return
# 先打印左结点,再打印根结点,后打印右结点
self.inOrder(BinaryTreeNode.left)
print(BinaryTreeNode.data)
self.inOrder(BinaryTreeNode.right)
def postOrder(self, BinaryTreeNode):
if BinaryTreeNode is None:
return
# 先打印左结点,再打印右结点,后打印根结点
self.postOrder(BinaryTreeNode.left)
self.postOrder(BinaryTreeNode.right)
print(BinaryTreeNode.data)
n1 = BinaryTreeNode(data="D")
n2 = BinaryTreeNode(data="E")
n3 = BinaryTreeNode(data="F")
n4 = BinaryTreeNode(data="B", left=n1, right=n2)
n5 = BinaryTreeNode(data="C", left=n3, right=None)
root = BinaryTreeNode(data="A", left=n4, right=n5)
bt = BinaryTree(root)
print('先序遍历')
bt.preOrder(bt.root)
print('中序遍历')
bt.inOrder(bt.root)
print('后序遍历')
bt.postOrder(bt.root)
# 先序遍历
# ABDECF
# 中序遍历
# DBEAFC
# 后序遍历
# DEBFCA
二 非递归实现
class BinaryTreeNode(object):
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
class BinaryTree(object):
def __init__(self, root=None):
self.root = root
def is_empty(self):
return self.root is None
def preOrder1(self):
"""
preOrder每次都将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个访问的节点,再访问其右子树。入栈打印
"""
if not self.root:
return
stack = []
node = self.root
while node or stack:
while node:
# 从根节点开始,一直找它的左子树
print(node.data)
stack.append(node)
node = node.left
# while结束表示当前节点node为空,即前一个节点没有左子树了
node = stack.pop()
# 开始查看它的右子树
node = node.right
def inOrder1(self, root):
"""
中序的非递归遍历与先序的非递归遍历类似。先序遍历是先访问节点,然后再将节点入栈,后中序遍历则是先入栈,然后节点弹出栈后再访问。出栈打印
"""
if not self.root:
return
stack = []
node = self.root
while node or stack:
while node:
# 从根节点开始,一直找到左子树
stack.append(node)
node = node.left
# while结束表示当前节点node为空,即前一个节点没有左子树了
node = stack.pop()
print(node.data)
node = node.right
def postOrder1(self, root):
"""
从直觉上来说,后序遍历对比中序遍历难度要增大很多。因为中序遍历节点序列有一点的连续性,而后续遍历则感觉有一定的跳跃性。先左,再右,
最后才中间节点;访问左子树后,需要跳转到右子树,右子树访问完毕了再回溯至根节点并访问之
"""
if not self.root:
return
stack1 = []
stack2 = []
node = self.root
stack1.append(node)
while stack1:
node = stack1.pop()
if node.left:
stack1.append(node.left)
if node.right:
stack1.append(node.right)
stack2.append(node)
while stack2:
print(stack2.pop().data)
def postOrder2(self):
"""
自己写的后序遍历,自己更容易理解, 把先序遍历, 改成先压右节点,变成 中右左 在用个列表逆序下, 左右中 变成后序
"""
if not self.root:
return
stack = []
stack1 = []
node = self.root
while node or stack:
while node:
stack1.append(node.data)
stack.append(node)
node = node.right
node = stack.pop()
node = node.left
for i in stack1[::-1]:
print(i)
n1 = BinaryTreeNode(data="D")
n2 = BinaryTreeNode(data="E")
n3 = BinaryTreeNode(data="F")
n4 = BinaryTreeNode(data="B", left=n1, right=n2)
n5 = BinaryTreeNode(data="C", left=n3, right=None)
root = BinaryTreeNode(data="A", left=n4, right=n5)
bt = BinaryTree(root)
print('非递归先序遍历')
bt.preOrder1()
print('非递归中序遍历')
bt.inOrder1()
print('非递归后续遍历')
bt.postOrder1()
三 构造二叉树还可以直接用列表构造
不过如[1, 2, 3 ,4, 5, 6, 7 ,8 ,9]
是这样的情况,不是列表的第一个节点作为root。
这样实现
四 判断是否是搜索二叉树
其实总体思路挺简单的, 就是中序遍历二叉树, 如果是搜索二叉树的话, 中序遍历是升序的。那么更优化方法是,就是中序变量时,记录下前继节点的值,再和当前的值比较, 如果前继节点小于当前节点,就继续, 否则直接返回False。
class TreeNode(object):
def __init__(self, data=None, left=None, right=None):
self.data = data
self.right = right
self.left = left
class TreeList(object):
def __init__(self, root=None, prev=None):
self.root = root
self.prev = prev
def arr_to_tree(self, arr, start, end):
if end >= start:
root = TreeNode()
# 最好+1, 能构成二叉搜索
mid = (start + end + 1) // 2
root.data = arr[mid]
root.left = self.arr_to_tree(arr, start, mid - 1)
root.right = self.arr_to_tree(arr, mid + 1, end)
else:
root = None
self.root = root
return root
def helper(self, root):
"""
判断是否是搜索二叉树
"""
if root is None:
return True
if not self.helper(root.left):
return False
if self.prev and self.prev.data >= root.data:
return False
self.prev = root
if not self.helper(root.right):
return False
return True
t = TreeList()
t.arr_to_tree([1, 2, 3, 4, 5, 6], 0, 5)
print(t.helper(t.root))
五 找到两个节点的最近公共节点
二叉树怎么找, 和搜素二叉树怎么找。
class TreeNode(object):
def __init__(self, data=None, left=None, right=None):
self.data = data
self.right = right
self.left = left
class TreeList(object):
def __init__(self, root=None, prev=None):
self.root = root
self.prev = prev
def arr_to_tree(self, arr, start, end):
if end >= start:
root = TreeNode()
# 最好+1, 能构成二叉搜索
mid = (start + end + 1) // 2
root.data = arr[mid]
root.left = self.arr_to_tree(arr, start, mid - 1)
root.right = self.arr_to_tree(arr, mid + 1, end)
else:
root = None
self.root = root
return root
def find_it(self, root, p, q):
"""
任意二叉树找最近的公共祖先
"""
if not root or root.data == p or root.data == q:
return root
tree_left = self.find_it(root.left, p, q)
tree_right = self.find_it(root.right, p, q)
if not tree_left:
return tree_right
elif not tree_right:
return tree_left
elif tree_left:
return root
else:
return
def lowest_common_ancestor(self, p, q):
"""
搜索二叉树找公共祖先
"""
root = self.root
while root:
if p < root.data > q:
root = root.left
elif p > root.data < q:
root = root.right
else:
return root
t = TreeList()
t.arr_to_tree([1, 2, 3, 4, 5, 6], 0, 5)
print()
print(t.lowest_common_ancestor(1, 3).data)
print(t.find_it(t.root, 1, 3).data)
二叉树集大成
class TreeNode(object):
def __init__(self, data=None, left=None, right=None):
self.data = data
self.right = right
self.left = left
class TreeList(object):
def __init__(self, root=None):
self.root = root
def arr_to_tree(self, arr, start, end):
"""
数组转成搜索二叉树, 别忘记传start,end
:param arr:
:param start:
:param end:
:return:
"""
if end >= start:
root = TreeNode()
# 最好+1, 能构成二叉搜索
mid = (start+end+1) // 2
root.data = arr[mid]
root.left = self.arr_to_tree(arr, start, mid-1)
root.right = self.arr_to_tree(arr, mid+1, end)
else:
root = None
self.root = root
return root
def pre_print(self):
"""
前序遍历
:return:
"""
if not self.root:
return
stack = []
node = self.root
while node or stack:
while node:
print(node.data)
stack.append(node)
node = node.left
node = stack.pop()
node = node.right
def ord_print(self):
"""
中序遍历
:return:
"""
if not self.root:
return
stack = []
node = self.root
while stack or node:
while node:
stack.append(node)
node = node.left
node = stack.pop()
print(node.data)
node = node.right
def last_print(self):
"""
后续遍历
:return:
"""
if not self.root:
return
stack, tmp = [], []
node = self.root
while stack or node:
while node:
stack.append(node)
tmp.append(node)
node = node.right
node = stack.pop()
node = node.left
for i in reversed(tmp):
print(i.data)
def revers_tree(self):
"""
反转二叉树
:return:
"""
root = self.root
if not root:
return
node_list = [root]
while node_list:
node = node_list.pop(0)
if node:
node_list.append(node.right)
node_list.append(node.left)
node.left, node.right = node.right, node.left
def level_order(self):
"""
层序遍历
"""
root = self.root
if not root:
return []
result = []
queue = deque()
queue.append(root)
while queue:
level_size = len(queue)
current_level = []
for _ in range(level_size):
node = queue.popleft()
current_level.append(node.data)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(current_level)
return result
def find_it(self, root, p, q):
"""
任意二叉树找最近的公共祖先
"""
if not root or root.data == p or root.data == q:
return root
tree_left = self.find_it(root.left, p, q)
tree_right = self.find_it(root.right, p, q)
if not tree_left:
return tree_right
elif not tree_right:
return tree_left
elif tree_left:
return root
else:
return
def lowest_common_ancestor(self, p, q):
"""
搜索二叉树找公共祖先
"""
root = self.root
while root:
if p < root.data > q:
root = root.left
elif p > root.data < q:
root = root.right
else:
return root
def helper(self, root):
"""
判断是否是搜索二叉树
"""
if root is None:
return True
if not self.helper(root.left):
return False
if self.prev and self.prev.data >= root.data:
return False
self.prev = root
if not self.helper(root.right):
return False
return True
def max_depth(self, root):
"""
最大深度
"""
if not root:
return 0
return 1 + max(self.max_depth(root.left), self.max_depth(root.right))
def min_depth(self, root):
"""
最小深度
"""
if not root:
return 0
left = self.min_depth(root.left)
right = self.min_depth(root.right)
if left == 0 or right == 0:
# 判断是否右左右子树
return left + right + 1
else:
return min(left, right) + 1
class Solution(object):
"""
实现的是二叉树中两个节点的最大路径
"""
def __init__(self):
self.tmp = []
def max_depth(self, root):
if not root:
return 0
left = self.max_depth(root.left)
right = self.max_depth(root.right)
return max(right, left) + 1
def max_distance(self, root):
if not root:
return 0
self.tmp.append(self.max_depth(root.left) + self.max_depth(root.right))
self.max_distance(root.left)
self.max_distance(root.right)
return max(self.tmp)
t = TreeList()
t.arr_to_tree([1, 2, 3, 4, 5, 6], 0, 5)
t.revers_tree()
t.pre_print()
t.ord_print()
s = Solution()
print(s.max_distance(t.root))
print(t.helper(t.root))