完全二叉树、搜索二叉树、平衡二叉树和满二叉树(C++)
完全二叉树是这样定义的:若二叉树的深度为 h,除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的叶子结点都连续集中在最左边,这就是完全二叉树。(第 h 层可能包含 [1~2h] 个节点)上面的定义看起来比较复杂。所有节点只存在3种情况:1、有左右2个孩子;2、只有左孩子;3、没有孩子(叶节点),即不存在只有右孩子的情况;我们采用层序遍历的方式遍历节点,当遍历到某一个节点,它只有左孩子
一、完全二叉树
1、什么是完全二叉树
完全二叉树是这样定义的:若二叉树的深度为 h,除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的叶子结点都连续集中在最左边,这就是完全二叉树。(第 h 层可能包含 [1~2h] 个节点)
上面的定义看起来比较复杂。事实上,完全二叉树的节点满足如下规律:
- 所有节点只存在3种情况:1、有左右2个孩子;2、只有左孩子;3、没有孩子(叶节点),即不存在只有右孩子的情况;
- 我们采用层序遍历的方式遍历节点,当遍历到某一个节点,它只有左孩子,或者没有孩子,则后序所有节点一定都是叶节点。
如下所示是完全二叉树
2、怎么判断是否是完全二叉树
根据上述规律,我们可以设计算法:采用层序(广度优先)方法遍历节点,每一个节点都要满足上诉条件1。当遍历到某个节点不是有左右孩子时,开始检测后面节点是否全部是叶节点,如果满足,则是完全二叉树。
至于怎么写层序遍历,可以参考下面文章二叉树的宽度(广度)优先遍历C++_二叉树广度优先遍历c语言-CSDN博客
代码如下
bool IsCST(TreeNode* root)
{
bool flag = false; //一个flag,记录已经遍历到不是左右孩子双全的节点
if (root == nullptr)
{
return true;
}
std::queue<TreeNode*> node_que;
node_que.push(root);
while(!node_que.empty())
{
// 弹出队首节点
TreeNode* node = node_que.front();
node_que.pop();
// flag为true
if(flag)
{
// 条件二:flag是true,存在任何孩子都不是完全二叉树
if(node->left || node->right) return false;
}
else
{
if (node->left)
{
// 左孩子入队
node_que.push(node->left);
//条件二:有左孩子没有右孩子,则flag置true
if (!node->right)
{
flag = true;
}
else
{
// 右孩子入队
node_que.push(node->right);
}
}
else
{
// 条件一:无左孩子但有右孩子,不是完全二叉树
if(node->right) return false;
// 条件二:无左孩子无右孩子,则flag同样置true
flag = true;
}
}
}
return true;
}
二、搜索二叉树
1、什么是搜索二叉树
一个二叉搜索树要满足如下条件:
- 节点的左子树只包含 小于 当前节点的数
- 节点的右子树只包含 大于 当前节点的数
- 所有左子树和右子树自身必须也是二叉搜索树。
空树也是二叉搜索树。
例如下面是一个二叉搜索树:
2、怎么判断是否是搜索二叉树
二叉搜索树要求任意节点的左子树都要比该节点小,而右子树比该节点大。那么,我们如果采用中序遍历的方式遍历一个二叉搜索树,理应得到一个严格递增的数列。所以我们可以考虑结合中序遍历的算法实现对搜索二叉树的判断。
我们改变一下中序遍历的代码,就可以得到如下算法。
int s_val = 0; // 记录上一个节点的值
bool b_first = true; //记录是不是中序第一个节点
bool IsBST(TreeNode *root) {
// 根节点为空,直接返回
if (nullptr == root) {
return true;
}
// 1)判断左子树是不是搜索二叉树
if (!IsBST(root->left)) {
return false;
}
// 比较当前值与左子树最后一个值
if (!b_first && root->val < s_val) {
return false;
}
else {
s_val = root->val;
b_first = false;
}
// 3)判断右子树是不是搜索二叉树
return IsBST(root->right);
}
三、平衡二叉树
1、什么是平衡二叉树
一棵平衡二叉树必须要满足如下条件:
- 每个节点左右两个子树的高度差不超过1
- 每个左子树和右子树自身必须也是平衡二叉树
空树也是平衡二叉树。
例如下图就是一棵平衡二叉树
2、怎么判断是否是平衡二叉树
根据平衡二叉树的性质,我们只要判断左右子树分别是平衡二叉树,并且左右子树的高度差在1以内即可。
我们很自然的想到用递归法求解。在递归中我们需要提取2个信息:一是子树是否是平衡的;二是子树的高度,方便返回判断是否和兄弟子树的高度相差在1以内。
代码如下
struct Result{
int height; //树的高度
bool is_balance; //是否平衡
Result(int l=0, bool is_b=true):height(l), is_balance(is_b){}
};
Result IsBalanceTree(TreeNode* root)
{
if (root == nullptr)
{
return Result();
}
Result ret_left = IsBalanceTree(root->left);
Result ret_right = IsBalanceTree(root->right);
int lev = std::max(ret_left.height, ret_right.height) + 1;
int lv_diff = std::abs(ret_left.height - ret_right.height);
if (!ret_left.is_balance || !ret_right.is_balance || lv_diff > 1)
{
return Result(lev, false);
}
return Result(lev, true);
}
四、满二叉树
1、什么是满二叉树
如果一棵树除最后一层无任何子节点外,每一层上的所有节点都有两个子节点,则这棵树是满二叉树。
2、怎么判断是否是满二叉树
假设根节点所在是1层,则一棵满二叉树的每k层共有2^(k-1)个节点,一棵满二叉树的总结点个数为(2^k) -1。根据这个数学特征,我们采用层序遍历的方法可以很方便地判断是否是满二叉树。
代码如下
bool IsFullTree(TreeNode* root)
{
if (root == nullptr) return true;
std::queue<TreeNode*> node_que;
std::unordered_map<TreeNode*, int> node_lv; //记录每个节点所在层数
int cur_lv = 1; //记录当前遍历到的层数
int node_count = 0; // 当前层节点计数
node_que.push(root);
node_lv[root] = 1;
while(!node_que.empty())
{
// 弹出队首节点
TreeNode* node = node_que.front();
node_que.pop();
//换层
if (node_lv[node] != cur_lv)
{
// 计算上一层是否满足条件,不满足数学条件,则返回false
if(node_count != pow(2, cur_lv-1)) return false;
// 重置此层节点计数
node_count = 0;
cur_lv = node_lv[node];
}
node_count++;
// 左孩子进队列
if (node->left)
{
node_que.push(node->left);
node_lv[node->left] = cur_lv + 1;
}
// 右孩子进队列
if (node->right)
{
node_que.push(node->right);
node_lv[node->right] = cur_lv + 1;
}
}
//检测最后一层是否满足节点个数条件
if(node_count != pow(2, cur_lv-1)) return false;
return true;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)