力扣二叉搜索树专题

刷完了二叉树,我们接着在二叉搜索树的路上努力~
在这里插入图片描述

二叉搜索树属性

二叉搜索树中的搜索

LC 第700题

迭代法
TreeNode* searchBST(TreeNode* root, int val) {
   if(!root) return NULL;
   while(root != NULL){
       if(val > root->val) root = root->right;
       else if(val < root->val) root = root->left;
       else break;
   }
   return root ? root : NULL;
}
递归法
TreeNode* searchBST(TreeNode* root, int val) {
    if(!root || root->val == val) return root;
    if(val > root->val) return searchBST(root->right, val);//若函数有返回值,这里一定要return
    if(val < root->val) return searchBST(root->left, val);
    return NULL;
}

没什么好说的,就是注意root != NULL的判断,必须非空才可以用root -> val

验证二叉搜索树

LC 第98题

陷入了一个陷阱:以为只要当前结点的左结点<当前值,当前结点的右结点>当前值就可以了,但是要求的是当前结点的左子树上所有值均<当前值,当前结点的右子树上的所有值均>当前值呀。想过求左子树的最大值和右子树的最小值。但有更巧妙的方法——只要中序遍历然后判读数组是否有序即可了呀。(这都没想到果然是迷糊中)

递归法
vector<int> res;
void solve(TreeNode* root){
    if(root->left) solve(root->left);
    res.push_back(root->val);
    if(root->right) solve(root->right);
}
bool isValidBST(TreeNode* root) {
    solve(root);
    int s = res.size();
    for(int i = 0; i < s - 1; i++){
        if(res[i+1] <= res[i]) return false;
    }
    return true;
}

或者不必开数组,直接在中序时候就判断了(顺手记录前一个,拿来和当前值比较)
不可判断root->left时就return,因为一颗树为搜索二叉树那么其左右子树都是搜索二叉树。

TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
    if(!root) return true;
    bool left =  isValidBST(root->left);
    if(pre && pre->val >= root->val) return false;
    pre = root;
    bool right =  isValidBST(root->right);
    return left && right;
}
迭代法

用栈模拟中序遍历?忘得干干净净了(因为当时并没有理解透彻)

注意:用栈模拟中序遍历一开始是不需要把根结点push进去的(因为中序并不是从跟结点开始的)

bool isValidBST(TreeNode* root) {
    stack<TreeNode*> st;
    TreeNode* cur = root;
    TreeNode* pre = NULL;
    while(!st.empty() || cur){
        if(cur){
            st.push(cur);
            cur = cur->left;
        }else{
            cur = st.top();
            st.pop();
            if(pre && pre->val >= cur->val) return false;
            pre = cur;
            cur = cur->right;
        }
    }
    return true;
}

二叉搜索树的最小绝对差

LC 第530题

递归法
vector<int> v;
int res = INT_MAX;
void solve(TreeNode* root){
    if(root->left) solve(root->left);
    v.push_back(root->val);
    if(root->right) solve(root->right);
}
int getMinimumDifference(TreeNode* root) {
    solve(root);
    int s = v.size();
    for(int i = 0; i < s-1; i++) res = min(res, v[i+1] - v[i]);
    return res;
}

或者不必开数组,在中序时顺便记录前一结点的值。
注意:这个pre指针须在全局处定义

int res = INT_MAX;
TreeNode* pre = NULL;
void solve(TreeNode* cur){
    if(cur->left) solve(cur->left);
    if(pre) res = min(res, cur->val - pre->val);
    pre = cur;
    if(cur->right) solve(cur->right);
}
int getMinimumDifference(TreeNode* root) {
    solve(root);
    return res;
}
迭代法

继续复习用栈模拟中序

注意一开始并不把根结点压栈

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

总结

二叉搜索树求最值、求差值之类,都要考虑到二叉树是有序的,利用好这一特点。

二叉搜索树中的众数

LC 第501题

