day21 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

目录:

解题及思路学习

530. 二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

!https://assets.leetcode.com/uploads/2021/02/05/bst1.jpg

输入:root = [4,2,6,1,3]
输出:1

思考:因为是二叉搜索树,所以任意两个不同节点最小差值只会出现在相邻节点。迭代法和递归方法。

递归方法:

class Solution {
public:
    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);
    }

    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;
    }
};

迭代法

class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        int result = INT_MAX;
        TreeNode* pre = NULL;
        TreeNode* cur = root;
        stack<TreeNode*> st;
        while(cur != NULL || !st.empty()) {
            if (cur != NULL) {
                st.push(cur);
                cur = cur->left;
            } else {
                cur = st.top();
                st.pop();
                if (pre != NULL) {
                    result = min(result, cur->val - pre->val);
                }
                pre = cur;
                cur = cur->right;
            }
        }
        return result;
    }
};

501.二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

示例 1:

!https://assets.leetcode.com/uploads/2021/03/11/mode-tree.jpg

输入:root = [1,null,2,2]
输出:[2]

思考:暴力方法:用一个map记录所有数字的出现次数,再找出众数。

其他方法,二叉搜索树在遍历的过程中,对于同一个数字总是一直持续的。所以,可以用一个count来记录重复数字出现的次数。然后返回的vector数组中,如果遇到的次数比之前的都大,则清空并存入最大的。如果一样大,则继续存入。(第一个节点没考虑到,maxCount更新没考虑到)

class Solution {
private:
    int maxCount = 0;
    int count = 0;
    vector<int> result;
    TreeNode* pre = NULL;
    void searchBST(TreeNode* cur) {
        if (cur == NULL) return;

        searchBST(cur->left);
        if (pre == NULL) count = 1;
        if (pre != NULL && cur->val == pre->val) count++;
        else count = 1;
        pre = cur;
        if (count == maxCount) result.push_back(cur->val);
        if (count > maxCount) {
            maxCount = count;
            result.clear();
            result.push_back(cur->val);
        }
        searchBST(cur->right);
        return;
    }

public:
    vector<int> findMode(TreeNode* root) {
        result.clear();
        searchBST(root);
        return result;
    }
};

一般二叉树的统计方法:用unordered_map来统计:

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;
        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++) {
            if (vec[i].second  == vec[0].second) result.push_back(vec[i].first);
            else  break;
        }
        return result;
    }
};

迭代遍历的逻辑一样,只是用的是st来进行遍历。

class Solution {
private:
public:
    vector<int> findMode(TreeNode* root) {
        int maxCount = 0;
        int count = 0;
        TreeNode* pre = NULL;
        TreeNode* cur = root;
        vector<int> result;
        stack<TreeNode*> st;
        while(cur != NULL || !st.empty()) {
            if (cur != NULL) {
                st.push(cur);
                cur = cur->left;
            } else {
                cur = st.top();
                st.pop();
                if (pre == NULL) count = 1;
                if (pre != NULL && cur->val == pre->val) count++;
                else count = 1;
                if (count == maxCount) result.push_back(cur->val);
                if (count > maxCount) {
                    maxCount = count;
                    result.clear();
                    result.push_back(cur->val);
                }
                pre = cur;
                cur = cur->right;
            }
        }
        return result;
    }
};

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

例 1:

!https://assets.leetcode.com/uploads/2018/12/14/binarytree.png

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3

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

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == NULL || root == p || root == q) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left != NULL && right != NULL) return root;
        if (left == NULL && right != NULL) return right;
        if (left != NULL && right == NULL) return left;
        else {
            return NULL;
        }
    }
};

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

归纳:

  1. 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。
  2. 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
  3. 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。

复盘总结

个人反思

二叉搜索树的特性,经常和双指针法一起使用。

迭代法和递归法的一般写法。

你可能感兴趣的:(训练营二刷,C++,LeetCode,刷题,leetcode,数据结构,c++,算法)