python 数据结构四 之 二叉树和树
来源:互联网 发布:跳跃网络的游戏 编辑:程序博客网 时间:2024/05/16 00:54
python数据结构教程第四课
树形结构是复杂结构中最简单的一类,这是一类非常重要的结构,在实际中使用广泛,反映了许多计算过程的抽象结构
一、简介
1.树
2.二叉树
二、二叉树和树的抽象数据类型(ADT)
三、二叉树的python实现
1.二叉树的list实现
2.二叉树的遍历操作
3.二叉树的链表实现
四、二叉树的应用——Huffman Tree
五、树的python实现
一、简介
二叉树是树的子集,具有树的全部特性,这里先介绍树的定义与基本特性
1.树
定义:一棵树是n(n>=0)个结点的有限集T,当T非空时满足:
1)T中有且仅有一个特殊结点r称为树T的根
2)除根结点外的其余结点分为m(m>=0)个互不相交的非空有限子集,每个集合为一棵非空树,称为r的子树
结点个数为0的树称为空树
树的特性:
1)如果树的结构不空,其中就存在着唯一的起始结点,称为树根
2)按结构的连接关系,树根外的其余结点都有且只有一个前驱,但另一方面,一个结点可以有0个或者多个后继。在非空的树结构中一定有一些结点并不连接到其他结点,与尾结点相似
3)从树根结点出发,经过若干次后继关系可以到达结构中的任意一点
4)结点之间的联系不会形成循环关系
5)从这个结构里的任意两个不同结点出发,通过后继关系可达的两个结点集合,或者互不相交,或者一个是另一个的子集
2.二叉树
定义:二叉树是结点的有穷集合。这个集合或者是空集,或者其中有一个称为根结点的特殊结点,其余结点分属两棵不相交的二叉树,这两棵二叉树分别是原二叉树的左子树和右子树
关于二叉树的一些基本概念:
1)一棵二叉树的根结点称为该树的子树根结点的父结点;与此对应,子树的根结点称为二叉树树根结点的子结点
2)从父结点到子结点有一条连线,称为从父结点到子结点的边,父结点的两个结点互为兄弟结点
3)在二叉树里有些结点的两棵子树都空,没有子结点,这种结点称为树叶,树中其余结点称为分支结点
4)一个结点的子结点个数称为该结点的度数。显然,二叉树中树叶结点的度数为0,分支结点的度数可以是1或2
二、二叉树和树的抽象数据类型(ADT)
二叉树的基本操作包括创建空树,设置左右子树等,其基本ADT如下:
ADT BTree: BTree(self, data, left,right) #构造空树 is_empty(self) #空树判断 num_nodes(self) #返回结点树 data(self) #返回树根数据 left(self) #返回左子树 right(self) #返回右子树 set_left(self) #设置左子树 set_right() #设置右子树 traversal(self) #迭代器 forall(self,op) #op的遍历操作
树的抽象数据类型与二叉树类似,不过在具体实现时,实现策略会有所不同,这里先给出ADT:
ADT Tree: Tree(self, data,forest) #树的初始化 is_empty(self) #空树判断 num_nodes(self) #返回结点数据 data(self) #返回树根中的数据 first_child(self,node) #取得node的第一棵子树 children(self,node) #结点node子树的迭代器 set_first(self,tree) #用tree取代第一棵子树 insert_child(self, i,tree) #设置第i棵子树 traversal(self) #所有结点的迭代器 forall(self,op) #op的遍历操作
本文会着重介绍二叉树的实现方法和操作,由于篇幅问题对于树的实现只做简单的策略介绍,感兴趣的同学可以等待我以后另开博客讲解
三、二叉树的python实现
1.二叉树的list实现
思考可以发现,二叉树的一个结点其实就是一个三元组,分别存储着数据和左右子树,因此这里给出一个简单的list二叉树实现方式(也可以用tuple实现,区别只是在于list可以改变)
采用嵌套括号的形式,可以将二叉树表示为:
['A',['B',None,None], ['C',['D',['F',None,None], ['G',None,None]], ['E',['I',None,None], ['H',None,None]]]]
由上,我们来定义一组函数,描述二叉树的基本构造与方法,其具体的类实现,会在介绍了二叉树的遍历之后,使用链表方法实现
def BTree(data,left=None,right = None): return [data,left,right]def is_empt_BTree(btree): return btree is Nonedef root(btree): return btree[0]def left(btree): return btree[1]def right(btree): return btree[2]def set_root(btree,data): btree[0] = datadef set_left(btree,data): btree[1] = datadef set_right(btree,data): btree[2] = data
定义一个二叉树的源码为:
t1 = BTree(2,BTree(4),BTree(8))
2.二叉树的遍历
二叉树的结构比较复杂,因此系统化遍历有许多种可能的方式,以根为起点,存在两种基本方式:
1)深度优先遍历,顺着一条路径尽可能向前搜索,必要时回朔。对于二叉树,最基本的回朔情况是检查完一个结点,由于无路可走便回头
2)宽度优先遍历,在所有路径上齐头并进
按深度优先遍历需要做三件事情:遍历左子树、遍历右子树和访问根结点,选择这三项工作不同的执行顺序,就可以得到三种常见的遍历顺序:先根序遍历、中根序遍历、后根序遍历;对二叉树做宽度优先遍历,就是按二叉树的层次逐层访问树中的各结点,常见的是在每一层从左往右逐个访问
下面给出先根序遍历二叉树的递归函数:
#先给出二叉树结点类的定义class BNode: def __init__(self,data,left = None,right = None): self._data = data self._left = left self._right = right def data(self): if self is None: return None else: return self._data def left(self): if self is None: return None else: return self._left def right(self): if self is None: return None else: return self._right def set_data(self,data): self._data = data def set_left(self,left): self._left = left def set_right(self,right): self._right = right def __eq__(self,another): if self is None: return another == None if another is None: return self is None else: return self._data == another.data() and self._left == another.left() and self._right == another.right()#递归遍历,先根序,proc是对结点中元素的操作def preorder(t,proc): if t is None: return proc(t.data()) preorder(t.left()) preorder(t.right())
测试函数
#使用了上述递归算法思想的二叉树输出函数def print_BNode(t): if t is None: print('^',end = '') return print('(' + str(t.data()),end = '') print_BNode(t.left()) print_BNode(t.right()) print(')',end='')t = BNode(1,BNode(2,BNode(5)),BNode(3))print_BNode(t)
结果
(1(2(5^^)^)(3^^))
下面给出宽度优先遍历的二叉树遍历函数:
def levelorder(t,proc): qu = SQueue() qu.enqueue(t) while not qu.is_empty(): t = qu.dequeue() if t is None: #弹出的树为空则直接跳过 continue qu.enqueue(t.left()) qu.enqueue(t.right()) proc(t.data())
当我们使用非递归算法实现二叉树的遍历时,算法的过程和结构会更加清晰,这里仅给出先根序遍历的非递归算法
def preorder_nonrec(t,proc): s = SStack() while t is not None or not s.is_empty(): while t is not None: proc(t.data()) s.push(t.right()) t = t.left() t = s.pop()
3.二叉树的链表实现
在熟悉了二叉树的遍历算法之后,我们便可以较为完整利用链表实现二叉树类,二叉树的结点继续使用前面的结点类,没有任何问题
#二叉树的基本链表实现类定义class BTree: def __init__(self,data,left = None,right = None): if data == None: self._root = None self._num = 0 else: self._root = BNode(data,left,right) self._num = 1 #空链表判断 def is_empty(self): return self._num == 0 #获得根结点中的数据 def root(self): if self.is_empty(): return None else: return self._root.data() #获得根结点的左子树 def left(self): if self.is_empty(): return None else: return self._root.left() #获得根结点的右子树 def right(self): if self.is_empty(): return None else: return self._root.right() #设置根结点的根 def set_root(self,root): self._root = root #设置根结点的左子树 def set_left(self,left): self._root.set_left(left) #设置根结点的右子树 def set_right(self,right): self.set_right(right) #先根序遍历方法 def preorder_elements(self): t,s = self._root,SStack() while t is not None or not s.is_empty(): while t is not None: s.push(t.right()) yield t.data() t = t.left() t = s.pop() #层次序遍历方法 def levelorder(self): qu = SQueue() qu.enqueue(self._root) while not qu.is_empty(): t = qu.dequeue() if t is None: continue qu.enqueue(t.left()) qu.enqueue(t.right()) yield t.data() #两棵树的结构数据都相等时,两棵树相等 def __eq__(self,another): def preorder_node(Tree): t,s = Tree._root,SStack() while t is not None or not s.is_empty(): while t is not None: s.push(t.right()) yield t t = t.left() t = s.pop() lt1 = [] lt2 = [] for i in preorder_node(self): lt1.append(i) for i in preorder_node(another): lt2.append(i) if len(lt1) != len(lt2): return False i = 0 while i < len(lt1): if lt1[i] != lt2[i]: return False return True #返回克隆树 def clone(self): return copy.deepcopy(self) #分别返回根结点和分支结点的数目 def count_nodes(self): def preorder_node(Tree): t,s = Tree._root,SStack() while t is not None or not s.is_empty(): while t is not None: s.push(t.right()) yield t t = t.left() t = s.pop() count_leaf = 0 count_nleaf = 0 for i in preorder_node(self): if i.left() == None and i.right() == None: count_leaf += 1 else: count_nleaf += 1 return count_leaf,count_nleaf #输出树的所有结点 def printall(self): for i in self.preorder_elements(): print(i,end = ' ')
测试代码
T = BTree(10,BNode(3,BNode(6)),BNode(5))for i in T.levelorder(): print(i,end = ' ')T2 = BTree(10,BNode(4,BNode(6)),BNode(5))print()print(T == T2)print(T.count_nodes())
结果
10 3 5 6 False(2, 2)
四、二叉树的应用——Huffman Tree
Huffman Tree(哈夫曼树),哈夫曼树是信息领域重要的算法,这里首先给出哈夫曼树的定义:
设有实数集W={w0,w1,…,wm-1},T是一棵扩充二叉树,其m个外部结点分别以wi为权,而且T的带权外部路径长度WPL在所有这样的扩充二叉树中达到最小,则称T为数据集W的最优二叉树或者哈夫曼树
哈夫曼提出了一种算法,可以从任意的实数集中构造出与之对应的哈夫曼树,这个构造算法如下:
1)算法的输入为实数集W={w0,w1,…wm-1}
2)在构造中维护一个包含K棵二叉树的集合F,开始时k=m且F={T0,T1,…,Tm-1},其中每个Ti是一棵只包含权为wi的根结点的单点二叉树
3)在算法的过程中重复执行下面两个步骤,直到集合F中剩下一棵树为止:
构造一棵新二叉树,其左右子树是从集合F中选取的两棵权最小的二叉树,其根结点的权值设置为这两棵子树的根结点的权值之和;
将所选的两棵二叉树从F中删除,把新构造的二叉树加入F
下面给出哈夫曼树的实现算法:
#哈夫曼树结点类class HTNode(BNode): def __lt__(self,othernode): return self.data() < other.data()#哈夫曼树中需要的优先队列class HuffmanPrioQ(PrioQueue): def number(self): return len(self._elems)#哈夫曼树构造算法def HuffmanTree(weights): trees = HuffmanPrioQ() for w in weights: trees.enqueue(HTNode(w)) while trees.number() > 1: t1 = trees.dequeue() t2 = trees.dequeue() x = t1.data() + t2.data() trees.enqueue(HTNode(x,t1,t2)) return trees.dequeue()
五、树的python实现
树的结构相对于二叉树来比较复杂,其实现方式也多种多样,比如子结点引用表示法、子结点表表示法、长子-兄弟表示法等,这里仅介绍一种较为简单的子结点引用法
树的最基本表示方法是子结点引用法,其基本设计与二叉树的链接表示法类似:用一个数据单元表示结点,通过结点间的链接表示树结构,通过结点间的链接表示树结构。但这里有一个麻烦:树的结点度数不确定,而且结点的度数差可能很大,在这种情况下,一种简单的考虑是只支持度数不超过固定m的树,也就是说,树中分支结点至多允许m棵子树,其具体的类实现方法与二叉树的类实现方法类似,这里不给出具体的源代码
- python 数据结构四 之 二叉树和树
- Python数据结构之二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- SDUT3343数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- SDUT3343数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- 数据结构实验之二叉树四:还原二叉树
- nw.js:编写桌面应用
- 【转载】SAP ABAP ALV(LVC)的一个自定义事件(F4帮助事件,回车ENTER按钮事件)的一个实例
- 【割点 && dfs】UVALive
- VB中九九乘法表的实现
- [日推荐] 『雅思口语自练狂』雅思考试神助攻!
- python 数据结构四 之 二叉树和树
- 指针三
- 2D or 3D projects
- jquery-文档操作
- python:get,post-urllib urllib2
- mysql在插入中文和空格时 text数据类型报mysql incorrect string value .....错误!
- 二叉树的非递归【前/中/后 序遍历】
- QT 出现错误 QT 运行cc1plus.exe Error while building/deploying project cartest
- Java 拼接字符的两个方法