目录
一、树的基本介绍
树由节点和边组成,其中节点表示数据元素,边表示节点之间的关系。
二、二叉树的定义
二叉树(Binary tree)是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。
三、二叉树的实现
(一)二叉树的结构
包含一个值,两个二叉树节点指针分别指向左右节点。
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
(二)二叉树的创建
二叉树的前序遍历是指先访问根节点,然后访问左子树,最后访问右子树。顺序是:根 -> 左 -> 右。
而创建一个二叉树使用了递归调用,值不为NULL(#)时,使用malloc函数开辟一块空间创建一个节点,然后该节点再次调用BinaryTreeCreate函数创建其左节点和右节点。
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if (*pi >= n || a[*pi] == '#')
{
(*pi)++;
return NULL;//不是return
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
newnode->_data = a[(*pi)++];
newnode->_left = BinaryTreeCreate(a, n, pi);
newnode->_right = BinaryTreeCreate(a, n, pi);
return newnode;
}
(三)二叉树的遍历
二叉树遍历的代码恨简单,当root==NULL时,返回,注意这里的return是返回至上一层。
还有就是是打印以及调用,将打印根节点这一行代码放在不同位置就可以实现前序,中序以及后序。
1.前序遍历(PreOrder)
先访问根节点,然后访问左子树,最后访问右子树。顺序是:根 -> 左 -> 右。
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
printf("%c", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
2.中序遍历(InOrder)
先访问左子树,然后访问根节点,最后访问右子树。顺序是:左 -> 根 -> 右。
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreeInOrder(root->_left);
printf("%c", root->_data);
BinaryTreeInOrder(root->_right);
}
3.后序遍历(PostOrder)
先访问左子树,然后访问右子树,最后访问根节点。顺序是:左 -> 右 -> 根。
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c", root->_data);
}
(四)二叉树的节点个数
也是通过递归调用,返回左节点个数+右节点个数,每次递归回来就相当于是访问了对应的根节点
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;//+1是代表根节点
}
也可以简化为下面的写法
int TreeSize(BTNode* root)
{
return root == NULL ? 0 :
TreeSize(root->left) + TreeSize(root->right) + 1;
}
(五)二叉树叶子节点的个数
叶子节点的左右节点都为NULL,当遇到叶子节点时返回1,其余返回0
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return TreeLeafSize(root->left)
+ TreeLeafSize(root->right);
}
(六)返回二叉树高度(深度)
其实就是递归调用,返回左右更高的那个,以下代码也可以像二叉树节点个数代码一样进行简化
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
return leftHeight > rightHeight ?
leftHeight + 1 : rightHeight + 1;
}
(七)二叉树第k层节点个数
第一层为k,第二层k-1,直到第k层为1,那就当k==1的时候,这一层的节点个数
int TreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
// 子问题
return TreeLevelKSize(root->left, k - 1)
+ TreeLevelKSize(root->right, k - 1);
}
(八)查找值为x的节点
其实就是遍历每一个节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* ret1 = TreeFind(root->left, x);
if (ret1)
return ret1;
BTNode* ret2 = TreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
(九)层序遍历
层序遍历要求输出二叉树,一层一层从左到右
我们选择使用队列(先进先出)
当队列为空时,进入左右节点
void BinaryTreeLevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c ", front->_data);
//左不为空,把左进去,右不为空把右进去
if (front->_left)
QueuePush(&q, front->_left);
if (front->_right)
QueuePush(&q, front->_right);
}
QueueDestroy(&q);
}
(十)判断二叉树是否为完全二叉树
一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同。
除了最后一层外,其他层是满二叉树,最后一层的节点要求从左到右连续。
解决办法很简单,利用层序遍历,但是当遇到第一个NULL的时候,如果之后全为空则是完全二叉树,否则不是。
bool TreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
// 遇到第一个空,就可以开始判断,如果队列中还有非空,就不是完全二叉树
if (front == NULL)
{
break;
}
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
// 如果有非空,就不是完全二叉树
if (front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
四、相关题目
(一)单值二叉树
判断是否存在,在判断根的左节点是否存在,以及值是否相等,接着判断右节点是否存在和值是否相等,返回递归将左右节点分别作为根节点。
(二)二叉树深度
返回二叉树高度
(三)二叉树前序遍历
这个题需要注意的是
使用数组存放,但是数组下标不能使用局部变量,也就是要求传地址
(四)判断是否为相同的树
(五)对称二叉树
(六)另一个树子树
我是构造一个判断相同的函数,然后遍历每个节点
注意在对节点判断时,先看是否存在,不然会报错
(七)二叉树遍历
跟前序遍历那个题类似
(八)翻转二叉树
(九)平衡二叉树
五、题目代码
(一)单值二叉树
bool isUnivalTree(struct TreeNode* root) {
if (root == NULL)
{
return true;
}
if((root->left && root->left->val != root->val) || (root->right && root->right->val != root->val))
return false;
return isUnivalTree(root->right)&&isUnivalTree(root->left);;
}
(二)二叉树深度
int maxDepth(struct TreeNode* root) {
if(root == NULL)
return 0;
int leftHeight = maxDepth(root->left);
int rightHeight = maxDepth(root->right);
return leftHeight > rightHeight? leftHeight+1:rightHeight+1;
}
(三)二叉树前序遍历
//要求数组需要malloc,假定使用的人会free
//1.扩容的方式 2.先遍历一遍
int TreeSize(struct TreeNode* root)
{
return root == NULL? 0 : TreeSize(root->left)+TreeSize(root->right)+1;
}
void prevOrder(struct TreeNode* root, int* a, int* pi)//int i 是局部变量,每个递归调用中不同了!!!
{
if(root == NULL)
{
return;
}
a[(*pi)++] = root->val;
prevOrder(root->left, a, pi);
prevOrder(root->right, a, pi);
}
//输出型参数 -- returnSize
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
//returnSize 是leetcode的规范,如果返回数组,需要返回数组大小
*returnSize = TreeSize(root);
int* a = (int*)malloc(sizeof(int)*(*returnSize));
int *i = 0;
prevOrder(root, a, &i);
return a;
}
(四)判断是否为相同的树
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
if(p == NULL && q == NULL)
return true;
//其中一个不为空
if(p == NULL || q == NULL)
return false;
if(p->val != q->val)
return false;
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
(五)对称二叉树
bool _isSymmetric(struct TreeNode* p, struct TreeNode* q) {
if(!p && !q)//pq都为空
{
return true;
}
if((p && q) && (p->val == q->val))//pq都不为空
return _isSymmetric(p->right, q->left)&&_isSymmetric(p->left, q->right);
else
{
return false;
}
return true;
}
bool isSymmetric(struct TreeNode* root) {
if(_isSymmetric(root->right, root->left))
return true;
else
return false;
}
(六)另一棵树子树
//遍历所有子树,看是否相同(isSame 递归返回左跟左,右跟右)
//在isSun中返回左与subRoot,右与subRoot
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p == NULL && q == NULL)
return true;
//其中一个不为空
if(p == NULL || q == NULL)
return false;
if(p->val != q->val)
return false;
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root == NULL)
{
return false;
}
if(root->val == subRoot->val && isSameTree(root, subRoot))
return true;
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
(七)二叉树遍历
#include <stdio.h>
typedef struct TreeNode
{
char val;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
TreeNode* MakeTree(char* a, int* pi)
{
if(a[*pi] == '#' || a[*pi] == '\0')//不能在这(*pi)++,放这==,如果不是#也会++
{
(*pi)++;
return NULL;
}
TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
newnode->val = a[(*pi)++];
newnode->left = MakeTree(a, pi);
newnode->right = MakeTree(a, pi);
return newnode;
}
// TreeNode* MakeTree(char* a, int* pi)
// {
// if(a[*pi] == '#' || a[*pi] == '\0')
// {
// return NULL;
// }
// TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
// newnode->val = a[(*pi)++];
// newnode->left = MakeTree(a, pi);
// (*pi)++;
// newnode->right = MakeTree(a, pi);
// return newnode;
// }
void Inorder(TreeNode* root)
{
if(root==NULL)
{
return;
}
Inorder(root->left);
printf("%c ",root->val);
Inorder(root->right);
}
int main() {
char a[101];
scanf("%s", a);
int i = 0;
TreeNode* newtree = MakeTree(a, &i);
Inorder(newtree);
return 0;
}
(八)翻转二叉树
struct TreeNode* invertTree(struct TreeNode* root) {
if(root == NULL)
{
return NULL;
}
//invertTree返回值是交换之后的节点
struct TreeNode* left = invertTree(root->left);
struct TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
(九)平衡二叉树
// int getTreeHeight(struct TreeNode* root)//这个太慢,超出时间限制
// {
// if (root == NULL)
// return 0;
// return getTreeHeight(root->left) > getTreeHeight(root->right)?getTreeHeight(root->left)+1:getTreeHeight(root->right)+1;
// }
int getTreeHeight(struct TreeNode* root)
{
if (root == NULL)
return 0;
int leftHeight = getTreeHeight(root->left);
int rightHeight = getTreeHeight(root->right);
return (leftHeight>rightHeight?leftHeight:rightHeight)+1;
}
bool isBalanced(struct TreeNode* root) {
if(root == NULL)
return true;
//左右子树高度差小于等于1
return abs(getTreeHeight(root->left) - getTreeHeight(root->right)) <= 1 &&
isBalanced(root->left) && isBalanced(root->right);
}