这篇文章挺简单的,就是数据结构中树和二叉树的一些理论要点。因为怕自己遗忘的太快,所以写出来加深记忆。读我的blog的话,要是从头学的话不建议看了,当做复习大纲还ok。
1、树的数据结构定义
D是具有相同特性的数据元素的集合。若D为空集,则称为空树。
若D仅含有一个元素,则R为空集,否则R={H},H是如下二元关系:
(1)D中存在唯一的root元素,在H关系下无前驱;
(2)若D除去root元素之外非空,则存在一个划分D1,D2,…,Dn,任意两个划分无交集,且唯一存在元素x,满足(root,x)属于H集合;
(3)对应于上述D1,D2,…,Dn的划分,存在唯一的划分H1,H2,…,Hn与之对应,且任意两个划分之间无交集,任何一个都是一个子树。
定义中强调了两点,(1)树种的关系有方向性,(2)树的D和H都有唯一划分,互无交集。
无序树即子树的各个元素没有顺序,不分 left 和right。
2、二叉树的性质
如果二叉树的终端结点树为n,度为2的节点数为 n2,则 n=n2+1。(我认为这一条性质不太直观,除此之外的性质都还挺直观好理解的,尤其是完全有序二叉树)
3、完全二叉树与满二叉树
满二叉树:二叉树每一层节点数都是最大的。
完全二叉树:其中的每一个节点序号都和满二叉树,也就是说只有最后一层的节点数无法达到最大。
4、二叉树的存储结构
(1)顺序存储结构:只适合完全二叉树,节点较少时非常浪费空间。深度为k的二叉树,空间复杂度为 o(2**k);
(2)链式存储结构:二叉链表,每个节点有三个域,左子节点,右子节点,数据域,可以方便找到子节点,但是要找父节点则需要从根节点遍历,复杂度为o(n);
三叉链表,在二叉链表基础上添加父节点域,可以方便找到父节点,复杂度为o(1)。
5、遍历二叉树
本质是将非线性的二叉树组织成规则的线性序列,然后进行线性化操作。
先序,中序,后序。
深度搜索,广度搜索。遍历的时间复杂度均为o(n),n为节点数量。
空间复杂度为o(m),m是遍历辅助栈,即树的深度。最坏情况下,树深度等于节点总数,树演变成链表,空间复杂度为o(n)。当存储结构为三叉链表时,不需要栈,空间复杂度为0。
6、线索二叉树
目的是在遍历时能够直接找到前驱和后继,而非仅仅是左右子节点的信息。
规定一个节点有5个域,数据域,左子节点,右子节点,左标志域,右标志域。当tag域为0时,子节点域指示指针,当tag域为1时,子节点域指示线索。这样方便寻找前驱和后继。
在此种线索链表中,中序的情况要比后续稍微简单一些,不需要设栈。而后续遍历则必须采用三叉链表。
线索化的实质是将空指针改为指向前驱或者后继的线索。
线索化后,在线索链表的头部添加头结点指向根节点,构成双向线索链表。
7、树与森林的存储结构
(1)双亲表示法:找父节点很容易,找孩子节点需要遍历,数组结构存储。
(2)孩子表示法:与(1)相反,由于有多个指针域,如果同构,则浪费存储空间,不同构则操作不方便。
(3)孩子兄弟表示法:将树以二叉树的形式进行存储。左子节点为孩子,右子节点为兄弟。按照分类进行的表示,展示了树和二叉树之间的转换关系。
8、树与森林的遍历
先序遍历,中序遍历。将树改为二叉树,然后采用二叉树的遍历完成数的遍历。
9、Huffman树,带权路径最优树
霍夫曼树是信息论中的一个概念,用于编码理论。叶子节点有权值,其余非叶子节点没有,权值越小,层次越深。构造huffman树的方法。
10、回溯法与树的遍历
八皇后算法,骑士游历,迷宫,选最优解,jump game中处理0元素(我的另一篇blog里面涉及这个问题,但是我没有处理掉这个问题jump game 的python代码),以后有时间把这几个问题全部解决掉。
11、树的计数
具有n个节点的不同形态的树共有多少颗,这是一个数学问题。结果不方便打出来,就算了吧。结果不重要,推导的思路才重要。
首先考虑二叉树的情况。root节点的左右子树的子节点数量为 i 和 n-i-1,然后递归就好了。
给定前序遍历和中序遍历,可以唯一确定一颗树。
我自己写了一个最简单版本的二叉树数据结构的实现,还不是平衡的。这个数据结构中还有漏洞:广度优先我没实现了,树深度还没算出来写上去,等随后有时间实现一下吧。
class Node(object):
'''
二叉树的链式表示,但不是线索二叉树,线索二叉树还要加上结点的前驱和后继
'''
def __init__(self, name=-1, left=0, right=0, value=None):
self.value = value # 树结点的值
self.left = left
self.right = right
self.name = name # 用于标记树中的结点,唯一,不可重复,必须有才行
class BinaryTree(object):
def __init__(self):
self.root = 0
def init_bin_tree(self, data):
'''
data 是 BinaryTree 的一个实例,并且制定将这个实例放在根节点下
'''
self.root = data
def is_empty(self): # 判断树是否为空,如果根节点指针都没有指向它,说明树为空
if self.root == 0:
return True
else:
return False
def clear_binary_tree(self):
self.root = 0
def add_node(self, name, rorl, value=value):
"""为树添加节点,给某个结点name添加子节点,rorl确定是左还是右"""
node = Node(name=name, value=value)
parent_node = self.get_node(name, self.root)
if rorl == 'r' and parent_node.right == 0:
parent_node.right = node
elif rorl == 'l' and parent_node.left == 0:
parent_node.left = node
else:
print "Wrong rorl parameter or the r or l child node has already existed."
def replace_node(self, name, rorl, value=value):
"""为树替换某个节点,给某个结点name添加子节点,rorl确定是左还是右"""
node = Node(name=name, value=value)
parent_node = self.get_node(name, self.root)
if rorl == 'r' and parent_node.right != 0:
parent_node.right = node
elif rorl == 'l' and parent_node.left != 0:
parent_node.left = node
else:
print "Wrong rorl parameter or the r or l child node is empty."
def traverse_parent_first(self, node): # 先序遍历,node 一定要给到 self.node
if node == 0:
return
print "Node(" + node.name + ")"
self.traverse_parent_first(node.left)
self.traverse_parent_first(node.right)
def traverse_parent_middle(self, node): # 中序遍历,node 给到根节点
if node == 0:
return
self.traverse_parent_first(node.left)
print "Node(" + node.name + ")"
self.traverse_parent_first(node.right)
def traverse_parent_last(self, node=self.root): # 中序遍历,node 给到根节点
if node == 0:
return
self.traverse_parent_last(node.left)
self.traverse_parent_last(node.right)
print "Node(" + node.name + ")"
def traverse_deep_first(self, node): # 深度优先遍历
if node == 0:
return
print "Node(" + node.name + ")"
if node.left != 0:
self.traverse_deep_first(node.left)
if node.right != 0:
self.traverse_deep_first(node.right)
def get_root(self):
if self.is_empty():
print "The binary tree is empty."
return
else:
return self.root
def get_value(self, name):
node = self.get_node(name, self.root)
if node:
return node.value
def get_node(self, name, node): # 获取 name 结点,node必须是根节点
if node == 0:
return
if node.name == name:
return node
if name.left != 0:
return self.get_node(name, node.left)
if name.right != 0:
return self.get_node(name, node.right)
def traverse_broad_first(self, node): # 广度优先遍历,还没想好
pass