二叉树的定义:
二叉树是一种特殊的树,其特点是每个结点至多只有两个子树(即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。
因此二叉树是有序树,若将其左右子树颠倒,则会成为另一颗不同的二叉树。即使树中结点只有一个子树,也要区分它是左子树还是右子树。
注意:
二叉树与度为2的树的区别:
- 度为2的树至少有3个结点,而二叉树可以为空;
- 度为2的有序树的孩子结点的左右次序是相对于另一个孩子结点而言的,若某个结点只有一个孩子结点,则这个孩子结点就无须区分其左右次序,而二叉树无论其孩子数是否为2,均有确定的左右次序;
几种特殊的二叉树:
1.满二叉树:一颗高度为h,且含有2^h - 1个结点的二叉树称为满二叉树。
满二叉树的特点是:
- 所有叶子结点都集中在二叉树的最下面一层;
- 除叶子结点外,其余结点的度都为2;
- 对于结点i(根节点从1开始编号),若有双亲结点,则双亲结点编号为(i/2)向上取整,若有左孩子,则左孩子的编号为2*i,若有右孩子,则右孩子的编号为2*i+1;
2.完全二叉树:设高度为h,有n个结点的二叉树,当且仅当其每个结点都与高度为h的满二叉树中编号为1-n的结点一一对应,称为完全二叉树。简单理解就是完全二叉树从最大编号依次删除一个或多个结点后的二叉树。
完全二叉树的特点:
- 若 i<=(n/2向上取整),则结点i为分支结点,否则为叶子结点;
- 叶子结点只可能在层次最大的两层上出现。对于最大层次中的叶子结点,都依次排列在该层最左边的位置上;
- 若有度为1的结点,则只可能有一个,且该结点只有左孩子而无右孩子;
- 按层序编号后,一旦出现某结点(编号为i)为叶子结点或者只有左孩子,则编号大于i的结点均为叶子结点;
- 若n为奇数,则每个分支结点都有左孩子和右孩子;若n为偶数,则编号最大的分支结点(n/2)只有左孩子,没有右孩子,其余节点均有左孩子和右孩子;
3.二叉排序树:一颗二叉树具有如下特性,左子树上所有节点的关键字均小于根结点的关键字,右子树上的所有结点的关键字均大于根结点的关键字。左子树和右子树又各是一个二叉排序树。
4.平衡二叉树:树上仍一一结点的左子树和右子树的深度之差不超过1;
二叉树的性质:
- 非空二叉树上的叶子结点数等于度为2的结点数加1,即n0 = n2+1;
- 非空二叉树上第k层上至多有2^(k-1)个结点(k>=1);
- 高度为h的二叉树至多有(2^h - 1)个结点(h>=1);
- 对于完全二叉树,设自根结点开始,从1开始编号(1,2,3...n),则有以下的性质:
- 当i>1时,结点i的双亲结点编号为(i/2向上取整);
- 当2*i<=n时,结点i的左孩子编号为2*i,否则无左孩子;
- 当2*i+1<=n时,结点i的右孩子编号为2*i+1,否则无右孩子;
- 结点i所在的层次(深度)为(log2(i)向上取整+1);
5. 具有n个结点的完全二叉树的高度为(log2(n+1)向下取整)或者((log2(n)向上取整)+1);
二叉树的存储结构:
二叉树的存储结构主要分为顺序存储和链式存储
二叉树的顺序存储:
二叉树的顺序存储是指用一组连续的存储单元依次自上而下,自左至右存储二叉树上的元素。
依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号可以唯一的反应结点之间的逻辑关系。但是对于一般的二叉树,为了让数组下标能够反应树二叉树上元素的逻辑关系,可能要空出很多空单元。
二叉树顺序存储的相关操作:
1.二叉树的结构定义:
#define maxsize 8
typedef int SqBiTree[maxsize];
2.初始化二叉树
void inittree(SqBiTree& tr) {
for (int i = 0; i < maxsize; i++)
{
tr[i] = i+1;
}
}
3.打印有左孩子的结点的左孩子:
void printleft(SqBiTree& tr) {
for (int i = 0; tr[i] * 2 <= maxsize; i++)
{
cout << "结点" << i << "的左孩子是: " << tr[2*i+1] << endl;
}
}
4.打印有右孩子结点的右孩子:
void printright(SqBiTree& tr) {
for (int i = 0; tr[i] * 2 +1<= maxsize; i++)
{
cout << "结点" << i << "的右孩子是: " << tr[2 *(i+1)] << endl;
}
}
5.打印完全二叉的高度:
void getdeep(SqBiTree& tr) {
cout << "完全二叉树的高度为: " << log2(maxsize) + 1;
}
6.整体代码:
#include<iostream>
#include<cmath>
#define maxsize 8
using namespace std;
typedef int SqBiTree[maxsize];
//初始化树
void inittree(SqBiTree& tr) {
for (int i = 0; i < maxsize; i++)
{
tr[i] = i+1;
}
}
//打印结点左孩子
void printleft(SqBiTree& tr) {
for (int i = 0; tr[i] * 2 <= maxsize; i++)
{
cout << "结点" << i << "的左孩子是: " << tr[2*i+1] << endl;
}
}
//打印结点右孩子
void printright(SqBiTree& tr) {
for (int i = 0; tr[i] * 2 +1<= maxsize; i++)
{
cout << "结点" << i << "的右孩子是: " << tr[2 *(i+1)] << endl;
}
}
//打印完全二叉树的高度
void getdeep(SqBiTree& tr) {
cout << "完全二叉树的高度为: " << log2(maxsize) + 1;
}
int main() {
SqBiTree T;
inittree(T);
printleft(T);
cout << "------------" << endl;
printright(T);
cout << "------------" << endl;
getdeep(T);
return 0;
}
7.执行结果:
二叉树的链式存储:
二叉树的结构体定义:
typedef struct BiTNode{
int data;
struct BiTNode* lchild, * rchild;
}BiTNode,*BiTree;
与二叉树的链式存储的相关操作涉及增删改查,遍历等,将会在后续的文章中详细讲解。
总结:
二叉树的链式存储的空间利用效率会比顺序存储高,树的存储通常都是用的链式存储。