一、基础知识总结
树的定义
树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。
-
树具有的特点:
-
(1)每个结点有零个或多个子结点
(2)没有父节点的结点称为根节点
(3)每一个非根结点有且只有一个父节点
(4)除了根结点外,每个子结点可以分为多个不相交的子树。
若一个结点有子树,那么该结点称为子树根的“双亲”,子树的根称为该结点的“孩子”。有相同双亲的结点互为“兄弟”。一个结点的所有子树上的任何结点都是该结点的后裔。从根结点到某个结点的路径上的所有结点都是该结点的祖先。
-
树的基本术语:
-
结点的度:结点拥有的子树的数目
叶子结点:度为0的结点
分支结点:度不为0的结点
树的度:树中结点的最大的度
层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1
树的高度:树中结点的最大层次
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。
二叉树
1、二叉树的定义
二叉树是每个结点最多有两个子树的树结构。它有五种基本形态:二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空。
2、二叉树的性质
性质1:二叉树第i层上的结点数目最多为
2i−1(i>=1)
2
i
−
1
(
i
>=
1
)
性质2:深度为k的二叉树最多有
2k−1(k>=1)
2
k
−
1
(
k
>=
1
)
个结点
性质3:包含n个结点的二叉树的高度至少为
(log2n)+1
(
l
o
g
2
n
)
+
1
性质4:在任意一棵二叉树中,若终端结点的个数为
N0
N
0
,度为2的结点数为
N2
N
2
,则
N0=N2+1
N
0
=
N
2
+
1
性质4的证明
因为二叉树中所有结点的度数均不大于2,不妨设 N0 N 0 表示度为0的结点个数, N1 N 1 表示度为1的结点个数, N2 N 2 表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到: N=N0+N1+N2 N = N 0 + N 1 + N 2 (1)
由度之间的关系可得第二个等式: N=N0∗0+N1∗1+N2∗2+1 N = N 0 ∗ 0 + N 1 ∗ 1 + N 2 ∗ 2 + 1
即 N=N1+2N2+1 N = N 1 + 2 N 2 + 1 (2)
将(1)(2)组合在一起可得到 N0=N2+1 N 0 = N 2 + 1
满二叉树、完全二叉树和二叉查找树
1、满二叉树
定义:高度为h,并且由 2h−1 2 h − 1 个结点组成的二叉树,称为满二叉树
2、完全二叉树
定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下层的叶子结点集中在靠左的若干位置上,这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。
一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
如果一棵完全二叉树的结点总数为n,那么叶子结点等于 n/2 n / 2 (当n为偶数时)或者 (n+1)/2 ( n + 1 ) / 2 (当n为奇数时)
3、二叉查找树
定义:二叉查找树又被称为二叉搜索树。设x为二叉查找树中的一个结点,x结点包含关键字key,结点x的key值计为key[x]。如果y是x的左子树中的一个结点,则key[y]<=key[x];如果y是x的右子树的一个结点,则key[y]>=key[x]
-
在二叉查找树中:
-
(1)若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
(2)若任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
(3)任意结点的左、右子树也分别为二叉查找树。
(4)没有键值相等的结点。
二叉树的遍历方式
-
二叉树的四种遍历方式,前序遍历,中序遍历,后序遍历和按层遍历。
-
前序遍历的排列方式为:根>左>右
中序遍历的排列方式为:左>根>右
后序遍历的排列方式为:左>右>根
按层遍历,从上往下,从左往右
二叉树代码实现
# coding=utf-8
class Node(object):
"""节点类"""
def __init__(self, data=-1, lchild=None, rchild=None):
self.data = data
self.lchild = lchild
self.rchild = rchild
self.child_number = 0
self.parent = None
self.name = None
def add_Rchild(self, node):
if self.rchild is not None:
self.rchild = node
else:
self.rchild = node
self.child_number += 1
node.set_parent(self)
def drop_Rchild(self):
self.rchild = None
self.child_number -= 1
def set_parent(self, node):
self.parent = node
def add_Lchild(self, node):
if self.lchild is not None:
self.lchild = node
else:
self.lchild = node
self.child_number += 1
node.set_parent(self)
def drop_Lchild(self):
self.lchild = None
self.child_number -= 1
class Tree(object):
"""树类"""
def __init__(self):
self.root = Node()
self.myQueue = []
self.size = 1
# 存放各层节点数目
self.n = []
# 初始化层,否则列表访问无效
for item in range(pow(2, 5)):
self.n.append(0)
# 索引标识
self.maxwidth = 0
self.i = 0
def width(self, root):
"""二叉树的最大宽度"""
if root is None:
return
else:
# 如果是访问根节点
if self.i == 0:
# 第一层加一
self.n[0] = 1
# 到达第二层
self.i += 1
if root.lchild:
self.n[self.i] += 1
if root.rchild:
self.n[self.i] += 1
# print('临时数据:', self.n)
else:
# 访问子树
self.i += 1
# print('二叉树所在层数:', self.i)
if root.lchild:
self.n[self.i] += 1
if root.rchild:
self.n[self.i] += 1
# 开始判断, 取出最大值
# maxwidth = max(maxwidth, n[i])
# maxwidth.append(max(max(maxwidth), n[i]))
self.maxwidth = max(self.maxwidth, self.n[self.i])
# 遍历左子树
self.width(root.lchild)
# 往上退一层
self.i -= 1
# 遍历右子树
self.width(root.rchild)
return self.maxwidth
def height(self, root):
"""二叉树的最大高度"""
if not root:
return 0
ldeepth = self.height(root.lchild)
rdeepth = self.height(root.rchild)
return max(ldeepth + 1, rdeepth + 1)
def deepth(self, root):
"""二叉树的最大深度"""
return self.height(root) - 1
def getsize(self):
"""二叉树包含的节点数目"""
stack = [self.root]
# 为了正确获取数目,这里需要先初始化一下
self.size = 0
while stack:
temp = stack.pop(0)
self.size += 1
if temp.lchild:
stack.append(temp.lchild)
if temp.rchild:
stack.append(temp.rchild)
return self.size
def add(self, data):
"""为树添加节点"""
node = Node(data)
if self.root.data == -1: # 如果树是空的,则对根节点赋值
self.root = node
self.myQueue.append(self.root)
else:
treeNode = self.myQueue[0] # 此结点的子树还没有齐。
if treeNode.lchild is None:
treeNode.lchild = node
self.myQueue.append(treeNode.lchild)
else:
treeNode.rchild = node
self.myQueue.append(treeNode.rchild)
self.myQueue.pop(0) # 如果该结点存在右子树,将此结点丢弃。
def front_digui(self, root):
"""利用递归实现树的前序遍历"""
if root is None:
return
print(root.data, end='\t')
self.front_digui(root.lchild)
self.front_digui(root.rchild)
def middle_digui(self, root):
"""利用递归实现树的中序遍历"""
if root is None:
return
self.middle_digui(root.lchild)
print(root.data, end='\t')
self.middle_digui(root.rchild)
def later_digui(self, root):
"""利用递归实现树的后序遍历"""
if root is None:
return
self.later_digui(root.lchild)
self.later_digui(root.rchild)
print(root.data, end='\t')
def front_stack(self, root):
"""利用堆栈实现树的前序遍历"""
if root is None:
return
myStack = []
node = root
while node or myStack:
while node: # 从根节点开始,一直找它的左子树
print(node.data, end='\t')
myStack.append(node)
node = node.lchild
node = myStack.pop() # while结束表示当前节点node为空,即前一个节点没有左子树了
node = node.rchild # 开始查看它的右子树
def middle_stack(self, root):
"""利用堆栈实现树的中序遍历"""
if root is None:
return
myStack = []
node = root
while node or myStack:
while node: # 从根节点开始,一直找它的左子树
myStack.append(node)
node = node.lchild
node = myStack.pop() # while结束表示当前节点node为空,即前一个节点没有左子树了
print(node.data, end='\t')
node = node.rchild # 开始查看它的右子树
def later_stack(self, root):
"""利用堆栈实现树的后序遍历"""
if root is None:
return
myStack1 = []
myStack2 = []
node = root
myStack1.append(node)
while myStack1: # 这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
node = myStack1.pop()
if node.lchild:
myStack1.append(node.lchild)
if node.rchild:
myStack1.append(node.rchild)
myStack2.append(node)
while myStack2: # 将myStack2中的元素出栈,即为后序遍历次序
print(myStack2.pop().data, end='\t')
def level_queue(self, root):
"""利用队列实现树的层次遍历"""
if root is None:
return
myQueue = []
node = root
myQueue.append(node)
while myQueue:
node = myQueue.pop(0)
print(node.data, end='\t')
if node.lchild is not None:
myQueue.append(node.lchild)
if node.rchild is not None:
myQueue.append(node.rchild)
if __name__ == '__main__':
"""主函数"""
elems = range(10) # 生成十个数据作为树节点
tree = Tree() # 新建一个树对象
for elem in elems:
tree.add(elem) # 逐个添加树的节点
print('队列实现层次遍历:')
tree.level_queue(tree.root)
print('\n\n递归实现前序遍历:')
tree.front_digui(tree.root)
print('\n递归实现中序遍历:')
tree.middle_digui(tree.root)
print('\n递归实现后序遍历:')
tree.later_digui(tree.root)
print('\n\n堆栈实现前序遍历:')
tree.front_stack(tree.root)
print('\n堆栈实现中序遍历:')
tree.middle_stack(tree.root)
print('\n堆栈实现后序遍历:')
tree.later_stack(tree.root)
print('\n递归实现树的节点数:')
print(tree.getsize())
print('\n递归实现树的高度:')
print(tree.height(tree.root))
print('\n递归实现树的深度:')
print(tree.deepth(tree.root))
print('\n递归实现树的宽度:')
print(tree.width(tree.root))
霍夫曼树实现
霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
树的路径长度是从树根到每一结点的路径长度之和,记为 WPL=(W1∗L1+W2∗L2+W3∗L3+...+Wn∗Ln) W P L = ( W 1 ∗ L 1 + W 2 ∗ L 2 + W 3 ∗ L 3 + . . . + W n ∗ L n ) ,N个权值Wi(i=1,2,…n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,…n)。可以证明霍夫曼树的WPL是最小的。
-
霍夫曼树具有如下特性:
- 对于同一组权值,所能得到的霍夫曼树不一定是唯一的。
- 霍夫曼树的左右子树可以互换,因为这并不影响树的带权路径长度。
- 带权值的节点都是叶子节点,不带权值的节点都是某棵子二叉树的根节点。
- 权值越大的节点越靠近霍夫曼树的根节点,权值越小的节点越远离霍夫曼树的根节点。
- 霍夫曼树中只有叶子节点和度为2的节点,没有度为1的节点。
- 一棵有n个叶子节点的霍夫曼树共有2n-1个节点。 霍夫曼树的构建步骤如下:
- 1、将给定的n个权值看做n棵只有根节点(无左右孩子)的二叉树,组成一个集合HT,每棵树的权值为该节点的权值。
- 2、从集合HT中选出2棵权值最小的二叉树,组成一棵新的二叉树,其权值为这2棵二叉树的权值之和。
- 3、将步骤2中选出的2棵二叉树从集合HT中删去,同时将步骤2中新得到的二叉树加入到集合HT中。
- 4、重复步骤2和步骤3,直到集合HT中只含一棵树,这棵树便是霍夫曼树。
class Node(object):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = None
self.right = None
class Huffman(object):
def __init__(self, items=[]):
while len(items) != 1:
a, b = items[0], items[1]
newvalue = a.value + b.value
newnode = Node(value=newvalue)
newnode.left, newnode.right = a, b
items.remove(a)
items.remove(b)
items.append(newnode)
items = sorted(items, key=lambda node: int(node.value))
# 每次都要记得更新新的霍夫曼树的根节点
self.root = newnode
def print(self):
queue = [self.root]
while queue:
current = queue.pop(0)
print(current.value, end='\t')
if current.left:
queue.append(current.left)
if current.right:
queue.append(current.right)
def create_huffman_tree(lists):
while len(lists) > 1:
a, b = lists[0], lists[1]
node = Node(value=int(a.value+b.value))
node.left, node.right = a, b
lists.remove(a)
lists.remove(b)
lists.append(node)
lists = sorted(lists, key=lambda node: node.value)
return lists
def scan(root):
if root:
queue = [root]
while queue:
current = queue.pop(0)
print(current.value, end='\t')
if current.left:
queue.append(current.left)
if current.right:
queue.append(current.right)
if __name__ == '__main__':
ls = [Node(i) for i in range(1, 10)]
huffman = Huffman(items=ls)
huffman.print()
print()
ls = [Node(i) for i in range(1, 10)]
root = create_huffman_tree(ls)[0]
scan(root)