我们知道,树的度,就是一个节点的孩子个数,可以为零也可以有很多,但是作用最大的还得是我们的二叉树。二叉树就是一棵树,这棵树中所有的节点中最大的度就是二
二叉树中又包括完全二叉树和满二叉树
完全二叉树是一种特殊的二叉树,其所有叶子节点都出现在最后一层或倒数第二层,并且最后一层的叶子节点都靠左对齐。除了最后一层,其他层节点数都是满的
满二叉树就是指这棵树每一层都是满的,每一层都无法再插入节点了,满二叉树也属于完全二叉树
下面我们来定义一下二叉树的节点,每个节点要包括本身要存的值和左孩子的指针和右孩子的指针
typedef struct binarytreenode {
struct binarytreenode* left;
struct binarytreenode* right;
int val;
}BTNode;
接下来就是对于一个二叉树的遍历了,二叉树的遍历分为前序遍历,中序遍历和后序遍历
先看前序遍历,前序遍历是对于每一颗二叉树(也包括子树)来说,先访问它的根节点,再访问它的左孩子,再访问它的右孩子,所以这就符合了递归的特性
void PrevOrder(BTNode* root) {
if (root == NULL) {
printf("NULL ");
return;
}
printf("%d ", root->val);
PrevOrder(root->left);
PrevOrder(root->right);
}
同理,再看中序遍历,先访问它的左孩子,再访问它的根节点,再访问它的右孩子
void InOrder(BTNode* root) {
if (root == NULL) {
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->val);
InOrder(root->right);
}
最后看后序遍历,先访问它的左孩子,再访问它的右孩子,再访问它的根节点
void PostOrder(BTNode* root) {
if (root == NULL) {
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->val);
}
接下来是求一个二叉树的节点个数,其实一个二叉树的节点个数不就等于左孩子的节点个数加右孩子的节点个数再加上自己的根节点的一个,所以我们还是用递归去写,包括后面的内容,很大一部分都是用递归写的,因为二叉树的结构就决定了它确实适合用二叉树去解决一些问题
int TreeSize(BTNode* root) {
if (root == NULL) {
return 0;
}
else {
return TreeSize(root->right) + TreeSize(root->left) + 1;
}
}
下面求一个树的叶子节点个数,先说什么样的节点属于叶子节点呢?就是左孩子和右孩子为空的节点,那我们就用这个条件去判断就可以,对于一个根节点来说,还是返回一个根节点的左边叶子个数和右边叶子个数之和
int TreeLeafSize(BTNode* root) {
if (root == NULL) {
return 0;
}
if (root->left == NULL && root->right == NULL) {
return 1;
}
return TreeLeafSize(root->right) + TreeLeafSize(root->left);
}
下面求第K层的节点个数,我们设定整棵二叉树的根节点为第一层,对于第一层来说是求第K层,对于第二层来说是求第K-1层,那么对于第K层来说不就是求第一层嘛
int TreeKSzie(BTNode* root, int k) {
assert(k > 0);
if (root == NULL) {
return 0;
}
if (k == 1) {
return 1;
}
return TreeKSzie(root->left, k - 1) + TreeKSzie(root->right, k - 1);
}
下面在树中寻找某个值为x的数据,返回x的地址,那还是要递归,能找到就返回地址,找不到就返回空
BTNode* TreeFind(BTNode* root, int x) {
if (root == NULL) {
return NULL;
}
if (root->val == x) {
return root;
}
BTNode* ret = TreeFind(root->left, x);
if (ret != NULL) {
return ret;
}
return TreeFind(root->right, x);
}
下面是对于一棵二叉树的层序遍历,也是广度优先遍历,我们之前的前序遍历,中序遍历和后序遍历都是深度优先遍历。所谓的层序遍历就是先遍历第一层,在遍历第二层,以此类推,直到遍历完。
我们应该怎么实现呢?我们先让根节点入队列,根出它的孩子就进,队头元素再出,它的孩子再进,直到队列为空。
void LevelOrder(BTNode* root) {
Que q;
QueueInit(&q);
if(root)
QueuePush(&q, root);
while (!QueueEmpty(&q)) {
BTNode* tmp = QueueFront(&q);
printf("%d ", tmp->val);
if (QueueFront(&q)->left) {
QueuePush(&q,tmp->left);
}
if (QueueFront(&q)->right) {
QueuePush(&q,tmp->right);
}
QueuePop(&q);
}
QueueDestroy(&q);
}
这里需要用到队列的知识,有关函数可以通过下面的链接跳转到我的另一篇博客中
链接:队列
下面我们求一下二叉树的高度,高度无非就是左子树和右子树中最高的那个
int TreeHeight(BTNode* root) {
if (root == NULL) {
return 0;
}
return (int)fmax(TreeHeight(root->left), TreeHeight(root->right))+1;
}
这里的fmax函数要包含math.h的头文件
下面我们来写一个函数来判断一棵树是否为完全二叉树,完全二叉树的特征就是,把所有的节点按层序遍历入队列,包括空节点,在这个队列中,所有非空的节点都是连续的,也就是其中不包含非空的节点。但非完全二叉树不是这样的,它的非空节点之间肯定包括空节点。我们就利用这个特性来解决问题
于是,我们这里的操作就和层序遍历一样,只不过空节点也要入,直到到空节点出队列了,然后检测这时队列中是否全是空节点
bool TreeComplete(BTNode* root) {
Que q;
QueueInit(&q);
if (root) {
QueuePush(&q,root);
}
while (QueueFront(&q) != NULL) {
BTNode* tmp = QueueFront(&q);
QueuePush(&q, tmp->left);
QueuePush(&q, tmp->right);
QueuePop(&q);
}
while (!QueueEmpty(&q)) {
if (QueueFront(&q) != NULL) {
QueueDestroy(&q);
return false;
}
QueuePop(&q);
}
QueueDestroy(&q);
return true;
}