二叉树
平衡二叉树
输入:root = [3,9,20,null,null,15,7]
输出:true
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
什么是平衡二叉树?
对于任意节点,其左子树和右子树的高度差的绝对值不超过1(即平衡因子的绝对值不超过1)。
其左子树和右子树都是平衡二叉树。
换句话说,平衡二叉树是一个空树或者具有以下性质的二叉树:
- 它的左子树和右子树的高度差的绝对值不超过1。
- 它的左子树和右子树都是平衡二叉树。
思路:
计算高度, 我们要知道高度和深度的区别, 这里我直接说结论
求高度一般使用 后序遍历 ( 左右中 ) 的处理顺序
递归基本步骤:
① 确定终止条件
② 单层遍历代码
③ 处理逻辑
确定终止条件:
if (node == NULL) {
return 0;
}
单层遍历代码以及处理逻辑
// 左边
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
// 右边
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
// 中
// 如果 两边相差大于 1 就就返回 -1 返回 1 + 最大值
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
看到这里, 可能有同学还是不理解, 别走, 后面我对上面的代码进行逐步分析 ( 包教会哈 )
完整代码
class Solution {
public:
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
int getHeight(TreeNode* node) {
// 终止条件
if (node == NULL) {
return 0;
}
// 左边
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
// 右边
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
// 中
// 如果 两边相差大于 1 就就返回 -1 返回 1 + 最大值
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
};
关键点分析
Q1: 请问这个代码运行过程是什么样的, 递归理解不了(初学者)?
下面我用上面第二个例子进行逐步分析代码运行
第一步:
代码运行
int leftHeight = getHeight(node->left);
到达 4 节点
// 左边
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
// 右边
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
因为 4 节点 左右孩子都没有
所以在下面的 getHeight(node->left) 直接返回 0 ;
此时, leftHeight 为 0 , rightHeight 也为 0
下面代码返回值为 1
也就是 节点4的 高度
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
Q2: 为什么要用 max(…) ?
我和你说一个例子就知道了
比如节点2, 节点2的高度为 3
请问这个 3 是如何计算出来的呢?
① 计算出左子树的高度
② 计算出右子树高度
③ 取最大值 + 1
二叉搜索树中众数
题目描述
重点: 二叉搜索树的特点: 中序遍历就是一个有序数组
知道上面那个知识点, 这道题就很简单。中序遍历得到有序的数组, 然后通过遍历方式找到众数加入result数组
中序遍历
void searchBtn(TreeNode *cur,vector<int>& nums) {
// 终止条件
if(cur == nullptr) {
return;
}
searchBtn(cur->left,nums);
// 中
nums.push_back(cur->val);
// 右边
searchBtn(cur->right,nums);
}
遍历找众数
思路:
① 设某数字频数为 count , 最大频数为 maxcount
② 遍历
对比**前一个数** 以及 **当前这个数**
- 如果相等就 count++
- 如果不相等 count = 1
对比 count 以及 maxcount
- 如果 count == maxcount 就 添加该数字到 **result** 数组
- 如果 count > maxcount 就清空 **result** 数组, 同时更新maxcount,并加入到 **result** 中
Code
vector<int> findMode(TreeNode* root) {
int value = 0;
int count = 0;
int maxCount = 0;
vector<int> nums;
vector<int> result;
if(root == nullptr) {
return result;
}
// 调用函数
searchBtn(root,nums);
for(int i= 0; i< nums.size(); i++) {
if(i == 0 || nums[i] != nums[i-1]) {
count = 1;
} else {
count++;
}
if(count == maxCount) {
result.push_back(nums[i]);
} else if(count > maxCount) {
// 清空数组
result.clear();
result.push_back(nums[i]);
// 更新最大频数
maxCount = count;
}
}
return result;
}
完整Code
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void searchBtn(TreeNode *cur,vector<int>& nums) {
// 终止条件
if(cur == nullptr) {
return;
}
searchBtn(cur->left,nums);
// 中
nums.push_back(cur->val);
// 右边
searchBtn(cur->right,nums);
}
vector<int> findMode(TreeNode* root) {
int value = 0;
int count = 0;
int maxCount = 0;
vector<int> nums;
vector<int> result;
if(root == nullptr) {
return result;
}
// 调用函数
searchBtn(root,nums);
for(int i= 0; i< nums.size(); i++) {
if(i == 0 || nums[i] != nums[i-1]) {
count = 1;
} else {
count++;
}
if(count == maxCount) {
result.push_back(nums[i]);
} else if(count > maxCount) {
// 清空数组
result.clear();
result.push_back(nums[i]);
// 更新最大频数
maxCount = count;
}
}
return result;
}
};
下面介绍一下, 如果不是二叉搜索树, 仅仅是一个普通的二叉树, 如何解决呢?
通过遍历,然后通过 unordered_map<int,int> map 记录每个元素出现次数, 然后遍历找出出现最多的值加入到 result 中
遍历+哈希
void searchBST(TreeNode* cur, unordered_map<int,int>& map ) {
if(cur == nullptr) {
return;
}
// 中左右
map[cur->val]++;
searchBST(cur->left,map);
searchBST(cur->right,map);
}
一次遍历找出最大频数, 二次遍历找出所有众数
vector<int> findMode(TreeNode* root) {
vector<int> result;
if(root == nullptr) {
return result;
}
unordered_map<int,int> map;
searchBST(root,map);
int Max_Num = 0;
for(auto it:map) {
if(it.second > Max_Num) {
Max_Num = it.second;
}
}
// 遍历
for(auto it: map) {
if(it.second == Max_Num) {
result.push_back(it.first);
}
}
return result;
}
完整Code
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void searchBST(TreeNode* cur, unordered_map<int,int>& map ) {
if(cur == nullptr) {
return;
}
// 中左右
map[cur->val]++;
searchBST(cur->left,map);
searchBST(cur->right,map);
}
vector<int> findMode(TreeNode* root) {
vector<int> result;
if(root == nullptr) {
return result;
}
unordered_map<int,int> map;
searchBST(root,map);
int Max_Num = 0;
for(auto it:map) {
if(it.second > Max_Num) {
Max_Num = it.second;
}
}
// 遍历
for(auto it: map) {
if(it.second == Max_Num) {
result.push_back(it.first);
}
}
return result;
}
};
可能你会对上面的做法有点疑问, 干嘛不直接对map进行排序???
但是
有的同学可能可以想直接对map中的value排序,还真做不到,C++中如果使用std::map或者std::multimap可以对key排序,但不能对value排序。
所以要把map转化数组即vector,再进行排序,当然vector里面放的也是pair<int, int>类型的数据,第一个int为元素,第二个int为出现频率。
转化成 vector
vector<pair<int, int>> vec(map.begin(), map.end());
定义排序规则
bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
return a.second > b.second;
}
排序
sort(vec.begin(), vec.end(), cmp);
完整Code
class Solution {
private:
void searchBST(TreeNode* cur, unordered_map<int, int>& map) { // 前序遍历
if (cur == NULL) return ;
map[cur->val]++; // 统计元素频率
searchBST(cur->left, map);
searchBST(cur->right, map);
return ;
}
bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
return a.second > b.second;
}
public:
vector<int> findMode(TreeNode* root) {
unordered_map<int, int> map; // key:元素,value:出现频率
vector<int> result;
if (root == NULL) return result;
searchBST(root, map);
vector<pair<int, int>> vec(map.begin(), map.end());
sort(vec.begin(), vec.end(), cmp); // 给频率排个序
result.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i++) {
// 取最高的放到result数组中
if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
else break;
}
return result;
}
};
538.二叉树搜索树转换为累加树
测试数据一:
题目
题意
每一个节点修改成大于等于改数字所有数字之和
看到二叉搜索树一定要想一下中遍历可以输出有序遍历这个性质
把上面的数组修改成有序数组
[ 0,1 ,2 ,3, 4,5,6,7,8 ]
按照题目意思可以得到 [36,36,35,33,30,26,21,15,8]
考虑遍历方式
常见遍历有: 前序, 后序, 中序
这题目我们用中序可以实现遍历升序数字
但是题目要求我们得到是大于等于该数字的所有数字之和, 所以反中序遍历 非常方便
也就是 右 中 左
输出来就是降序数组 [13,5,2]
有了上面基本了解,下面直接开始解题
解题步骤
- 设置前一个节点的值
- 设置递归终止条件
- 反中序遍历
- 确定遍历时逻辑
- 返回 root
- 设置前一个节点的值
int pre = 0;
void travel(TreeNode* root)
- 终止条件 + 遍历逻辑
void travel(TreeNode* root ) {
if(root == nullptr) {
return;
}
// 右中左 ==> 得到降序数组
travel(root->right);
root->val += pre;
// 刚刚开始最右端数字是 root->val = 8 , pre 可以看作是 0
pre = root->val; // 记录前一个节点数值
travel(root->left);
}
完整Code
class Solution {
public:
int pre = 0; // 记录前一个节点的值
void travel(TreeNode* root ) {
if(root == nullptr) {
return;
}
// 右中左 ==> 得到降序数组
travel(root->right);
root->val += pre;
pre = root->val;
travel(root->left);
}
TreeNode* convertBST(TreeNode* root) {
if(root == nullptr) {
return root;
}
// 调用函数
travel(root);
return root;
}
};
最后, 如果有错, 欢迎指正, 本人也是初学者哈。如果对刷题感兴趣的话, 可以关注一下, 督促一下我每天做算法题哈, 感谢Thanks♪(・ω・)ノ