【基本概念】
二叉树( binary tree)是一种特殊的树型结构,它是度数为2的树,即二叉树的每个结点最多有两个子结点。
每个结点的子结点分别称为左孩子、右孩子,它的两棵子树分别称为左子树、右子树。
二叉树有5种基本形态:
【二叉树的性质】
1.在二叉树的第 i 层上最多有 2^(i-1) 个结点(i>=1)
满二叉树,其特点是每层上的结点数都是最大结点数。
对满二叉树的结点进行连续编号,约定编号从根结点起,自上而下,从左到右,由此引出完全二叉树的定义:深度为 k,有 n 个结点的二叉树当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 到 n 的结点一一对应时,称为完全二叉树
3.对任意一棵二叉树,如果其叶结点数为 n0,度为 2 的结点数为 n2,则一定满足:n0=n2+1
4.具有 n 个结点的完全二叉树的深度为 floor(log2n)+1
5.对于一棵具有 n 个结点的完全二叉树,对任一个结点(编号为i),有:
【二叉树的存储结构】
方法1:链式存储结构,即:单链表结构或双链表结构
typedef struct node; typedef node *tree; struct node{ char data; tree lchild, rchild; }; tree bt; 或 typedef struct node; typedef node *tree; struct node{ char data; tree lchild, rchild,father; }; tree bt;如图,一棵二叉树(左图)即可用单链表表示(右图)
方法2:顺序存储结构,即:用几个数组与一个指针变量表示
const int n = 10; char data[n]; char lchild[n]; char rchild[n]; int root;
表达式树(用叶结点表示运算元,分支结点表示运算符)经常用到此种方式。
例:现有一表达式:(a+b/c)*(d-e),可用下图表示
数据结构定义如下:
按表达式的书写顺序逐个编号,分别为1..9,注意表达式中的所有括号在树中是不出现的,因为表达式树本身就是有序的,叶结点的左右子树均为空(用0表示)。
char data[9] = {'a', '+', 'b', '/', 'c', '*', 'd', '-', 'e'}; int lchild[9] = {0,1,0,3,0,2,0,7,0}; int rchild[9] = {0,4,0,5,0,8,0,9,0}; int root=6; //根结点指针,初值指向'*'【二叉树的基本操作】
1.遍历二叉树
二叉树的遍历:按一定的规律和次序访问树中的各个结点,而且每个结点仅被访问一次。
二叉树的遍历问题:在树中查找具有某种特征的结点,或对所有结点逐一进行某种处理。
遍历一般按照从左到右的顺序,分为三种方法:先序遍历、中序遍历、后序遍历
1)先序遍历
若二叉树为空,则空操作,否则:先访问根结点,再先序遍历左子树,然后先序遍历右子树
void preorder(tree bt)//先序遍历根结点为bt的二叉树的递归算法 { if(bt) { cout << bt->data; preorder(bt->lchild); preorder(bt->rchild); } }以下图为例,先序遍历此图结果为:124753689
2)中序遍历
若二叉树为空,则空操作,否则:先中序遍历左子树,再访问根结点,然后中序遍历右子树
void inorder(tree bt)//中序遍历根结点为bt的二叉树的递归算法 { if(bt) { inorder(bt->lchild); cout << bt->data; inorder(bt->rchild); } }以下图为例,中序遍历此图结果为:742513869
3)后序遍历
若二叉树为空,则空操作,否则:先后序遍历左子树,再后序遍历右子树,然后访问根结点
void postorder(tree bt) //后序遍历根结点为bt的二叉树的递归算法 { if(bt) { postorder(bt->lchild); postorder(bt->rchild); cout << bt->data; } }以下图为例,后序遍历此图结果为:745289631
2.建立二叉树
void pre_crt(tree &bt)//按先序次序输入二叉树中结点的值,生成 { char ch; ch = getchar();//二叉树的单链表存储结构,bt为指向根结点的指针,'$'表示空树 if(ch != '$') { bt = new node;//建根结点 bt->data = ch; pre_crt(bt->lchild);//建左子树 pre_crt(bt->rchild);//建右子树 } else bt = NULL; }3.删除二叉树
void dis(tree &bt)//删除二叉树 { if(bt) { dis(bt->lchild);//删左子树 dis(bt->rchild);//删右子树 delete bt;//释放父结点 } }4.插入一个结点到排序二叉树中
void insert(tree &bt, int n)//插入一个结点到排序二叉树中 { if(bt) { if(n < bt->data) insert(bt->lchild, n); else if(n > bt->data) insert(bt->rchild, n); } else { bt = new node;//新开一个空间 bt->data = n; bt->lchild = bt->rchild = NULL; } } 5.在排序二叉树中查找一个数,找到返回该结点,否则返回NULL
tree findn(tree bt, int n) //在二叉树中查找一个数,找到返回该结点,否则返回NULL。 { if(bt) { if(n < bt->data) findn(bt->lchild, n); else if(n > bt->data) findn(bt->rchild, n); else return bt; } else return NULL; }6.用嵌套括号表示法输出二叉树
void print(tree bt)//用嵌套括号表示法输出二叉树 { if(bt) { cout << bt->data; if(bt->lchild || bt->rchild) { cout << '('; print(bt->lchild); if(bt->rchild) cout << ','; print(bt->rchild); cout << ')'; } } }【树的计数问题】
相似二叉树:两者都为空树或者两者均不为空树,且它们的左右子树分别相似。
等价二叉树:两者不仅相似,而且所有对应结点上的数据元素均相同。
二叉树的计数问题就是讨论具有 n 个结点、互不相似的二叉树的数目 Bn 。
一般情况,一棵具有 n(n>1) 个结点的二叉树可以看成是由一个根结点、一棵具有 i 个结点的左子树和一棵具有 n-i-1 个结点的右子树组成,其中0<=i<=n-1,当n很小时,易得:B0=1,B1=1,B2=2,B3=5
化为函数,即得: