数据结构:二叉树
来源:互联网 发布:网络教学管理平台 编辑:程序博客网 时间:2024/06/09 23:50
1. 树的定义与抽象数据类型
(1) 树的定义
树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一棵非空树中:
- 有且仅有一个特定的称为根(root)的结点;
- 当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、……、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(subTree)。
需要注意的是:
- n>0时根结点是唯一的,不可能存在多个根结点,别和现实中的大树混为一起,现实中的树有很多根须,那是真实的树,数据结构中的树是只能有一个根结点;
- m>0时,子树的个数是没有限制,但它们一定不相交。
(2) 树的抽象数据类型
ADT 树(Tree)
Data
树是由一个根结点和若干棵子树组成。树中结点具有相同数据类型及层次关系。
Operation
initTree(&T):构造空树T。
destoryTree(&T):销毁树T。
createTree(&T, definition):按definition中给出树的定义来构造树。
clearTree(&T):若树T存在,则将树T清为空树。
treeEmpty(T):若T为空树,返回true,否则返回false。
treeDeep(T):返回T的深度。
root(T):返回T的根结点。
value(T, cur_e):cur_e是树T中的一个结点,返回此结点的值。
assign(T, cur_e, value):给树T的结点cur_e赋值为value。
parent(T, cur_e):若cur_e是树T的非根结点,则返回它的双亲,否则返回空。
leftChild(T, cur_e):若cur_e是树T的非叶子结点,则返回它的最左孩子,否则返回空。
rightSibling(T, cur_e):若cur_e有右兄弟,则返回它的右兄弟,否则返回空。
insertChild(&T, &p, i, c):其中p指向树T的某个结点,i为所指结点p的度加上1,非空树c与T不相交,操作结果为插入c为树T中p指结点的第i棵子树。
deleteChild(&T, &p, i):其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指结点的第i棵子树。
endADT
(3) 树的存储结构
双亲表示法
struct NodeType{
ElemType data ; //该结点的数据
int Parent; //该结点的父亲的数组下标,对于根结点该域为0
}Node;
struct TreeType
int r,n; //根的位置 树的结点的总数目
NodeType Node[MaxNodeCount]; //存储树的结点
}
双亲孩子表示法
去掉parent数据域便是孩子表示法
孩子兄弟表示法
2. 二叉树的定义及性质
(1) 二叉树的定义
二叉树(Binary Tree)是n(n >= 0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
特点:
- 每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是两棵子树,而是最多有。没有子树或者只有一棵子树都是可以有。
- 左子树和右子树是有顺序的,次序不能颠倒。
- 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
二叉树的五种基本形态:
- 空二叉树;
- 只有一个根结点;
- 根结点只有左子树;
- 根结点只有右子树;
- 根结点既有左子树,又有右子树。
(2) 一些特殊的二叉树
斜树:
顾名思义,斜树一定要是斜的,但是往哪斜还是有讲究的。所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。
满二叉树:
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有的叶子都在同一层上,这样的二叉树称为满二叉树。
单是每个结点都存在左右子树,不能算是满二叉树,还必须要所有的叶子都在同一层上,这就做到了整棵树的平衡。因此,满二叉树的特点有:
- 叶子只能出现在最下一层;
- 非叶子结点的度一定是2;
- 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
完全二叉树:
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1 <= i <= n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
完全二叉树的特点:
- 叶子结点只能出现在最下两层;
- 最下层的叶子一定集中在左部连续位置;
- 倒数第二层,若有叶子结点,一定都在右部连续位置;
- 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况;
- 同样结点数的二叉树,完全二叉树的深度最小。
(3) 二叉树的性质
性质1:在二叉树的第i层至多有2i-1个结点(i >= 1)。
满二叉树每一层结点数最多,可以用归纳法得到结论。
性质2:深度为k的二叉树至多有2k-1个结点(k >= 1)。
由性质1可知:第1层最多1个结点,第2层最多2个结点,第3层最多3个结点,……,第k层至多2k-1个结点;
则一共最多(1 + 2 + 4 + …… + 2k-1) = 2k-1 个结点。
性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
终端结点数其实就是叶子结点数,而一棵二叉树,除了叶子结点外,剩下的就是度为1或度为2的结点数了,我们设n1为度为1的结点数。则树T结点总数为n=n0+n1+n2。
则二叉树中总的入度数为n-1,因为除根结点外,每个结点入度均为1。
二叉树中总的出度为n1+2n2,度为0的结点没有出度。
显然二叉树中有 入度=出度,即n1+2n2=n-1=n0+n1+n2-1,则n0=n2+1。
性质4:具有n个结点的完全二叉树的深度为[log2n]+1([x]表示不大于x的最大整数)。
它的总结点数一定少于等于同样深度的满二叉树的结点数2k-1,但一定多于2k-1-1。
即2k-1-1 < n <= 2k-1。
也即2k-1 <= n < 2k
两边取对数,可得到k-1 <= log2n < k
而k作为深度数也是整数,因此,k=[log2n]+1。
性质5:如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),对任一结点i(1<=i<=n)有:
1. 如果i=1,则结点i是二叉树的根,无双新;如果i>1,则其双新结点为[i/2];
2. 如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子结点是2i;
3. 如果2i+1>n,是结点i无右孩子;否则其右孩子为2i+1。
(4) 二叉树的存储结构
二叉树的顺序存储结构
二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现结点之间的关系,比如双亲与孩子的关系,左右兄弟的关系等。对于完全二叉树而言,层序编号即可反应逻辑关系。
对于一般的二叉树,尽管层序编号不能反映逻辑关系,但可以将其按完全二叉树编号,只不过,把不存在的结点设置为空而已。
考虑一种极端的情况,一棵深度为k的右斜树,它只有k个结点,却需要分配2k-1个存储单元空间,这显然是对存储空间的浪费,所以,顺序存储结构一般只用于完全二叉树。
二叉链表
二叉树的每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。
以下是我们的二叉链表的结点结构定义代码:
typedef struct BiTNode{ TElemType data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree;
如果需要,还可以再增加一个指向其双亲的指针域,那样就称为三叉链表。
具体代码如下:
#include<stdio.h>#include<stdlib.h>#include<string.h>#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define OVERFLOW -1#define INFEASIBLE -2#define Max_Tree_SIZE 20//最大结点数typedef int Status;typedef char TElemType;typedef struct BiTnode{ TElemType data; struct BiTnode *lchild,*rchild;}BiTNode,*BiTree;Status InitBiTree(BiTree*);/*构造空树*/Status CreateBiTree(BiTree*);/*先序递归建立二叉树*/Status PreOrder(BiTree);/*先序递归遍历*/Status InOrder(BiTree);/*中序递归遍历*/Status PostOrder(BiTree);/*后序递归遍历*/Status LevelOrder(BiTree);/*按层次非递归遍历*/Status NRPreOrder(BiTree);/*先序非递归遍历二叉树*/int TreeDepth(BiTree);/*求高度*/int LeafNumber(BiTree);/*求叶结点书*/int main(){ BiTree BT; TElemType ch; int flag=1,select; printf("To Creat binary tree,please input preorder with '#':"); InitBiTree(&BT);//初始化树 CreateBiTree(&BT);/*创建二叉树*/ while(flag==1) { printf("please select:\n"); printf("1. Preorder Traversal \n"); printf("2. Inorder Traversal \n"); printf("3. Postorder Traversal \n"); printf("4. Levelorder Traversal \n"); printf("5. NRPreorder Traversal \n"); printf("6. TreeDepth \n"); printf("7. LeafNumber \n"); printf("8. Exit \n"); scanf("%d",&select); switch(select){ case 1:printf("\nThe Preoder Traversal of binary Tree is:"); PreOrder(BT); break; case 2:printf("\nThe Inorder Traversal of binary Tree is:"); InOrder(BT); break; case 3:printf("\nThe Postorder Traversal of binary Tree is:"); PostOrder(BT); break; // case 4:printf("\nThe levelorder Traversal of binary Tree is:"); LevelOrder(BT); break; // case 5:printf("\nThe NRPreorder Traversal of binary Tree is:"); NRPreOrder(BT); break; case 6:printf("Binary tree's depth is %d",TreeDepth(BT)); break; case 7:printf("\nThe number of leaf is:%d",LeafNumber(BT)); break; default:flag=0; printf("Press any key to exit"); getchar(); }//switch printf("\n"); }//while return 0;}//mainStatus InitBiTree(BiTree* BT){ *BT=NULL; return OK;}//构造空二叉树Status CreateBiTree(BiTree* BT){ TElemType ch; scanf("%c",&ch); if(ch=='#') *BT=NULL;//注意是*BT else{ *BT=(BiTree)malloc(sizeof(BiTNode)); (*BT)->data=ch; CreateBiTree(&(*BT)->lchild); CreateBiTree(&(*BT)->rchild); } return OK;}Status PreOrder(BiTree BT){ if(BT){ printf("%c",BT->data); PreOrder(BT->lchild); PreOrder(BT->rchild); } return OK;}Status InOrder(BiTree BT){ if(BT){ InOrder(BT->lchild); printf("%c",BT->data); InOrder(BT->rchild); } return OK;}Status PostOrder(BiTree BT){ if(BT){ PostOrder(BT->lchild); PostOrder(BT->rchild); printf("%c",BT->data); } return OK;}int TreeDepth(BiTree BT){ if(!BT) return 0; int d1,d2; d1=TreeDepth(BT->lchild); d2=TreeDepth(BT->rchild); return (d1>d2?d1:d2)+1;}int LeafNumber(BiTree BT){ if(BT == NULL) return 0; return 1+LeafNumber(BT->lchild)+LeafNumber(BT->rchild);}
层次遍历如下:
void LevelOrderTraverse(BiTree T){ Queue BiNodeQueue; BiTree p=T; EnQueue(BiNodeQueue,p); while(!BiNodeQueue.Empty()) { DeQueue(BiNodeQueue,p); if(p) { printf("%c",p->data); EnQueue(BiNodeQueue,p->lchild); EnQueue(BiNodeQueue,p->rchild); } }}
/*******************************************//* 递归法将二叉树的左右子树互换 *//*******************************************/void Exchange(BiTree T){ BiTree temp; if(T) { Exchange1(T->lchild); Exchange1(T->rchild); temp=T->lchild; T->lchild=T->rchild; T->rchild=temp; }}
- 数据结构-树-二叉树
- 数据结构::树,二叉树
- 数据结构-二叉树
- 二叉树的数据结构
- 数据结构-二叉树算法
- java数据结构:二叉树
- 数据结构---二叉树
- 数据结构(C++)--二叉树
- JAVA 数据结构 二叉树
- 数据结构-二叉树 问题
- 转贴:数据结构:二叉树
- 二叉树(数据结构 c++)
- 数据结构-二叉树操作
- java数据结构----二叉树
- 数据结构二叉树
- 数据结构中的二叉树
- 数据结构--二叉树
- 数据结构二叉搜索树
- 【正则表达式】python re模块用法总结
- HDU 4886 TIANKENG’s restaurant(Ⅱ) (枚举 哈希)
- tensorflow学习之路(2-2):tf.variable_scope(),tf.name_scope(),tf.get_variable()的认识(补充)
- Teams的定义
- Disruptor学习总结(二):Disruptor分析
- 数据结构:二叉树
- windows下choco及mongodb的安装
- dos命令
- 什么产品里包含Teams
- 创建一颗最优二叉树(哈夫曼树)
- C++ socket 实参传递的两种写法
- jquery实现在鼠标点击处的炫酷效果
- 减法
- linux静态链接库与动态链接库的区别及动态库的创建