递归法
map<int, int> count;
int max_count;
void solve(TreeNode* root){
    if(root->left) solve(root->left);
    count[root->val]++;
    max_count = max(max_count, count[root->val]);
    if(root->right) solve(root->right);
}
vector<int> findMode(TreeNode* root) {
    solve(root);
    vector<int> v;
    map<int,int>::iterator iter;
    for(iter = count.begin(); iter != count.end(); iter++){
        if(iter->second == max_count) v.push_back(iter->first);
    }
    return v;
}

或者只需遍历一遍,还是老套路,记录之前的结点,由于中序遍历二叉搜索树是有序的,所以如果是众数必定集中在相邻区域。

vector<int> res;
int max_count, count;
TreeNode* pre = NULL;
void solve(TreeNode* cur){
    if(cur->left) solve(cur->left);
    if(!pre) count = 1; //第一个结点
    else if(pre->val == cur->val) count++;
    else count = 1;
    pre = cur;
    if(count == max_count) res.push_back(cur->val);
    if(count > max_count){
        max_count = count;
        res.clear();
        res.push_back(cur->val);
    }
    if(cur->right) solve(cur->right);
}
vector<int> findMode(TreeNode* root) {
    solve(root);
    return res;
}
迭代法
vector<int> res;
int max_count, count;
vector<int> findMode(TreeNode* root) {
   stack<TreeNode*> st;
   TreeNode* cur = root;
   TreeNode* pre = NULL;
   while(!st.empty() || cur){
       if(cur){
           st.push(cur);
           cur = cur->left;
       }else{
           cur = st.top(); st.pop();
           if(pre == NULL) count = 1;
           else if(pre->val == cur->val) count++;
           else count = 1;
           pre = cur;
           if(count == max_count) res.push_back(cur->val);
           if(count > max_count){
               max_count = count;
               res.clear();
               res.push_back(cur->val);
           }
           cur = cur->right;
       }
   }
   return res;
}

二叉树的最近公共祖先

LC 第236题

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 && right) return root;
    else if(left) return left;
    else if(right) return right;
    else return NULL;
}

在这里插入图片描述

此题解法甚妙!我只有抚掌称赞的份儿了!
自己在那里搞什么中序遍历、取中间元素搞得满头大汗的,果然又剑走偏锋了(lll¬ω¬)

二叉搜索树的最近公共祖先

LC 第235题

递归法

从下到上遍历搜索二叉树,一旦发现cur的值处于p和q之间就算找到了最近公共祖先。
因为搜索二叉树的特性,中序有序,那么值处于二者之间必定是其父。

TreeNode* res;
void solve(TreeNode* root, TreeNode* p, TreeNode* q){
    if(!root) return;
    if(root->val >= min(p->val, q->val) && root->val <= max(p->val, q->val)){
        res = root;
        return;
    }
    lowestCommonAncestor(root->left, p, q);
    lowestCommonAncestor(root->right, p, q);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    solve(root, p, q);
    return res;
}

和标程不太一样,这是我自己想的,符合前序的逻辑,而且我觉得也不需要返回值(不需要遍历整棵树)

来看看迭代的解法(再次利用性质)

迭代法
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    while(root){
        if(root->val > p->val && root->val > q->val) root = root->left;
        else if(root->val < p->val && root->val < q->val) root = root->right;
        else return root;
    }
    return NULL;
}

二叉搜索树的修改与构造

二叉树搜索树的插入

LC 第701题

个人认为还是蛮简单的(它给的数据不强,而且有很多种方法,我这是最简单的直接往叶子那里插)

