1. LeetCode第110题—平衡二叉树
链接: link.
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 为理解本题的重要点。
这题有两种解题思路①相当于前序遍历的思想。把它分为当前树和左子树右子树,写出当前树的思路然后对左子树和右子树实现递归即可。如果当前树的左子树和右子树都不满足平衡二叉树的条件那么就直接可以返回false,然后再开始判断左子树中的每个子树是否满足平衡二叉树的定义,总之要把每一结点都看作一颗二叉树根结点,来判断是否满足平衡二叉树的定义。
int TreeDepth(struct TreeNode* root)
{
if(root == NULL)
return 0;
//为的就是消除代码冗余
int leftDepth = TreeDepth(root->left);
int rightDepth = TreeDepth(root->right);
//求出左右子树较大的哪一个
return leftDepth > rightDepth? leftDepth+1 :rightDepth+1;
}
bool isBalanced(struct TreeNode* root){
if(root == NULL)
return true;
//这样重新定义一个变量的目的就是消除代码的冗长
int gap = TreeDepth(root->left)-TreeDepth(root->right);
if(abs(gap) > 1)
return false;
return isBalanced(root->left) && isBalanced(root->right);
}
但是你会发现这样自来求平衡二叉树时间复杂介于O(N)~O(N*N)之间,只要遍历时间复杂度就是O(N),当你一进来遍历发现左右子树就不满足平衡二叉树的情况,但是当你一直遍历直到右子树的最后一颗子树不满足平衡二叉树的定义的时候基本上时间复杂度的计算满足一个等差数列,差不多是O(N*N),**那么能不能优化呢?**你会发现前序遍历的这种思想,会存在大量的重复计算树的高度的过程。所以这里使用后续遍历更加优化,你进来就判断左树,计算出左树中的最后一个左子树的高度然后返回真假,在计算右树同样的过程,那么最后你会发现你的每一个结点所看成一颗树的高度都是只计算了一次,没有重复的计算。
//一定要记住在你返回true的同时要返回一个高度值
bool _isBalanced(struct TreeNode* root,int* pDepth)
{
//此时需要的是相当于后续遍历
//先找到你最左边的树,再试最右边的树,在满足的同时返回true
if(root == NULL)
{
*pDepth = 0;
return true;
}
else
{
int leftDepth = 0;
if(_isBalanced(root->left,&leftDepth) == false)
return false;
int rightDepth = 0;
if(_isBalanced(root->right,&rightDepth) == false)
return false;
//走到这里说明左树和右树中所有的结点所对应小子树都是满足平衡二叉树的条件
if(abs(leftDepth-rightDepth) > 1)
return false;
*pDepth = leftDepth > rightDepth?leftDepth+1:rightDepth+1;
return true;
}
}
bool isBalanced(struct TreeNode* root){
int depth = 0;
return _isBalanced(root,&depth);
}
2. 牛客网—二叉树遍历
链接: link.
根据前序遍历且输入为abc##de#g##f##可以画出二叉树的图。
#include<stdio.h>
#include<stdlib.h>
typedef struct TreeNode {
char val;
struct TreeNode *left;
struct TreeNode *right;
}TreeNode;
TreeNode* CreateTree(char* str,int* pi)
{
if(str[*pi] == '#')
{
(*pi)++;
return NULL;
}
else
{
TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
root->val = str[*pi];
//构建左右子树
(*pi)++;
root->left = CreateTree(str,pi);
root->right = CreateTree(str,pi);
return root;
}
}
void InOrder(TreeNode* root)
{
if(root == NULL)
return;
InOrder(root->left);
printf("%c ",root->val);
InOrder(root->right);
}
int main()
{
char str[100];
scanf("%s ",str);
int i = 0;
TreeNode* root = CreateTree(str, &i);
InOrder(root);
return 0;
}
牛客网不向LeetCode一样是需要自己来包含头文件等内容的,由于画图盘不太会使用,只能采用截图的办法来大致的进行分析这个递归的过程。下图是构建左树的过程分析。
3. 层序的实现
层序的实现需要借助队列的性质,先进先出。
这里也需要借助原先写的Queue.h和Queue.c文件,这里就省略了,但是在你的Queue.h中需要加入BTNode*的文件申明
Queue.h
//相当于把你的struct BinaryTreeNode拿过来申明一下
extern struct BinaryTreeNode;
typedef struct BinaryTreeNode* QDateType;
//这里可以把BTNode的定义放到这里,但是这是一个Queue的头文件,有点挫
//二叉树的层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
//在这里所定义的队列里面放的可不能只是值,那么你就在他出的时候找不到他的左右孩子入队列了
Queue q;
QueueInit(&q);
if (root == NULL)
return;
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
//这里的Pop只是把这个数据从队列里面拿出来了,并没有删除
QueuePop(&q);//从这个队列里面把这个结点拿出来
printf("%c ", front->_date);
if (front->_left)
{
QueuePush(&q, front->_left);
}
if (front->_right)
{
QueuePush(&q, front->_right);
}
}
QueueDestory(&q);
printf("\n");
}
3.1 是否是完全二叉树
这个题借助上面的思想,完全二叉树和非完全二叉树的区别就是:
在层序遍历中
如果你是完全二叉树那么你的结点都是聚集在前面的(结点和你的NULL是明显分割开的)
但是如果你是非完全二叉树你会发现,你的结点中间可能包含了NULL
//判断二叉树是否是一颗完全二叉树
//这个题借助上面的思想,完全二叉树和非完全二叉树的区别就是:
//在层序遍历中
//如果你是完全二叉树那么你的结点都是聚集在前面的(结点和你的NULL是明显分割开的),
//但是如果你是非完全二叉树你会发现,你的结点中间可能包含了NULL
int BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root == NULL)
return 1; //是就返回1,不是返回0
QueuePush(&q, root);
//如果是完全二叉树,那么你的结点都出队列之后,队列就是NULL的
//如果是非完全二叉树,当你遇见一个break之后,你去检查队列,你会发现队列是不为NULL的
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)
{
QueueDestory(&q);
return 0;
}
}
//一定要记住放置内存泄漏,要对其所创建的队列进行释放
QueueDestory(&q);
return 1;
}