树
定义
树(Tree)(Tree)(Tree):树是n(n≥0)n(n\ge 0)n(n≥0)个结点的有限集。n=0n=0n=0时称为空树。在任意一棵非空树中:(1)有且仅有一个特定的称为根(Root)(Root)(Root)的结点;(2)当n>1n>1n>1时,其余结点可分为m(m>0)m (m>0)m(m>0)个互不相交的有限集T1、T2、……、TmT_1 、T_2、……、T_mT1、T2、……、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)(SubTree)(SubTree)。
Note:1.n>0n>0n>0时根结点是唯一的,不可能存在多个根结点。
2.m>0m>0m>0时,子树的个数没有限制,但它们一定是互不相交的。
度(Degree):结点拥有的子树数称为结点的度(Degree)(Degree)(Degree) 。度为0的结点称为叶结点(Leaf)(Leaf)(Leaf)或终端结点;度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。
节点间的关系:结点的子树的根称为该结点的孩子(Child)(Child)(Child),相应地,该结点称为孩子的双亲(Parent)(Parent)(Parent)。同一个双亲的孩子之间互称兄弟(Sibling)(Sibling)(Sibling) 。结点的祖先是从根到该结点所经分支上的所有结点。反之,以某结点为根的子树中的任一结点都称为该结点的子孙。
结点的层次(LeveI)( LeveI)(LeveI):从根开始定义起,根为第一层, 根的孩子为第二层。若某结
点在第kkk层,则其子树的根就在第k+1k+1k+1层。其双亲在同一层的结点直为堂兄弟。
有序树:如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。
森林(Forest)(Forest)(Forest):是m(m>0)m(m>0)m(m>0)棵互不相交的树的集合。
储存结构
双亲表示法:在每个结点中,除了节点本身的数据,附设一个变量指向其双亲结点到链表中的位置。
datadatadata | parentparentparent |
---|
孩子表示法:把每个结点的孩子结点排列起来,以单链表作存储结构,则nnn个结点有nnn个孩子链表,如果是叶子结点则此单链表为空。然后nnn个头节点又组成一个线性表,采用顺序存储结构,存放进一个列表中。
datadatadata | firstchildfirstchildfirstchild |
---|
childchildchild | nextnextnext |
---|
双亲孩子表示法:相较于孩子表示法,在头节点出新增加一个变量用于指向该节点的双亲结点
datadatadata | parentparentparent | firstchildfirstchildfirstchild |
---|
childchildchild | nextnextnext |
---|
孩子兄弟表示法:任意一棵树, 它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我们设置两个变量,分别指向该结点的第一个孩子和此结点的右兄弟。
datadatadata | firstchildfirstchildfirstchild | rightsibrightsibrightsib |
---|
二叉树
定义
二叉树(Binary Tree)(Binary\ Tree)(Binary Tree):是n(n≥0)n(n\ge 0)n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
性质
1.每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是
只有两棵子树,而是最多有。没有子树或者有一棵子树都是可以的。
2.左子树和右子树是有顺序的,次序不能任意颠倒。
3.即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
特殊二叉树:
1.斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。
2.满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
3.完全二叉树:对一棵具有nnn个结点的二叉树按层序编号,如果编号为i(1≤i≤n)i (1\le i\le n)i(1≤i≤n)的结点与同样深度的满二叉树中编号为iii的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
性质:
1.在二叉树的第iii层上至多有2i−12^{i-1}2i−1个结点(i≥1)(i \ge 1)(i≥1) 。
2.深度为kkk的二叉树至多有2k−12^k-12k−1个结点(k≥1)(k \ge 1)(k≥1)。
3.对任何一棵二叉树TTT,如果其终端结点数为n0n_0n0,度为2的结点数为n2n_2n2,则n0=n2+1n_0=n_2+1n0=n2+1。
4.具有nnn个结点的完全二叉树的深度为⌊log2n⌋+1\lfloor log_2n\rfloor+1⌊log2n⌋+1(⌊x⌋\lfloor x \rfloor⌊x⌋ 表示向下取整)。
5.如果对一棵有nnn个结点的完全二叉树(其深度为⌊log2n⌋+1\lfloor log_2n\rfloor+1⌊log2n⌋+1) 的结点按层序编号(从第1层到第⌊log2n⌋+1\lfloor log_2n\rfloor+1⌊log2n⌋+1层,每层从左到右) ,对任一结点i(1≤i≤n)i(1\le i\le n)i(1≤i≤n)
有:
(1).如果i=1i=1i=1,则结点iii是二叉树的根,无双亲;如果i>1i>1i>1,则其双亲是结点
⌊i2⌋\lfloor \frac{i}{2} \rfloor⌊2i⌋ 。
(2).如果2i>n2i>n2i>n,则结点iii无左孩子(结点iii为叶子结点);否则其左孩子是结点2i2i2i。
(3).如果2i+1>n2i+1>n2i+1>n ,则结点iii无右孩子;否则其右孩子是结点2i+12i+12i+1。
储存结构
二叉树的顺序储存结构(略)
二叉链表:
lchildlchildlchild | datadatadata | rchildrchildrchild |
---|
还可以额外增加一个变量forthforthforth指向双亲结点
代码:
class BinaryTree(object):
def __init__(self,value):
self.value=value
self.lchild=None
self.rchild=None
self.forth=None
def setRoot_left(self,Root):
#设置双亲节点,self为Root的左子节点
if Root.lchild==None:
self.forth=Root
Root.lchild=self
else:
t=Root.lchild
self.lchild=t
Root.lchild=self
self.forth=Root
def setRoot_right(self,Root):
#设置双亲节点,self为Root的右子节点
if Root.rchild==None:
self.forth=Root
Root.rchild=self
else:
t=Root.rchild
self.rchild=t
Root.rchild=self
self.forth=Root
def insertLeft(self,newNode_value):
#插入左子节点
t=BinaryTree(newNode_value)
if self.lchild==None:
self.lchild=t
else:
t.lchild=self.lchild
self.lchild=t
t.forth=self
def insertRight(self,newNode_value):
#插入右子节点
t=BinaryTree(newNode_value)
if self.rchild==None:
self.rchild=t
else:
t.rchild=self.rchild
self.rchild=t
t.forth=self
遍历
Note:己知前序遍历序列和中序遍历序列,可以唯一确定一棵二叉树。已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树。已知前序和后序遍历,是不能确定一棵二叉树的。
二叉树的遍历(traversingbinarytree)(traversing binary tree)(traversingbinarytree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点.使得每个结点被访问一次且仅被访问一次。
前序遍历
规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树, 再前序遍历右子树。
代码
def PreOrderTraverse(T):
if T==None:
return -1
print(T.value)
PreOrderTraverse(T.lchild)
PreOrderTraverse(T.rchild)
中序遍历
规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点) ,中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
代码
def InOrderTraverse(T):
if T==None:
return -1
InOrderTraverse(T.lchild)
print(T.value)
InOrderTraverse(T.rchild)
后序遍历
规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。
代码
def PostOrderTraverse(T):
if T==None:
return -1
PostOrderTraverse(T.lchild)
PostOrderTraverse(T.rchild)
print(T.value)
层序遍历
规则是若树为空, 则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中, 按从左到右的颇用才结点逐个访问。
二叉树的建立
扩展二叉树:为了能让每个结点确认是否有左右孩子,我们对它进行了扩展。也就是将二叉树中每个结点的空指针引出一个虚结点,其值为一特定值,比如"#" 。
上图的前序遍历序列就为ABABAB#DDD##CCC##。
代码:
Note:利用在BinaryTreeBinaryTreeBinaryTree中设置的变量forthforthforth来执行叶子节点赋值为NoneNoneNone的操作。
def CreateBiTree_pre(List,root,key=1):
#利用前序序列创建二叉树
if len(List)==0:return None
else:
a=List.pop(0)
if a=='#':
if key==1:
root.forth.rchild=None
elif key==-1:
root.forth.lchild=None
else:
root.value=a
root.insertLeft(None)
root.insertRight(None)
CreateBiTree_pre(List,root.lchild,-1)
CreateBiTree_pre(List,root.rchild,1)
关于扩展二叉树的完整代码
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 16 14:47:33 2019
@author: Administrator
"""
class BinaryTree(object):
def __init__(self,value):
self.value=value
self.lchild=None
self.rchild=None
self.forth=None
def setRoot_left(self,Root):
#设置双亲节点,self为Root的左子节点
if Root.lchild==None:
self.forth=Root
Root.lchild=self
else:
t=Root.lchild
self.lchild=t
Root.lchild=self
self.forth=Root
def setRoot_right(self,Root):
#设置双亲节点,self为Root的右子节点
if Root.rchild==None:
self.forth=Root
Root.rchild=self
else:
t=Root.rchild
self.rchild=t
Root.rchild=self
self.forth=Root
def insertLeft(self,newNode_value):
#插入左子节点
t=BinaryTree(newNode_value)
if self.lchild==None:
self.lchild=t
else:
t.lchild=self.lchild
self.lchild=t
t.forth=self
def insertRight(self,newNode_value):
#插入右子节点
t=BinaryTree(newNode_value)
if self.rchild==None:
self.rchild=t
else:
t.rchild=self.rchild
self.rchild=t
t.forth=self
def CreateBiTree_pre(List,root,key=1):
#利用前序序列创建二叉树
if len(List)==0:return None
else:
a=List.pop(0)
if a=='#':
if key==1:
root.forth.rchild=None
elif key==-1:
root.forth.lchild=None
else:
root.value=a
root.insertLeft(None)
root.insertRight(None)
CreateBiTree_pre(List,root.lchild,-1)
CreateBiTree_pre(List,root.rchild,1)
def PreOrderTraverse_Expend(T,Str=[]):
#创建与扩展二叉树相关的前序序列,用于存储二叉树
if T==None:
print('#')
Str.extend('#')
return Str
else:
print(T.value)
Str.append(T.value)
PreOrderTraverse_Expend(T.lchild)
PreOrderTraverse_Expend(T.rchild)
return Str
head=BinaryTree(None)
head.insertRight(None)
root=head.rchild
List=list('AB#D##C##')
CreateBiTree_pre(List,root)
Str=PreOrderTraverse_Expend(root)
线索二叉树
线索二叉树:对于nnn个结点的二叉树,在二叉链存储结构中有n+1n+1n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的变量,这些变量称为线索(clue)(clue)(clue),加上线索的二叉树称为线索二叉树。
详细解释:对于二叉树,因为存在变量空置的现象,因此想要考虑利用好空置的变量来指向当前节点TTT的前驱与后置。但是有个问题就是如果只是单纯的用空置变量再指向前驱与后置,会导致无法分清当前节点TTT的变量lchildlchildlchild与rchildrchildrchild具体是指向当前节点的前驱与后置还是左右孩子,因此需要在TTT中新增设两个变量ltagltagltag与rtagrtagrtag来分别记录lchildlchildlchild与rchildrchildrchild的具体指向。
节点结构:
lchildlchildlchild | ltagltagltag | datadatadata | rtagrtagrtag | rchildrchildrchild |
---|
ltagltagltag为0时指向该结点的左孩子,为1时指向该结点的前驱。
rtagrtagrtag为0时指向该结点的右孩子,为1时指向该结点的后继。
关于中序遍历的线索二叉树代码
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 17 15:06:42 2019
@author: Administrator
"""
class BinaryTree(object):
def __init__(self,value):
self.value=value
self.lchild=None
self.rchild=None
self.ltag=None
self.rtag=None
def insertLeft(self,newNode_value):
#插入左子节点
t=BinaryTree(newNode_value)
if self.lchild==None:
self.lchild=t
else:
t.lchild=self.lchild
self.lchild=t
def insertRight(self,newNode_value):
#插入右子节点
t=BinaryTree(newNode_value)
if self.rchild==None:
self.rchild=t
else:
t.rchild=self.rchild
self.rchild=t
pre=None
def InThreading(root):
global pre
if root:
InThreading(root.lchild)
#设pre为相对root前一个访问的节点,故对于节点root的操作如下:
#若pre没有右孩子,则pre.rchild指向root
#若root没有左孩子,则root.lchild指向pre
if root.lchild:
root.ltag=0
else:
root.ltag=1
root.lchild=pre
if pre and pre.rchild:
pre.rtag=0
elif pre and pre.rchild==None:
pre.rtag=1
pre.rchild=root
pre=root
InThreading(root.rchild)
def InOrderTraverse_Thr(root): #中序线索二叉树的遍历
while(root):
while root.ltag==0:
#当p.ltag==0时,循环到中序序列的第一个节点
root=root.lchild
print(root.value)
#显示节点数据
while root.rtag==1 and root.rchild :
root=root.rchild
print(root.value)
root=root.rchild
return 'OK'
#一棵树:
root=BinaryTree('A')
root.insertLeft('B')
root.insertRight('C')
root.lchild.insertLeft('D')
root.lchild.insertRight('E')
root.lchild.lchild.insertLeft('H')
root.lchild.lchild.insertRight('I')
root.rchild.insertLeft('F')
root.rchild.insertRight('G')
InThreading(root)
InOrderTraverse_Thr(root)
树、森林与二叉树的转换
树转化为二叉树
步骤:
1.加线。在所有兄弟结点之间加一条连线。
2.去线。对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线。
3.层次调整。以树的根结点为轴心,将整棵树顺时针旋转一定的角度,使之结构
层次分明。注意第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结
点的右孩子。
树转化为二叉树
步骤:
1.把每个树转换为二叉树。
2.第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为
前一棵二叉树的根结点的右孩子,用线连接起来。当所有的二叉树连接起来后
就得到了由森林转换来的二叉树。
二叉树转化为树
**步骤:**即为树转为二叉树步骤的逆。
1.加线。若某结点的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点……左孩子的nnn个右孩子结点都作为此结点的孩子。将该结点与这些右孩子结点用线连接起来。
2.去线。删除原二叉树中所有结点与其右孩子结点的连线。
3.层次调整。使之结构层次分明。
二叉树转化为森林
步骤:
1.从根结点开始, 若右孩子存在,则把与右孩子结点的连线删除,再查看分离后
的二叉树,若右孩子存在,则连续删除……,直到所有右孩子连线都删除为止,得到分离的二叉树。
2.再将每棵分离后的二叉树转换为树即可。
树与森林的遍历
树的遍历:
1.先根遍历:先访问树的根结点,然后依次先根遍历根的每棵子树。
2.后根遍历:先依次后根遍历每棵子树,然后再访问根结点。
森林的遍历:
1.前序遍历: 先访问森林中第一棵树的根结点,然后再依次先根遍历根的每棵子树,再依次用同样方式遍历除去第一棵树的剩余树构成的森林。
2.后序遍历: 先访问森林中第一棵树,后根遍历的方式遍历每棵子树,然后再访问根结点,再依次同样方式遍历除去第一棵树的剩余树构成的森林。
哈夫曼树
从树中一个结点到另一个结点之间的分支构成两个结点之间的路径,路径上的分支数目称做路径长度。树的路径长度就是从树根到每一结点的路径长度之和。如果考虑到带权的结点,结点的带权的路径长度为从该结点到树根之间的路径长度与结点上权的乘积。
哈夫曼树:带权路径长度(WPL)(WPL)(WPL)最小的二叉树称做哈夫曼树。
哈夫曼树(Huffman)(Huffman)(Huffman)算法
1.根据给定的n个权值{w1,w2,…,wn}\{w_1,w_2,…,w_n\}{w1,w2,…,wn}构成nnn棵二叉树的集合F={T1,T2,…Tn}F=\{T_1,T_2,…T_n\}F={T1,T2,…Tn},其中每棵二叉树TiT_iTi中只有一个带权为wiw_iwi根结点,其左右子树均为空。
2.在FFF中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且
置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
3.在FFF中删除这两棵树,同时将新得到的二叉树加入FFF中。
4.重复2和3步骤,直到FFF只含一棵树为止。这棵树便是哈夫曼树。
Huffman TreeHuffman\ TreeHuffman Tree代码
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 6 09:30:06 2019
@author: Administrator
"""
class BinaryTree(object):
def __init__(self,value,weight=1):
self.value=value
self.weight=weight
self.lchild=None
self.rchild=None
def setLeft(self,new_lchild):
#leftChild指向N
self.lchild=new_lchild
def setRight(self,new_rchild):
#rightChild指向N
self.rchild=new_rchild
def getRight(self):
#获取右子节点
return self.rchild
def getLeft(self):
#获取左子节点
return self.leftChild
def setValue(self,new_value):
#设置根上的值
self.value=new_value
def getValue(self):
#获取根上的值
return self.value
def setWeight(self,new_weight):
self.weight=new_weight
def getWeight(self):
return self.weight
def Huffman_Tree(T):
if len(T)>1:
T.sort(key=lambda v:v.weight)
N=BinaryTree(T[0].value+T[1].value,T[0].weight+T[1].weight)
N.setLeft(T[0])
N.setRight(T[1])
T.append(N)
T.pop(0)
T.pop(0)
Huffman_Tree(T)
def create_Huffman_Tree(List): #[[v1,w1],[v2,w2],[v3,w3],...]
T=[BinaryTree(v[0],v[1]) for v in List]
Huffman_Tree(T)
return T[0]
List=[['A',5],['B',15],['C',40],['D',30],['E',10]]
T=create_Huffman_Tree(List)
哈夫曼编码
代码
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 6 16:27:17 2019
@author: Administrator
"""
class BinaryTree(object):
def __init__(self,value,weight=1):
self.value=value
self.weight=weight
self.lchild=None
self.rchild=None
def setLeft(self,new_lchild):
self.lchild=new_lchild
def setRight(self,new_rchild):
self.rchild=new_rchild
def Huffman_Tree(T): #通过数个叶子节点构成的树根列表创建huffman树
if len(T)>1:
T.sort(key=lambda v:v.weight)
N=BinaryTree(T[0].value+T[1].value,T[0].weight+T[1].weight)
N.setLeft(T[0])
N.setRight(T[1])
T.append(N)
T.pop(0)
T.pop(0)
Huffman_Tree(T)
def create_Huffman_Tree(List): #整合,通过列表创建huffman树
T=[BinaryTree(v[0],v[1]) for v in List]
Huffman_Tree(T)
return T[0]
def Dic_Huffman(T,A=[],a=[]): #由huffman树生成编码字典,储存于列表中
if T.rchild==None and T:
a.append(T.value)
A.append(a)
return A
elif T.lchild!=None and T:
A=Dic_Huffman(T.lchild,A[:],a+['0'])
A=Dic_Huffman(T.rchild,A[:],a+['1'])
return A
def Huffman_Encode(Str): #huffman编码
dic_Str={} #用于统计字符出现频率
dic_encode={} #加密字典
dic_decode={} #解密字典
str_code=[] #密文
for x in Str: #统计字符出现频率
if x not in dic_Str:
dic_Str[x]=1
else:
dic_Str[x]+=1
List=[(v,dic_Str[v]) for v in dic_Str]
T=create_Huffman_Tree(List)
A=Dic_Huffman(T) #得到编码列表
print(A)
for x in A: #生成加密字典
dic_encode[x[-1]]=''.join(x[0:-1])
for x in A: #生成解密字典
dic_decode[''.join(x[0:-1])]=x[-1]
for x in Str: #加密
str_code.append(dic_encode[x])
return ''.join(str_code),dic_decode
def Huffman_Decode(Nums,dic_decode):
a=[]
Str=[]
Nums=list(Nums)
while Nums:
a.append(Nums.pop(0))
if ''.join(a) in dic_decode:
Str.append(dic_decode[''.join(a)])
a=[]
return Str