树
定义:树是**n(n>=0)**个结点的有限集。n=0时称为空树。
注意
1.n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点。
2.m>0时,子树的个数没有限制,但它们一定是互不相交的。
非空:
(1)有且仅有一个特定的称为根(Root)的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、…、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。
概念:
结点的层次从根开始定义起,根为第一层,根的孩子为第二层。若某结点在第n层,则其子树的根就在第n+1层。其双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度或高度。如果将树种结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。森林是m(m>=0)课互不相交的树的集合。
结点之间的关系:
结点的子树的跟称为该结点的孩子,相应地,该结点称为孩子的双亲。同一个双亲的孩子之间互称兄弟,结点的祖先是从根到该结点所经分支上的所有结点。
基本操作
- 查找类
- 插入类
- 删除类
查找类
Root(T) 求树的根结点
Value(T,cur_e) 当前结点的元素值
Parent(T,cur_e) 求当前结点的双亲结点
LeftChild(T,cur_e) 求当前结点的最左孩子
RightSibling(T,cur_e) 求当前结点的右兄弟
TreeEmpty(T) 判断树是否为空树
TreeDepth(T) 求树的深度
TraverseTree(T,Visit()) 遍历
插入类
InitTree(&T) 初始化置空树
CreateTree(&T,definition) 按定义构造树
Assign(T,cur_e,vaule) 给当前结点赋值
InsertChild(&T,&p,i,c) 将以c为根的树插入为结点p的第i个子树
删除类
ChearTree(&T)将树清空
DestroyTree(&T)销毁树的结构
DeleteTree(&T,&p,i)删除结点p的第i个子树
二叉树
定义:
二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。(在某个阶段都是两种结果的情形)
性质
1.性质1:在二叉树的第i层上至多有2∧i-1个结点(i>=1)。
2.性质2:深度为k的二叉树至多有2∧k -1个结点(k>=1)。
3.性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
4.性质4:具有n个结点的完全二叉树的深度为[log2n]+1 ([x]表示不大于x的最大整数。
5.性质5:若对一棵有n个结点的完全二叉树(其深度为[log2n]+1) 的结点按层序编号(从第1层到[log2n]+1层,每层从左到右),对任一节点i(1≦i≦n)有:
若i=1,则结点i是二叉树的根,无双亲;
若i>1, 则其双亲是结点[i/2]。
若2i>n, 则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i。
若2i+1>n, 则结点i无右孩子;否则其右孩子是结点2i+1。
特点:
每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。左子树和右子树是有顺序的,次序不能任意颠倒。即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
存储结构
1.二叉树的顺序存储结构:二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现结点之间的逻辑关系。*顺序存储结构一般只用于完全二叉树。2.二叉链表(链式存储结构)二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。
**二叉树的遍历:**是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点呗访问一次且仅被访问一次。
1.前序遍历:规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。
2.中序遍历:规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
3.后序遍历:规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。
前序遍历算法:
/二叉树的前序遍历递归算法/
void PreOrder(BiTree T)
{
if(T){
visit(T>=data)
访问结点
PreOrder(T->lchild);
再先序遍历左子树
PreOrder(T->rchild);
最后先序遍历右子树
}
}
中序与后序类似
==二叉树的建立:建立二叉树,也是利用了递归的原理。只不过在原来应该是打印结点的地方,改成了生成结点,给结点赋值的操作而已。
void CreateBiTree(BiTree *T)
{
TElemTypech;scanf("%c",&ch);if(ch’#’)*T=NULL;
else{*T(BiTree)malloc(sizeof(BiTNode));
if(!*T){
exit(OVERFLOW);
(*T)->data=ch; 生成根结点CreateBiTree(&(*T)->lchild); 构造左子树CreateBiTree(&(*T)->rchild); 构造右子树}
}
线索二叉树
对于一个有n个结点的儿茶链表,每个结点有指向左右孩子的两个指针域,所以一共是2n个指针域。而n个结点的二叉树一共有n-1条分支线数,也就是说,其实是存在2n-1(n-1)=n+1个空指针域。
线索二叉树:指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。
线索二叉树,等于是把一棵二叉树转变成了一个双向链表。
对二叉树以某种次序遍历使其变为线索二叉树的过程称作是线索化。
森林转换为二叉树
1.把每个树转换为二叉树。
2.第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。
二叉树转换为树
1.加线。若某结点的右孩子存在,则将做左孩子的n各右孩子结点都作为此结点的孩子。将该结点与这些右孩子结点用线连接起来。
2.去线。删除原二叉树中所有结点与其右孩子结点的连线。
3.层次调整。使之结构层次分明。
树与森林的遍历
树的遍历分为两种方式
1.一种是先根遍历树,即先访问树的根结点,然后依次先根遍历根的每棵子树。
2.另一种是后跟遍历,即先依次后根遍历每棵子树,然后再访问根结点。
森林的遍历也分为两种方式:
1.前序遍历:先访问森林中第一棵树的根结点,然后再依次县根遍历根的每棵子树,再依次用同样方式遍历除去第一棵树的剩余树构成的森林。
2.后序遍历:是先访问森林中第一棵树,后跟遍历的方式遍历每棵子树,然后再访问根结点,再依次同样方式遍历除去第一棵树的剩余树构成的森林。
赫夫曼树及其应用
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。其中带权路径长度WPL最小的二叉树称作赫夫曼树。