【数据结构】Leetcode——二叉搜索树 经典题


开始之前学习一个单词热热身:

     freebie 英[ˈfriːbi]
     n. (常指公司提供的) 免费品;


二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。
二叉查找树中序遍历有序。DL是玄学,递归也是玄学!!!

1 修剪二叉搜索树(669)

题目:
     给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

示例:
在这里插入图片描述

思路:
     从树的根节点开始判断,若根节点的值小于L,则返回的根节点在左子树中;若根节点的值大于R,则返回的根节点在右子树中;
     如果根节点的值在[L, R]范围内,则对根节点的左右子节点递归即可。

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if(!root) return nullptr;
        if(root->val > high) return trimBST(root->right, low, high);
        if(root->val < low) return trimBST(root->left, low, high);
        root->left = trimBST(root->left, low, high);
        root->right = trimBST(root->right, low, high);
        return root;
    }
};

2 二叉搜索树中第K小的元素(230)

题目:
     给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。

示例:
在这里插入图片描述
思路:
     对二叉搜索树中序遍历得到的结果即是有序的,进而返回有序列表中第k-1个元素即可。

// 递归实现
class Solution {
public:
    int num =0, k, res;
    void func(TreeNode* node, int K){
        if(!node || k>K) return;
        func(node->left, K);
        k++;
        if(k==K){   
            res = node->val;
            k++;
            return;
        }
        func(node->right, K);
    }
    int kthSmallest(TreeNode* root, int k) {
        func(root, k);
        return res;
    }
};
// 迭代实现
class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        stack<TreeNode*> stk;
        vector<int> res;
        TreeNode* temp = root;
        int num = 0;
        while(temp != nullptr || !stk.empty() ){
            if(temp!=nullptr){
                stk.push(temp);
                temp = temp->left;
            }
            else{
                temp = stk.top();
                stk.pop();
                res.push_back(temp->val);
                temp = temp->right;
            }
        }
        return res[k-1];
    }
};

3 把二叉搜索树转换为累加树(538)

题目:
     给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。

示例:
在这里插入图片描述

思路:
     将中序遍历二叉树的左中右顺序变为右中左顺序,不断累加右子节点的数值即可。

class Solution {
private:
    int pre; // 记录前一个节点的数值
    void traversal(TreeNode* cur) { // 右中左遍历
        if (cur == NULL) return;
        traversal(cur->right);
        cur->val += pre;
        pre = cur->val;
        traversal(cur->left);
    }
public:
    TreeNode* convertBST(TreeNode* root) {
        pre = 0;
        traversal(root);
        return root;
    }
};

4 二叉搜索树的最近公共祖先(235)

题目:
     给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
示例:
在这里插入图片描述
思路:

     在二叉搜索树里,如何判断一个节点的左子树里有p,右子树里有q呢?

     在从上到下遍历树的时候,满足条件(p->val <= cur->val && cur->val <= q->val)则说明该节点cur就是最近公共祖先了。
     当cur->val均小于p与q的值,去递归cur的右子节点;当cur->val均大于p与q的值,去递归cur的左子节点;否则当前节点即为p与q的公共祖先。

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

5 二叉树的最近公共祖先(236)

题目:
     给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
示例:在这里插入图片描述

思路:
(1) 如果当前结点 root等于 NULL,则直接返回 NULL
(2) 如果 root等于 p或者 q,那这棵树一定返回 p 或者 q
(3) 然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left 和 right 表示
(4) 此时若left为空,那最终结果只要看 right;若 right 为空,那最终结果只要看 left
(5) 如果 left和 right都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root 是他们的最近公共祖先
(6) 如果 left 和 right 都为空,则返回空(其实已经包含在第4步中了)

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL) return NULL;
        if(root==p|| root==q) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if(left == NULL)
            return right;
        if(right == NULL)
            return left;      
        if(left && right) // p和q在两侧
            return root;
        return NULL; // 必须有返回值
    }
};

6 将有序数组转换为二叉搜索树(108)

题目:
     将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