迭代法
TreeNode* insertIntoBST(TreeNode* root, int val) {
    if(!root) return new TreeNode(val);
    TreeNode* cur = root;
    TreeNode* pre = NULL;
    while(cur){
        pre = cur;
        if(val > cur->val) cur = cur->right;
        else cur = cur->left;
    }
    TreeNode* newNode = new TreeNode(val);
    newNode->left = NULL, newNode->right = NULL;
    if(val < pre->val) pre->left = newNode;
    else pre->right = newNode;
    return root;
}
递归法
void solve(TreeNode* cur, int val){
    if(!cur) return;
    if(!cur->left && cur->val > val){
        TreeNode* newNode = new TreeNode(val);
        cur->left = newNode;
    }
    if(!cur->right && cur->val < val){
        TreeNode* newNode = new TreeNode(val);
        cur->right = newNode;
    }
    if(val > cur->val) solve(cur->right, val);
    else solve(cur->left, val);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
    if(!root) return new TreeNode(val);
    solve(root, val);
    return root;
}

删除二叉搜索树的节点

LC 第450

啊啊啊啊删除好难!
但是务必掌握!

终止条件:没有找到要删除的节点,直接返回root

单层递归逻辑:
①左右孩子均为空(目标节点为叶子节点),直接删掉
②左孩子为空,右孩子补位
③右孩子为空,左孩子补位
④左右孩子均不为空,将待删结点的左孩子放到右孩子的最左叶子的左孩子处,然后右孩子补位

在这里插入图片描述
发现自己在魔改了就要及时止损,回头想想逻辑

TreeNode* deleteNode(TreeNode* root, int key) {
    if(!root) return root;
    if(root->val == key){
        if(!root->left && !root->right) return NULL;
        else if(!root->left) return root->right;
        else if(!root->right) return root->left;
        else{
            TreeNode* cur = root->right;
            while(cur->left) cur = cur->left;
            cur->left = root->left;
            TreeNode* tmp = root;
            root = root->right;
            delete tmp;
            return root;
        }
    }
    if(root->val < key) root->right = deleteNode(root->right, key);
    if(root->val > key) root->left = deleteNode(root->left, key);
    return root;
}

这里最难的就是左右孩子都有的处理问题(记得要删除原root节点)
还有就是修改的地方只管返回就是了,递归的话最终会返回到根上去,要注意的就是左右没有修改的地方还是用根的左右孩子接住,否则返回值不全。

修剪二叉搜索树

LC 第669题

也不是很难嘛(掌握了逻辑)

TreeNode* trimBST(TreeNode* root, int low, int high) {
    if(!root) return NULL;
    if(root->val < low){//如果它的值都比low小,那么它的左子更小
        if(root->right){//右子还有些希望
            root = root->right;
            return trimBST(root, low, high);
        }else return NULL;
    }
    if(root->val > high){//如果它的值都比high小,那么它的右子更大
        if(root->left){//左子还有些希望
            root = root->left;
            return trimBST(root, low, high);
        }else return NULL;
    }else{
        root->left = trimBST(root->left, low, high);
        root->right = trimBST(root->right, low, high);
    }
    return root;
}

构造一颗二叉搜索树

LC 第108题

TreeNode* solve(vector<int> nums, int begin, int end){
    if(begin > end) return NULL;
    int mid = (begin + end) / 2;
    TreeNode* root = new TreeNode(nums[mid]);
    root->left = solve(nums, begin, mid-1);
    root->right = solve(nums, mid+1, end);
    return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
    int s = nums.size();
    if(s == 0) return NULL;
    else return solve(nums, 0, s-1);
}

利用二叉搜索树的有序性,我们可以发现它比之前的构造二叉树要简单了一倍!

把二叉搜索树转换为累加树

LC 第538题

我知道顺序是右中左,但是因为肚子饿了,想不出来啦~

需要用pre记录前一个结点的值诶,这个我知道的,如果吃饱饱并且有精神的话绝对迎刃而解(都知道遍历顺序了就没有题目可以难倒我)

int pre;
void solve(TreeNode* cur){
    if(!cur) return;
    solve(cur->right);
    cur->val += pre;
    pre = cur->val;
    solve(cur->left);
}
TreeNode* convertBST(TreeNode* root) {
    pre = 0;
    solve(root);
    return root;
}

棒棒哒!次饭饭去~最近很能吃是真@_@

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值