力扣刷题日记之二叉树(不定时更新)

文章讲述了平衡二叉树的概念、递归求解二叉树高度的方法,以及在二叉搜索树中查找众数和将二叉搜索树转换为累加树的技巧,涉及中序遍历、前序遍历和反中序遍历的应用。
摘要由CSDN通过智能技术生成

二叉树

平衡二叉树

题目例子
输入: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]

有了上面基本了解,下面直接开始解题
解题步骤

  1. 设置前一个节点的值
  2. 设置递归终止条件
  3. 反中序遍历
  4. 确定遍历时逻辑
  5. 返回 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♪(・ω・)ノ

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值