示例:
在这里插入图片描述

思路:
     有序数组的中点为树的根节点,中点前半部分的数构成左子树,中点后半部分的数构成右子树,通过对根节点的左右子树递归得到整棵树。

class Solution {
public:
    TreeNode* DFS(vector<int>& nums, int start, int end){
        if(start == end) return NULL;
        int mid = (end + start)>>1;
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = DFS(nums, start, mid);
        root->right = DFS(nums, mid+1, end);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return DFS(nums, 0, nums.size());
    }
};

7 有序链表转换二叉搜索树(109)

题目:
     给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

示例:

在这里插入图片描述

思路:

①.原链表是有序的,要使转换成的二叉树高度平衡,只需要寻找中间节点作为跟节点即可,然后作用两侧分别转换成左子树和右子树。
②.查找中间节点可以使用快慢指针进行查找。
③.通过递归算法即可完成二叉搜索树的转换。

class Solution {
public:
    ListNode* preMID(ListNode* head){
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* pre = NULL;
        while(fast!=nullptr && fast->next!=nullptr){
            pre = slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        return pre;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        if(head==nullptr) return nullptr;
        if(head->next == nullptr) return new TreeNode(head->val);
        ListNode* premid = preMID(head);
        ListNode* mid = premid->next;
        premid->next = nullptr;
        TreeNode* root = new TreeNode(mid->val);
        root->left = sortedListToBST(head);
        root->right = sortedListToBST(mid->next);
        return root;
    }
};

8 两数之和 IV - 输入 BST(653)

题目:
     给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

思路:
     使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。
     应该注意到,这一题不能用分别在左右子树两部分来处理这种思想,因为两个待求的节点可能分别在左右子树中。

class Solution {
public:
    vector<int> sort;
    void vct(TreeNode* root){
        if(!root) return;
        vct(root->left);
        sort.push_back(root->val);
        vct(root->right);
    }
    bool findTarget(TreeNode* root, int k) {
            vct(root);
            int i = 0;
            int j = sort.size()-1;
            int sum = 0;  
            while(i<j){
                sum = sort[i] + sort[j];
                if(sum==k) return true;
                if(sum<k) i++;
                else j--;
            }
            return false;
    }
};

9 二叉搜索树的最小绝对差(530)

题目:
     给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。

思路:
     利用中序遍历二叉搜索树为有序数组的性质,取数组相邻元素之差最小值即可。

class Solution {
public:
    vector<int> vct;
    void handdle(TreeNode* root){
        if(!root) return;   
        handdle(root->left);
        vct.push_back(root->val);
        handdle(root->right);
    }
    int getMinimumDifference(TreeNode* root) {
        handdle(root);
        int min = vct[1]-vct[0];
        for(int i=2; i<vct.size();i++){
            int temp = vct[i]-vct[i-1];
            if(temp<min) min=temp;
        }
        return min;
    }
};

10 二叉搜索树中的众数(501)

题目:
     给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

思路:
     首先 BST 如果中序遍历,就是一个排序的数组,可以在遍历时记录这个数字出现的次数。
     当新的数字与前一个数字不同,则根据这个数字出现的次数判断。
     如果 小于 最大次数,不管;
     如果 等于 最大次数,加入到答案列表中;
     如果 大于 最大次数,清空答案列表,然后加入这个数字,并更新最大次数。
     可以先遍历树放入数组中查找,也可以直接在中序遍历中做上述的处理。

class Solution {
public:
    int count;
    int maxCount;
    int val = INT_MIN;
    vector<int> res;
    
    void inorder(TreeNode* root){
        if(!root) return;
        inorder(root->left);
        count = (root->val==val)? count+1:1;
        if(count==maxCount) res.push_back(root->val);
        else if(count>maxCount){
            maxCount = count;
            res.clear();
            res.push_back(root->val);
        }
        val = root->val;
        inorder(root->right);
    }
    vector<int> findMode(TreeNode* root) {
        inorder(root);
        return res;
    }
};

欢迎关注【OAOA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值