目录
- 二叉树的概念和性质
- 二叉树顺序结构及实现
- 二叉树链式结构及实现
- 二叉树的前 、中、后序遍历
- 二叉树基础题练习
1.二叉树的概念
二叉树是树的结构中最常用的一种结构,它的形式是一个结点最多可以有两个子节点,即左结点和右结点。用图表示如下
可以看出二叉树中不存在度超过2的结点,而且二叉树的子树有左右之分,次序不能颠倒,二叉树是一颗有序树,对于任意一颗二叉树都是由以下几种结构复合而成的。
二叉树中又分有两种特殊的树,即满二叉树和完全二叉树
- 满二叉树就是该树的每一层的结点数都到达了最大,就是除了最后一层都为叶子结点外,其余所有节点都有左右子结点,可以算出第k层的结点数为2(k-1) 总的结点数是2k -1。
- 完全二叉数是该树只有最后一层结点没满,且最后一层的叶子结点从左到右是连续布置的。
2. 二叉树的性质
- 二叉树第i层最多有2(i-1)个结点,数的深度为k时,总结点数最多为2k -1个,也就是满二叉树。
- 二叉树中只有度为0的结点n0、度为1的节点n1和度为2的结点n2,且有关系式n0=n2+1。
这里就简单推导以下:
设总节点数为n0+n1+n2,则总边数为n0+n1+n2-1,总边数又等n0x0+n1x1+n2x2,就有等式n0+n1+n2-1=n0x0+n1x1+n2x2,推出n0=n2+1。
2二叉树顺序结构及实现
顺序结构就是用数组来存储二叉数各结点的数据,将完全二叉数用数组来实现是非常方便的,数据依次存入数组中,而各节点的逻辑关系则通过数组下标来控制。
像上图一样,数组首元素来存根结点的数据,由关系式左孩子结点下标child=双亲结点下标parent*2+1,右孩子结点下标等于左孩子下标+1.这样来存数据,同时知道一个结点下标时可以由该下标-1后再除2得到双亲结点的下标。
非完全二叉树要用顺序结构来表示的话,为了保持树的逻辑结构,到最后一层从左到右最后一个结点为止,树空出来的部位,在数组中也要相应的空出来。
二叉树的顺序存储实现就衍生出一种叫堆的数据结构,详细了解可转到堆的概念、结构及应用
3.二叉树链式结构及实现
用代码表述
typedef char SDatatype;
typedef struct binarytree
{
SDatatype val; //要存储的数据
struct binarytree* left; //指向它的左孩子结点
struct binarytree* right; // 指向它的有孩子结点
}BTNode;
就是用链表的方式来表示二叉树的逻辑结构,链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在链结点的存储位置。
比如要实现上图的二叉树,可用下面的代码实现。
Datatype Buytree(SDatatype x) //动态申请一个链节点
{
Datatype root = (Datatype)malloc(sizeof(struct bintree));
if (root == NULL)
{
printf("mallof fail\n");
exit(-1);
}
root->val = x;
root->left = NULL;
root->right = NULL;
return root;
}
Datatype creattree()
{
Datatype NodeA = Buytree('A'); //这里分别创建各个结点
Datatype NodeB = Buytree('B');
Datatype NodeC = Buytree('C');
Datatype NodeD = Buytree('D');
Datatype NodeE = Buytree('E');
Datatype NodeF = Buytree('F');
Datatype NodeG = Buytree('G');
Datatype NodeH = Buytree('H');
NodeA->left = NodeB; //这里就人为的按照想要的逻辑关系连接各结点成二叉树
NodeA->right = NodeC;
NodeB->left = NodeD;
NodeB->right = NodeE;
NodeC->left = NodeF;
NodeC->right = NodeG;
NodeE->right = NodeH;
return NodeA;
}
4.二叉树的前 、中、后序遍历
对于二叉树来说如何构建一颗二叉树意义并不大,关键是如何搜索二叉树,遍历二叉树。主要由三种遍历方式,分为前序、中序和后续遍历,三种方式都是通过递归来实现的。
- 前序遍历是拿到一个结点后先获得该结点的数据,之后其左子树存在就遍历它的左子树。然后右子树存在就遍历右子树,遇到空指针就返回。
void Binarytreeprevorder(BTNode* root)
{
if (root == NULL)
return;
printf("%c ", root->val);
Binarytreeprevorder(root->left);
Binarytreeprevorder(root->right);
}
依次遍历我上面构建的二叉树,打印结果是A B D E H C F G.
2. 中序遍历是拿到一个结点后,如果它有左子树就先去遍历它的左子树,遍历完了,在操作该节点数据,之后如果右子树存在就再去遍历它的右子树。
void Binarytreeinorder(BTNode* root)
{
if (root == NULL)
return;
Binarytreeinorder(root->left);
printf("%c ", root->val);
Binarytreeinorder(root->right);
}
遍历结果是 D B E H A F C G
- 后续遍历是左子树存在先遍历左子树,之后右子树存在就遍历右子树,最后在获得该结点的数据。
void Binarytreeposorder(BTNode* root)
{
if (root == NULL)
return;
Binarytreeposorder(root->left);
Binarytreeposorder(root->right);
printf("%c ", root->val);
}
遍历结果是 D H E B F G C A
5.二叉树基础题练习
- 计算二叉树的结点总数
先拿到根结点地址,判断拿到的结点地址是否为空,如果为空就返回0,不为空则最少就知道有一个结点了,再递归去检查左子树和右子树,不断累加得出总结点数。
int Binarytreesize(BTNode* root)
{
if (root == NULL)
return 0;
return Binarytreesize(root->left) + Binarytreesize(root->right) + 1;
}
2.计算二叉数叶子结点数
叶子结点就要满足结点地址不为NULL其指向左右孩子的指针都为NULL
int BinarytreeLeafsize(BTNode* root)
{
if (root == NULL)
return 0;
if (root && root->left == NULL && root->right == NULL)
return 1;
return BinarytreeLeafsize(root->left) + BinarytreeLeafsize(root->right);
}
- 计算二叉数第k层的结点个数
从根结点开始,拿到一个结点地址后先判断是否为NULL,如果是,那么这个位置就没有结点,且没有子树了,无论是否是k层都返回0,当一个结点不为空时,这就算一层,将k-1后与0比较是否相等,比如k=1时,结点不为空k-1=0,判断k==0为真就是已经到了k层,返回1,依次类推。
int BinarytreeLevelKsize(BTNode* root, int k)
{
if (root == NULL)
return 0;
k--;
if ( k == 0)
return 1;
return BinarytreeLevelKsize(root->left, k) + BinarytreeLevelKsize(root->right, k);
}
- 二叉树查找值为x的结点
先判断地址是否为空,为空返回NULL,不为空就判断该结点所存值是否等于x,为真则返回该结点地址,为假就去遍历它的左子树,还未找出就去遍历右子树。
BTNode* BinarytreeFind(BTNode* root, Datatype x)
{
if (root == NULL)
return NULL;
if (root->val == x)
return root;
BTNode* left = BinarytreeFind(root->left, x);//这里定义一个变量来存遍历结果,避免两次重复的遍历
if (left)
return left;
BTNode*right= BinarytreeFind(root->right, x); //这里作用同上
if (right)
return right;
return NULL;
}