目录
7.通过前序遍历的数组"124##5#8##36##7##"构建二叉树
一.二叉树的概念及性质
1.二叉树的概念
节点的度:一个节点含有的子树的个数称为该节点的度(二叉树每个节点的度最大为2,最小为0)
叶节点:度为0的节点
分支节点:度不为0的节点
父节点、子节点:若一个节点含有子节点,则这个节点称为该子节点的父节点;若一个节点含有父节点,则这个节点成为该父节点的子节点
深度:二叉树的层数,一般情况下将第一层的层数看作1(因为如果第一层的层数看作0,空树的层数又如何定义?)
1.二叉树不存在度大于2的节点。
2.二叉树的子树有左右之分,次序不可颠倒,因此二叉树是有序树
注意:二叉树的主要用途是搜索二叉树,而不是存储数据。
2.二叉树的性质
若规定根节点的层数为1,则第i层最多有2^(i-1)个节点
若规定根节点的层数为1,则深度为h的二叉树最多有2^h-1个节点
任何一棵非空二叉树,度为0的节点永远比度为2的节点多一个,p0 = p2 + 1
若规定根节点的层数为1,n个节点的满二叉树深度:h=log2(n+1),log以2为底,n+1的对数
对于具有n个节点的完全二叉树,如果按照从上到下,从左到右的数组顺序对所有节点从0开始编号
则对序号为i位置的节点有:
若i大于0,i位置的父节点:(i - 1) / 2
i位置的左子节点:2i + 1
i位置的右子节点:2i + 2
3.特殊的二叉树
完全二叉树
假设一棵二叉树的深度为h,前h-1层符合一棵满二叉树,最后一层必须从左向右依次存放,那么这就是一棵完全二叉树。
完全二叉树的节点个数不确定,一棵深度为h的完全二叉树节的节点个数范围:2^(h-1) ~ 2^h-1
解释:一棵h-1层的满二叉树节点个数为2^(h-1)-1,第h层最少有1个节点,最多有2^(h-1)个节点。
所以假设总结点个数为X,那么 2^(h-1)-1+1 <= X <= 2(h-1)-1+2^(h-1),结果是2^(h-1) <= X <= 2^h-1
满二叉树
满二叉树是一种特殊的完全二叉树。
一棵二叉树的每一层节点个数都是该层最大值,那么这棵二叉树就称为满二叉树。
换种说法,如果一棵二叉树的深度为h,且这棵二叉树的节点个数为2^h-1,那么这就是一棵满二叉树。
一句话总结,完全二叉树和满二叉树是特殊的二叉树,而满二叉树又是特殊的完全二叉树。
二.二叉树的链式结构及遍历
1.二叉树的链式结构
这一块随意构建好一个二叉树,后面所有代码都基于这个二叉树实现。
typedef int BTDataType;
typedef struct BinaryTree
{
BTDataType val; //存储数据
struct BinaryTree* leftchild; //左孩子指针
struct BinaryTree* rightchild;//右孩子指针
}BTNode*;
//创建二叉树节点
BTNode* BuyNewNode(BTDataType x)
{
BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
if (tmp == NULL)
{
printf("malloc failed!\n");
exit(0);
}
BTNode* newnode = tmp;
newnode->val = x;
newnode->leftchild = newnode->rightchild = NULL;
return newnode;
}
//构建好一棵二叉树
BTNode* A = BuyNewNode(1);
BTNode* B = BuyNewNode(2);
BTNode* C = BuyNewNode(3);
BTNode* D = BuyNewNode(4);
BTNode* E = BuyNewNode(5);
BTNode* F = BuyNewNode(6);
BTNode* G = BuyNewNode(7);
A->leftchild = B;
A->rightchild = C;
B->leftchild = D;
B->rightchild = E;
E->leftchild = F;
C->rightchild = G;
2.二叉树的前、中、后序遍历
前序遍历
//前序遍历
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("%c ", '#');
return;
}
//打印当前根节点的值
printf("%d ", root->val);
//递归左子树
PrevOrder(root->leftchild);
//递归右子树
PrevOrder(root->rightchild);
}
中序遍历
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("%c ", '#');
return;
}
//递归左子树
InOrder(root->leftchild);
//打印当前根节点的值
printf("%d ", root->val);
//递归右子树
InOrder(root->rightchild);
}
后序遍历
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("%c ", '#');
return;
}
//递归左子树
PostOrder(root->leftchild);
//递归右子树
PostOrder(root->rightchild);
//打印当前根节点的值
printf("%d ", root->val);
}
3.二叉树的层序遍历
二叉树的层序遍历需要借助队列来实现,每出一次队列就要判断将要出队列的这个节点有无左右孩子节点,
如果有,就要将其左右孩子节点带入到队列中。
void BinaryTreeLevelOrder(BTNode* root)
{
assert(root);
//创建一个队列
Queue q;
QueueInit(&q);
//先把根节点入队列
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* first = QueueTop(&q);
printf("%d ", first->val);
QueuePop(&q);
//如果队头的左节点不为空,就带入队列
if (first->leftchild != NULL)
{
QueuePush(&q, first->leftchild);
}
//如果队头的右节点不为空,就带入队列
if (first->rightchild != NULL)
{
QueuePush(&q, first->rightchild);
}
}
QueueDestory(&q);
}
三.二叉树的基础操作实现
1.二叉树的销毁
二叉树的销毁建议使用后序遍历依次释放,前序和中序都不可取,因为释放了父节点就没有办法去找他的子节点。
void BinaryTreeDestory(BTNode* root)
{
if (root)
{
//采用后序的方式依次释放节点
BinaryTreeDestory(root->leftchild);
BinaryTreeDestory(root->rightchild);
free(root);
}
}
2.二叉树的总节点个数
int BinaryTreeSize(BTNode* root)
{
//如果根节点不为空,就把左子树和右子树的节点的和加一(意为加上自己)
return root == NULL ? 0 : BinaryTreeSize(root->leftchild) + BinaryTreeSize(root->rightchild) + 1;
}
3.二叉树的叶节点个数
int BinaryTreeLeapSize(BTNode* root)
{
//节点不存在
if (root == NULL)
{
return 0;
}
//是叶节点
if (root->leftchild == NULL && root->rightchild == NULL)
{
return 1;
}
//左子树的叶节点数+右子树的叶节点数
return BinaryTreeLeapSize(root->leftchild) + BinaryTreeLeapSize(root->rightchild);
}
4.二叉树第K层节点个数
我们所说的第K层节点个数,第K层相对对象是谁?
比如说第3层,肯定是相对于第1层来说,这时第1层是第1层,向后数到的第3层,
那么相对于第2层来说,第2层是第1层,我们刚刚说的第3层就是第2层,因为相对于第2层,第2层成了第1层
按照这个思路,求第K层,可以理解为:
求第1层的第K层,交给第2层的K-1层,交给第3层的K-2层 ... 交给第n层的K-n层,
当K-n等于1时,说明交给了第n层的第1层,这就是要求这一层的节点呀!这时就可以返回了,K-n == 1时(k = K-n),就是返回条件。
k:相对第n层的层数
K:要求的层数
n:k相对第几层,1,2,3,4,5 ...
int BinaryTreeLevelKSize(BTNode* root, int k)
{
//节点不存在
if (root == NULL)
{
return 0;
}
//节点存在,且是要求的第K层
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->leftchild, k - 1)
+ BinaryTreeLevelKSize(root->rightchild, k - 1);
}
递归展开图:
5.BinaryTreeFind - 在二叉树中查找指定值
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
//节点不存在
if (root == NULL)
return NULL;
if (root->val == x)
return root;
//递归左
BTNode* ret1 = BinaryTreeFind(root->leftchild, x);
//如果左子树找到值就返回
if (ret1)
return ret1;
//递归右
BTNode* ret2 = BinaryTreeFind(root->rightchild, x);
//如果左子树找到值就返回
if (ret2)
return ret2;
//都没找到返回空
return NULL;
}
6.判断是否是完全二叉树
层序遍历,这次的层序遍历把空节点也遍历进去,
如果遍历到空节点,那么这个节点之后的节点都应该是空,
如果出现非空节点,则不是完全二叉树。
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
//取头节点
BTNode* front = QueueTop(&q);
QueuePop(&q);
//当取到空节点时,跳出循环
if (front == NULL)
{
break;
}
//带入左右孩子,不管是否为空
QueuePush(&q, front->leftchild);
QueuePush(&q, front->rightchild);
}
//看后续是否全部为空节点
while (!QueueEmpty(&q))
{
BTNode* front = QueueTop(&q);
QueuePop(&q);
//出现不为空节点,直接否定
if (front != NULL)
{
QueueDestory(&q);
return false;
}
}
QueueDestory(&q);
return true;
}
7.通过前序遍历的数组"124##5#8##36##7##"构建二叉树
因为给了空节点的位置,所以才能只通过前序遍历来确定一颗二叉树。
如果不给空节点位置的话,需要中序+前序或者后序+前序,才可以确定一棵二叉树
// 通过前序遍历的数组"124##5#8##36##7##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
//是空节点就返回空
if (a[*pi] == '#')
{
//向后走一次
(*pi)++;
return NULL;
}
//创建根节点
BTNode* root = BuyNewNode(a[(*pi)++]);
//递归创建左子树
root->leftchild = BinaryTreeCreate(a, pi);
//递归创建右子树
root->rightchild = BinaryTreeCreate(a, pi);
//返回根节点
return root;
}
8.二叉树的最大深度
//求二叉树的最大深度
int BinaryTreeMaxDepth(BTNode* root)
{
//空节点返回0
if (root == NULL)
return 0;
//递归求左子树深度
int leftDepth = BinaryTreeMaxDepth(root->leftchild);
//递归求右子树深度
int rightDepth = BinaryTreeMaxDepth(root->rightchild);
//选左右子树深度最大的一个并且加上本层,返回给上一层。
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
9.二叉树的最小深度
//求二叉树的最小深度
int BinaryTreeMinDepth(BTNode* root)
{
//空节点返回0,这种情况其实只有在这个树为空的时候才会发生
if (root == NULL)
return 0;
//叶子节点返回1
if (root->leftchild == NULL && root->rightchild == NULL)
return 1;
//定义最小深度变量
int minDepth = INT_MAX;
//找二叉树的最小深度不能随意递归空节点,因为在比较的时候空节点深度是0,会影响最小深度值。
if (root->leftchild != NULL)
{
int getleft = BinaryTreeMinDepth(root->leftchild);
minDepth = getleft < minDepth ? getleft : minDepth;
}
if (root->rightchild != NULL)
{
int getright = BinaryTreeMinDepth(root->rightchild);
minDepth = getright < minDepth ? getright : minDepth;
}
return minDepth + 1;
}