树分为有序树和无序树。
无序树是树中任意节点的子节点没有顺序关系,也成为自由树。基本无应用价值。
有序树是树中任意结点之间有顺序关系,成为有序树。分为二叉树 、霍夫曼树、B树。
二叉树:每个节点最多含有两个子树的树成为二叉树。二叉树是有序树。
完全二叉树,对于一颗二叉树,假设其深度为d(d>1),
除了第d层外,其余各层节点数目均已达到最大值。且第d层所有结点从左到右连续的紧密排雷,这样的二叉树称为完全数。
满二叉树,所有叶结点都在最底层的二叉树。
平衡二叉树(AVL树):当且仅当任何结点的两颗子树的高度不大于1的二叉树。
排序二叉树(BST):又称二叉查找树,有序二叉树,二叉搜索树
B树:一种对读写操作进行优化的二叉查找树,能够保持数据有序,拥有多余两个子树。
输的存储有顺序存储和链式存储。
顺序存储在遍历速度有优势。但是不能应对残缺树,因此一般采用链式存储。
二叉树的性质:
1.在二叉树上第i层最多有2^(i-1)个结点
2.深度为k的二叉树上最多有2^k-1个结点
3.对于任意1颗二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0 = N2 +1
4.具有n个结点的完全二叉树的深度必维log(n+1)
5. 对于完全二叉树,若从上至下,从左至右编号,则编号为i的结点,其左孩子的编号为2i,右孩子结点编号为2i+1,其双亲的编号必为i/2
树的遍历:
分为广度遍历和深度遍历
深度遍历分为先序遍历,中序遍历和后续遍历。
二叉树的广度遍历代码如下,主要利用队列作为数据存储,先进先出的原则:
def breadth_travel(self):#s广度遍历
if self.root is None:
return
queue =[self.root]
while queue:
cur_node = queue.pop(0)
print(cur_node.elem, end = " ")
if cur_node.lchild is not None:
queue.append(cur_node.lchild)
if cur_node.rchild is not None:
queue.append(cur_node.rchild)
二叉树先序遍历,访问顺序为根-左-右,代码如下:
def non_recu_preorder(self):
#采用非递归方法前序遍历。其主要思想是从根节点开始不断寻找左节点并打印,同时将打印节点入栈,
#直到左节点为空。再弹出栈内元素并判断其是否有右结点,若有,继续第一层循环。
if self.root == None:
#如果树是空的,直接返回空值。
return
stack = []
# 用列表模拟栈
cur_node = self.root
while stack or cur_node is not None:
#注意此处不能采用stack == None 作为终止条件。外层循环控制,当栈里没有元素而且当前节点是空时,循环终止。
#只要栈内有元素或者当前节点不为None,循环继续。
while cur_node is not None:
#当前节点不是空值,直接打印节点。并继续寻找左子节点。直至左子节点为空。
print(cur_node.elem, end = " ")
stack.insert(0, cur_node)
cur_node = cur_node.lchild
while stack and cur_node is None:
#当前节点位于左子节点为空处,栈内弹出上一个节点并判断是否有右结点,若有,跳出本层循环并进入上一层循环,进入左子节点查找环节。
cur_node = stack.pop(0).rchild
二叉树中序遍历,遍历顺序为左-根-右,其实现方法与前序相似,不同的是其访问语句位置放在弹栈之后,其代码实现如下:
def non_recu_midorder(self):
#采用非递归方法前序遍历。其主要思想是从根节点开始不断寻找左节点并打印,同时将打印节点入栈,
#直到左节点为空。再弹出栈内元素并判断其是否有右结点,若有,继续第一层循环。
if self.root == None:
#如果树是空的,直接返回空值。
return
stack = []
# 用列表模拟栈
cur_node = self.root
while stack or cur_node is not None:
while cur_node is not None:
#当前节点不是空值,继续寻找左子节点。直至左子节点为空。
stack.insert(0, cur_node)
cur_node = cur_node.lchild
while stack and cur_node is None:
#当前节点位于左子节点为空处,栈内弹出上一个节点并判断是否有右结点,若有,跳出循环并再次进入左子节点查找。
cur_node = stack.pop(0)
#同前序遍历相比,移动了节点访问的位置,每次打印出栈的元素,实际上每次出栈的元素就是父节点。
print(cur_node.elem, end = " ")
cur_node = cur_node.rchild
二叉树后序遍历方法1,遍历顺序为左-右-根,打印顺序与访问顺序有差别,因此其控制流程比较复杂,一个投机做法是,就是如果知道前序遍历,其次序为根-左-右,很容易写出根-右-左次序的遍历方式,最后对数据倒序输出,遍历顺序就是左-右-根,这种方法只是权宜之计,并没有触及后序遍历的精髓,其代码如下:
def non_recu_postorder1(self):
if self.root == None:
#如果树是空的,直接返回空值。
return
stack = []
res = []
# 用列表模拟栈
cur_node = self.root
while stack or cur_node is not None:
#注意此处不能采用stack == None 作为终止条件。外层循环控制,当栈里没有元素而且当前节点是空时,循环终止。
#只要栈内有元素或者当前节点不为None,循环继续。
while cur_node is not None:
#当前节点不是空值,继续寻找左子节点。直至左子节点为空。
res.append(cur_node.elem)
stack.insert(0, cur_node)
cur_node = cur_node.rchild
while stack and cur_node is None:
#当前节点位于左子节点为空处,栈内弹出上一个节点并判断是否有右结点,若有,跳出循环并再次进入左子节点查找。
cur_node = stack.pop(0).lchild
#倒序打印即可
while res:
print(res.pop(), end = " ")
二叉树后序遍历方法2,采用单栈存储,加以流程控制,首先外层循环条件是栈内元素不为空,或者当前指针不为空,接下来如果指针元素不为空,则通过内层循环找到左子节点的终点。接着判断此时栈元素不为空,弹出栈内元素,如果该元素的右子节点不存在,或者右子节点存在,但上一次访问的节点恰好是右子节点,说明该出栈元素可以打印。否则,指针指向该元素的右子节点,同时将该元素压回栈内。等下次弹出打印。其代码如下:
def non_recu_postorder2(self):
if self.root == None:
#如果树是空的,直接返回空值。
return
stack = []
# 用列表模拟栈
#import pdb
#pdb.set_trace()
cur_node = self.root
pre_node = None
while stack or cur_node is not None:
#注意此处不能采用stack == None 作为终止条件。外层循环控制,当栈里没有元素而且当前节点是空时,循环终止。
#只要栈内有元素或者当前节点不为None,循环继续。
while cur_node is not None:
#当前节点不是空值,直接打印节点。并继续寻找左子节点。直至左子节点为空。
stack.insert(0, cur_node)
cur_node = cur_node.lchild
if stack:#保证栈内有元素,若无,说明已经遍历完毕
cur_node = stack.pop(0)
if cur_node.rchild == None or pre_node == cur_node.rchild:
#如果该元素的右子节点不存在,或者右子节点存在,但上一次访问的节点恰好是右子节点,说明该出栈元素可以打印。
print(cur_node.elem, end = " ")
pre_node = cur_node
cur_node = None #此处十分重要,否则无法跳出内层的while循环,一直执行cur_node = cur_node.lchild。置空后直接
#接收下一个栈的弹出值,从而进入下一次循环。
else:
#该元素右子节点存在,但上一次访问的节点恰好不是右子节点,更新指针,并将弹出的元素再压回栈内,等待下次打印。
stack.insert(0, cur_node)
cur_node = cur_node.rchild
二叉树后序遍历方法3,采用单栈存储,但是将栈内元素节点贴上标签,记录出栈次数,当第一次出栈时不打印并压回栈内,第二次出栈时打印,其代码如下:
def non_recu_postorder3(self):
#构造一个栈列表,栈元素为元组,元组包括节点地址和访问标记,首次入栈的元组,其访问标记均为False。逐个弹出栈元素,当访问标记为Flase,
#不打印并将访问标记修改为True,再压回栈内,以便下次访问时打印。
#if self.root == None:
#如果树是空的,直接返回空值。
# return
stack = []
stack.append((self.root, False))
while stack:
cur, visited = stack.pop()
if visited:
print(cur.elem, end = " ")
else:
stack.append((cur,True))
if cur.rchild:
stack.append((cur.rchild, False))
if cur.lchild:
stack.append((cur.lchild, False))
return