【代码随想录】Day 21 二叉树Ⅴ(BST最小差、众数、最小公共祖先)

第一题:

力扣

已AC,但是用的比较笨的vector存每个节点值,然后sort排序,再遍历一遍排序后的vector求出mindiff,无语死了,一直理解成了与节点值最小的差值,搞得绕了了老半天:

class Solution {
public:
    vector<int> val;

    void travel(TreeNode* node) {
        if (node == nullptr) return;
        travel(node->left);
        val.push_back(node->val);
        travel(node->right);
    }

    int getMinimumDifference(TreeNode* root) {
        travel(root);
        int diff = INT32_MAX;
        sort(val.begin(), val.end());
        for (int i = 1; i < val.size(); i++) {
            if ((val[i] - val[i-1]) < diff) {
                diff = val[i] - val[i-1];
            }
        }
        return diff;
    }
};

学习记录:

服了,看到题解才知道原来是一棵二叉搜索树,那sort都不用了,而且也不用vector存这么复杂了,直接node和node->left比一下差值,再和node->right比一下差值,返回这俩中小的一个就行了:

class Solution {
private:
int result = INT_MAX;
TreeNode* pre = NULL;
void traversal(TreeNode* cur) {
    if (cur == NULL) return;
    traversal(cur->left);   // 左
    if (pre != NULL){       // 中
        result = min(result, cur->val - pre->val);
    }
    pre = cur; // 记录前一个
    traversal(cur->right);  // 右
}
public:
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;
    }
};

遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点。同时要学会在递归遍历的过程中如何记录前后两个指针!!!

第二题:

力扣

想到的是用map记录val以及val出现的次数,但是对map排序又要把它转成vector排序,觉得很麻烦,所以没有写,代码随想录上写的普通二叉树的代码和我的思路一样:

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;
    }
};

但是这道题求的是二叉搜索树,二叉搜索树是有序的,所以可以用中序遍历做, 记录一下当前的count和最大的count。其实自己也想到了这个方法,但是有点绕,一时没捋清楚所以没写,后面看了一点代码随想录以后写的如下:

class Solution {
public:
    int maxCount = 1;
    int count = 1;
    TreeNode* pre = nullptr;
    vector<int> res;

    void traversal(TreeNode* node) {
        if (node == nullptr) return;
        traversal(node->left);
        if (pre != nullptr) {
            if (node->val == pre->val) { //当前值=前个值
                count++;
                }
            else { //当前值≠前个值
                count = 1; //count重置
                }
            if (count > maxCount) { //maxCount重置
                    maxCount = count;
                    res.clear(); //结果数组重置
                    res.push_back(pre->val); //存入新的结果
                }
            else if (count == maxCount) { //存入多个众数
                    res.push_back(node->val);
            }

        }
        else res.push_back(node->val); //如果只有一个根节点,把自己存进去
        pre = node;
        traversal(node->right);
    }

    vector<int> findMode(TreeNode* root) {
        traversal(root);
        return res;
    }
};

 代码随想录写的和我写的有一点区别在于,我将所有操作都是放在if (pre != null)里的,而他是只写关于count的操作,剩下的和maxCount的比较是放在if (pre)外面的。

class Solution {
private:
    int maxCount = 0; // 最大频率
    int count = 0; // 统计频率
    TreeNode* pre = NULL;
    vector<int> result;
    void searchBST(TreeNode* cur) {
        if (cur == NULL) return ;

        searchBST(cur->left);       // 左
                                    // 中
        if (pre == NULL) { // 第一个节点
            count = 1;
        } else if (pre->val == cur->val) { // 与前一个节点数值相同
            count++;
        } else { // 与前一个节点数值不同
            count = 1;
        }
        pre = cur; // 更新上一个节点

        if (count == maxCount) { // 如果和最大值相同,放进result中
            result.push_back(cur->val);
        }

        if (count > maxCount) { // 如果计数大于最大值频率
            maxCount = count;   // 更新最大频率
            result.clear();     // 很关键的一步,不要忘记清空result,之前result里的元素都失效了
            result.push_back(cur->val);
        }

        searchBST(cur->right);      // 右
        return ;
    }

public:
    vector<int> findMode(TreeNode* root) {
        count = 0;
        maxCount = 0;
        TreeNode* pre = NULL; // 记录前一个节点
        result.clear();

        searchBST(root);
        return result;
    }
};

 第三题:

力扣

 思路是找到从根节点到节点p和q的路径,路径上每一个节点都存入vector<TreeNode*> path中,然后对两条path取交集,找到交集中最后的那一个,就是最近公共祖先,已AC:

class Solution {
public:
    vector<TreeNode*> res;
    vector<TreeNode*> path;

    bool findAncestor(TreeNode* node, TreeNode* p) { //bool类型,只有当node==要找的节点时才返回true,并且将路径存入res,否则都是false
        if (node == NULL) {
            return false;
        }
        path.push_back(node);
        if (node == p) {
            res = path;
            return true;
        }
        bool left = findAncestor(node->left, p);
        bool right = findAncestor(node->right, p);
        path.pop_back();
        return left || right;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == NULL || p == NULL || q == NULL) return NULL;
        findAncestor(root, p);
        vector<TreeNode*> p_Ancestor = res;
        res.clear();
        path.clear();
        findAncestor(root, q);
        vector<TreeNode*> q_Ancestor = res;
        TreeNode* result = NULL;
        for (int i = 0; i < min(p_Ancestor.size(), q_Ancestor.size()); i++) {
            if (p_Ancestor[i] == q_Ancestor[i]) result = p_Ancestor[i];
        }
        return result;
    }
};

学习记录:

如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。

判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。

递归三部曲:

  1. 递归函数返回值TreeNode*,参数TreeNode* node,TreeNode* p,TreeNode* q;
  2. 终止条件:当node == null, return null;
  3. 单层递归逻辑,当node == p || node == q,return p || q;

TIPS:如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树呢?

搜索一条边的写法:

if (递归函数(root->left)) return ;

if (递归函数(root->right)) return ;

搜索整个树写法:

left = 递归函数(root->left);  // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理;         // 中 

在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)

这代码有点东西,结合图看更容易理解:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == p || root == q || root == NULL) return root;
        //后序遍历
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left != NULL && right != NULL) return root;//说明p和q都找到了

        if (left == NULL && right != NULL) return right;
        else if (right == NULL && left != NULL) return left;
        else return NULL;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值