一.简单建二叉树
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二 叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树 操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
所以我们先简单建一个二叉树:
以该二叉树为例:
//法一
TreeNode* Creat()
{//开辟树的空间TreeNode* node1 = (TreeNode*)malloc(sizeof(TreeNode));assert(node1);TreeNode* node2 = (TreeNode*)malloc(sizeof(TreeNode));assert(node2);TreeNode* node3 = (TreeNode*)malloc(sizeof(TreeNode));assert(node3);TreeNode* node4 = (TreeNode*)malloc(sizeof(TreeNode));assert(node4);TreeNode* node5 = (TreeNode*)malloc(sizeof(TreeNode));assert(node5);TreeNode* node6 = (TreeNode*)malloc(sizeof(TreeNode));assert(node6);//确定指向node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;//对每个进行赋值node1->date = 1;node2->date = 2;node3->date = 3;node4->date = 4;node5->date = 5;node6->date = 6;return node1;}
但是你会发现,这是一个明显可以简化的代码
简化结果如下:
//法二
TreeNode* BuyTreeNode(int x)
{//开辟树的空间TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->date = x;node->left = NULL;node->right = NULL;return node;
}
TreeNode* Creat()
{//开辟树并且对每个进行赋值TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);//确定指向node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;}
由于学习要逐渐深入,所以我们先简单构建二叉树。
二.二叉树的遍历
二叉树的概念:
二叉树是:
1. 空树
2. 非空:根节点,根节点的左子树、根节点的右子树组成的
你会发现:
二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。
回到我们的主题上:
二叉树主要分为四种遍历:
2.1.前序遍历
定义:
前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前
即:先根-》左子树-》右子树
回到我们前面的例题,如果该树是前序遍历,请问结果是什么?
注意:无写成NULL形式
结果为: 1->2->3->NULL->NULL->NULL->4->5->NULL->NULL->6->NULL->NULL
初学时一定不要省略了NULL,这有助于我们理解
下面我们用代码实现前序:
// 二叉树前序遍历
void PrevOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");return;}else{printf("%d ", root->date);PrevOrder(root->left);PrevOrder(root->right);}
}
结合前面我们建的二叉树,输出结果:
//法二
TreeNode* BuyTreeNode(int x)
{//开辟树的空间TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->date = x;node->left = NULL;node->right = NULL;return node;
}
TreeNode* CreateTree()
{//开辟树并且对每个进行赋值TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);//确定指向node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
//
// 二叉树前序遍历
void PrevOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");return;}else{printf("%d ", root->date);PrevOrder(root->left);PrevOrder(root->right);}
}
int main()
{TreeNode* root = CreateTree();PrevOrder(root);return 0;
}
2.2.中序遍历
定义:
中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
即:左子树-》树根-》右子树
同上,回到上面题目:
结果:NULL->3->NULL->2->NULL->1->NULL->5->NULL->4->NULL->6->NULL
代码如下:
// 二叉树中序遍历
void InOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");}else{InOrder(root->left);printf("%d ", root->date);InOrder(root->right);}
}
还是检验下:
TreeNode* BuyTreeNode(int x)
{//开辟树的空间TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->date = x;node->left = NULL;node->right = NULL;return node;
}
TreeNode* CreateTree()
{//开辟树并且对每个进行赋值TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);//确定指向node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
// 二叉树中序遍历
void InOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");}else{InOrder(root->left);printf("%d ", root->date);InOrder(root->right);}
}
int main()
{TreeNode* root = CreateTree();InOrder(root);printf("\n");return 0;
}
2.3.后序遍历
定义:
后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后
即:左子树-》右子树-》树根
上面的树结果:NULL->NULL->3->NULL->2->NULL->NULL->5->NULL->NULL->6->4->1
代码如下:
// 二叉树后序遍历
void PostOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");}else{PostOrder(root->left);PostOrder(root->right);printf("%d ", root->date); }
}
验证:
TreeNode* BuyTreeNode(int x)
{//开辟树的空间TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->date = x;node->left = NULL;node->right = NULL;return node;
}
TreeNode* CreateTree()
{//开辟树并且对每个进行赋值TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);//确定指向node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
// 二叉树后序遍历
void PostOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");}else{PostOrder(root->left);PostOrder(root->right);printf("%d ", root->date); }
}
int main()
{TreeNode* root = CreateTree();PostOrder(root);printf("\n");return 0;
}
2.4.层序遍历
层序遍历 :除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1 ,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第 2 层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历
后面我们会讲(下一个文章)
三.实现二叉树接口
3.1二叉树节点个数
还是对于这个二叉树,请用递归实现求它的节点个数
你想到了吗?如果没有,请看看我的实现吧!
对于这棵树,是不是可以看成左子树+右子树+树根,也就是左子树+右子树+1,左子树是不是也可以继续看成左子树+右子树+树根,即左子树+右子树+1,右子树同理,所以我们因此就实现了递归
看代码:
// 二叉树节点个数
int BinaryTreeSize(TreeNode* root)
{if (root == NULL){return 0;}else{return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;}
}
有煤油恍然大悟的感觉!
检验:
int main()
{TreeNode* root = CreateTree();int ret = BinaryTreeSize(root);printf("%d\n", ret);return 0;
}
(我省略了建树的代码,需要可以去前面找,后面我都会适当省略)
结果
3.2.二叉树叶子节点个数
(后面都是这棵树)
对于这个二叉树,请用 递归实现求它的 二叉树叶子节点个数
我这里就直接写了,当然你可以思考之后再看下面的代码。
要解决这个问题,关键是在于要知道什么是叶子节点,是不是它的左右子叶都是NULL,那么递归是不是就好理解了,如果为NULL,0个叶,如果左右子叶都是NULL,它为子叶
看代码:
// 二叉树叶子节点个数
int BinaryTreeLeafSize(TreeNode* root)
{if (root == NULL){return 0;}else if ((root->left == NULL) && (root->right == NULL)){return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
验证:
int main()
{TreeNode* root = CreateTree();int ret2=BinaryTreeLeafSize(root);printf("%d\n", ret2);return 0;
}
3.3.二叉树的高度
也可以认为是深度
还是递归法。
是不是树高度=max(左子树,右子树)+1;左子树高度=max(左子树,右子树),右子树同理。
代码如下:
//法一
//二叉树的高度
int BinaryTreeHeight(TreeNode* root)
{if (root == NULL){return 0;}else{int left = BinaryTreeHeight(root->left);int right = BinaryTreeHeight(root->right);return fmax(left, right) + 1;}
}
//法二
//二叉树的高度
int BinaryTreeHeight(TreeNode* root)
{if (root == NULL){return 0;}else{return fmax(BinaryTreeHeight(root->left), BinaryTreeHeight(root->right)) + 1;}
}
验证:
int main()
{TreeNode* root = CreateTree();int ret3 = BinaryTreeHeight(root);printf("%d\n", ret3);return 0;
}
3.4.二叉树第k层节点个数
求第K层节点个数,这是不是也是一个递归的题目,当K==1时,是不是就是一个节点,当k>1时,是不是可以依次递减。
代码实现过程:
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(TreeNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}else{return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);}
}
检验:
int main()
{TreeNode* root = CreateTree();int ret4=BinaryTreeLevelKSize(root, 2);printf("%d", ret4);int ret5 = BinaryTreeLevelKSize(root, 3);printf("%d", ret5);return 0;
}
3.5.二叉树查找值为x的节点
对于查找用递归来写,我们还是可以按照之前的方法,用分治法来解决。
情况一:root==NULL时,是不是该返回NULL
情况二:不为空,root本身为结果
相当于找自身,不行的话就找左子树和右子树
难点是找到之后如何将地址返回到开始的位置
是不是我们可以提前保存一下;找到返回保存值,依次回溯该值即可。
下面我们实现它:
//法一
// 二叉树查找值为x的节点定义
TreeNode* BinaryTreeFind(TreeNode* root, BTDateType x)
{if (root == NULL){return NULL;}if (root->date == x){return root;}TreeNode* ret1 = BinaryTreeFind(root->left, x);if (ret1 != NULL){return ret1;}TreeNode* ret2 = BinaryTreeFind(root->right, x);if (ret2 != NULL){return ret2;}return NULL;
}
检查:
int main()
{TreeNode* root = CreateTree();PrevOrder(root);printf("\n");TreeNode* ps = BinaryTreeFind(root, 4);if (ps == NULL){printf("没找到\n");}else{ps->date = 5;PrevOrder(root);printf("\n");}return 0;
}
如果找到的话结果4会变成5,看结果:
没问题,下面我们来用第二种方法实现:
//法二
TreeNode* BinaryTreeFind(TreeNode* root, BTDateType x)
{if (root == NULL){return NULL;}if (root->date == x){return root;}TreeNode* ret1 = BinaryTreeFind(root->left, x);if (ret1 != NULL){return ret1;}return BinaryTreeFind(root->right, x);;
}
结果没问题,大家可以深入理解下。
你是否发现这只是前序查找顺序,但是实际中,不一定就是前序查找,你是否会写中序,后序呢?
3.6.通过前序遍历的数组构建二叉树
这个是扩展知识,大家可以了解如何真正建树。
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
TreeNode* BinaryTreeCreate(char* arr, int* pi)//注意:char
{if (arr[(*pi)] == '#'){(*pi)++;return NULL;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));//判断开辟情况if (root == NULL){perror(root);exit(-1);} root->date = arr[(*pi)++];root->left = BinaryTreeCreate(arr, pi);root->right = BinaryTreeCreate(arr, pi);return root;
}
3.7.二叉树销毁
任何程序都要在不使用时进行销毁,所以我们下面来实现销毁接口。
// 二叉树销毁(一级指针法)//需要外面手动置空
void BinaryTreeDestory1(TreeNode* root)
{if (root == NULL)return;//后序销毁法BinaryTreeDestory1(root->left);BinaryTreeDestory1(root->right);free(root);
}
// 二叉树销毁(二级指针法)//不需要外面手动置空
void BinaryTreeDestory2(TreeNode** root)
{if (root == NULL)return;//后序销毁法BinaryTreeDestory2((*root)->left);BinaryTreeDestory2((*root)->right);free(root);root = NULL;
}
最后,我们所有汇总一下全部文件:
BinaryTreeNode.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
//定义结构体
typedef int BTDateType;
typedef struct BinaryTreeNode
{BTDateType date;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}TreeNode;
//动态开辟空间声明
TreeNode* BuyTreeNode(int x);
//建立二叉树声明
TreeNode* CreateTree();
// 二叉树前序遍历声明
void PrevOrder(TreeNode* root);
// 二叉树中序遍历声明
void InOrder(TreeNode* root);
// 二叉树后序遍历声明
void PostOrder(TreeNode* root);
// 二叉树节点个数声明
int BinaryTreeSize(TreeNode* root);
// 二叉树叶子节点个数声明
int BinaryTreeLeafSize(TreeNode* root);
//二叉树的高度声明
int BinaryTreeHeight(TreeNode* root);
// 二叉树第k层节点个数声明
int BinaryTreeLevelKSize(TreeNode* root, int k);
// 二叉树查找值为x的节点声明(前序)
TreeNode* BinaryTreeFindPreamble(TreeNode* root, BTDateType x);
//二叉树查找值为x的节点声明(中序)
TreeNode* BinaryTreeFindmedium(TreeNode* root, BTDateType x);
//二叉树查找值为x的节点声明(后序)
TreeNode* BinaryTreeFindpostorder(TreeNode* root, BTDateType x);
BinaryTreeNode.c:
#include "BinaryTreeNode.h"
//法一
//TreeNode* Creat()
//{
// //开辟树的空间
// TreeNode* node1 = (TreeNode*)malloc(sizeof(TreeNode));
// assert(node1);
// TreeNode* node2 = (TreeNode*)malloc(sizeof(TreeNode));
// assert(node2);
// TreeNode* node3 = (TreeNode*)malloc(sizeof(TreeNode));
// assert(node3);
// TreeNode* node4 = (TreeNode*)malloc(sizeof(TreeNode));
// assert(node4);
// TreeNode* node5 = (TreeNode*)malloc(sizeof(TreeNode));
// assert(node5);
// TreeNode* node6 = (TreeNode*)malloc(sizeof(TreeNode));
// assert(node6);
//
// //确定指向
// node1->left = node2;
// node1->right = node4;
// node2->left = node3;
// node4->left = node5;
// node4->right = node6;
//
// //对每个进行赋值
// node1->date = 1;
// node2->date = 2;
// node3->date = 3;
// node4->date = 4;
// node5->date = 5;
// node6->date = 6;
//}
//法二
//动态开辟空间定义
TreeNode* BuyTreeNode(int x)
{//开辟树的空间TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->date = x;node->left = NULL;node->right = NULL;return node;
}
//建立二叉树定义
TreeNode* CreateTree()
{//开辟树并且对每个进行赋值TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);//确定指向node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
// 二叉树前序遍历定义
void PrevOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");return;}else{printf("%d ", root->date);PrevOrder(root->left);PrevOrder(root->right);}
}
// 二叉树中序遍历定义
void InOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");}else{InOrder(root->left);printf("%d ", root->date);InOrder(root->right);}
}
// 二叉树后序遍历定义
void PostOrder(TreeNode* root)
{if (root == NULL){printf("NULL ");}else{PostOrder(root->left);PostOrder(root->right);printf("%d ", root->date); }
}
// 层序遍历定义(一:一起遍历)
void LevelOrder(TreeNode* root)
{;}
// 二叉树节点个数定义
int BinaryTreeSize(TreeNode* root)
{if (root == NULL){return 0;}else{return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;}
}
// 二叉树叶子节点个数定义
int BinaryTreeLeafSize(TreeNode* root)
{if (root == NULL){return 0;}else if ((root->left == NULL) && (root->right == NULL)){return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
法一
二叉树的高度定义
//int BinaryTreeHeight(TreeNode* root)
//{
// if (root == NULL)
// {
// return 0;
// }
// else
// {
// int left = BinaryTreeHeight(root->left);
// int right = BinaryTreeHeight(root->right);
// return fmax(left, right) + 1;
// }
//}
//法二
//二叉树的高度定义
int BinaryTreeHeight(TreeNode* root)
{if (root == NULL){return 0;}else{return fmax(BinaryTreeHeight(root->left), BinaryTreeHeight(root->right)) + 1;}
}
// 二叉树第k层节点个数定义
int BinaryTreeLevelKSize(TreeNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}else{return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);}
}
//法一
// 二叉树查找值为x的节点定义
//TreeNode* BinaryTreeFind(TreeNode* root, BTDateType x)
//{
// if (root == NULL)
// {
// return NULL;
// }
// if (root->date == x)
// {
// return root;
// }
// TreeNode* ret1 = BinaryTreeFind(root->left, x);
// if (ret1 != NULL)
// {
// return ret1;
// }
// TreeNode* ret2 = BinaryTreeFind(root->right, x);
// if (ret2 != NULL)
// {
// return ret2;
// }
// return NULL;
//}
//法二
TreeNode* BinaryTreeFindPreamble(TreeNode* root, BTDateType x)
{if (root == NULL){return NULL;}if (root->date == x){return root;}TreeNode* ret1 = BinaryTreeFindPreamble(root->left, x);if (ret1 != NULL){return ret1;}return BinaryTreeFindPreamble(root->right, x);;
}
//二叉树查找值为x的节点定义(中序)
TreeNode* BinaryTreeFindmedium(TreeNode* root, BTDateType x)
{if (root == NULL){return NULL;}TreeNode* ret1 = BinaryTreeFindmedium(root->left, x);if (ret1 != NULL){return ret1;}if (root->date == x){return root;}TreeNode* ret2 = BinaryTreeFindmedium(root->right, x);if (ret2 != NULL){return ret2;}return NULL;
}
//二叉树查找值为x的节点定义(后序)
TreeNode* BinaryTreeFindpostorder(TreeNode* root, BTDateType x)
{if (root == NULL){return NULL;}TreeNode* ret1 = BinaryTreeFindpostorder(root->left, x);if (ret1 != NULL){return ret1;}TreeNode* ret2 = BinaryTreeFindpostorder(root->right, x);if (ret2 != NULL){return ret2;}if (root->date == x){return root;}return NULL;
}
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
TreeNode* BinaryTreeCreate(char* arr, int* pi)//注意:char
{if (arr[(*pi)] == '#'){(*pi)++;return NULL;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));//判断开辟情况if (root == NULL){perror(root);exit(-1);} root->date = arr[(*pi)++];root->left = BinaryTreeCreate(arr, pi);root->right = BinaryTreeCreate(arr, pi);return root;
}
// 二叉树销毁(一级指针法)//需要外面手动置空
void BinaryTreeDestory1(TreeNode* root)
{if (root == NULL)return;//后序销毁法BinaryTreeDestory1(root->left);BinaryTreeDestory1(root->right);free(root);
}
// 二叉树销毁(二级指针法)//不需要外面手动置空
void BinaryTreeDestory2(TreeNode** root)
{if (root == NULL)return;//后序销毁法BinaryTreeDestory2((*root)->left);BinaryTreeDestory2((*root)->right);free(root);root = NULL;
}
三.关于树的基本部分就到这了,感谢大家的支持!!!
祝福大家元旦假期快乐。