本文记录作者刷题过程中与二叉搜索树相关的题目。

一、增删改查

700. 二叉搜索树中的搜索(简单难度)

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        //终止条件
        if(root==NULL)return NULL;
        //处理节点
        if(root->val==val) return root;
        //有条件的递归调用
        if(val<root->val)return searchBST(root->left,val);
        if(val>root->val)return searchBST(root->right,val);
        return root;
    }
};

701. 二叉搜索树中的插入操作(中等难度)

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        //终止条件和处理节点
        if(root==NULL){
            TreeNode* newNode=new TreeNode(val);
            return newNode;
        }
        //有条件的递归
        if(val<root->val) root->left=insertIntoBST(root->left,val);
        if(val>root->val) root->right=insertIntoBST(root->right,val);
        return root;
    }
};

450. 删除二叉搜索树中的节点(中等难度)

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        //终止条件(如果没有找到删除节点)
        if(root==NULL) return NULL;
        //处理节点(如果找到删除节点)
        if(root->val==key){
            //如果删除节点是叶子节点,返回NULL
            if(root->left==NULL&&root->right==NULL) return NULL;
            //如果删除节点只有右节点,返回右节点
            else if(root->left==NULL&&root->right!=NULL)return root->right;
            //如果删除节点只有左节点,返回左节点
            else if(root->left!=NULL&&root->right==NULL)return root->left;
            //如果删除节点有左节点和右节点,删除root,右节点替补,左节点接到右节点的最左侧节点上
            else{
                //找到其右子树的最左侧节点
                TreeNode* cur=root->right;
                while(cur->left!=NULL){
                    cur=cur->left;
                }
                //最左侧节点指向删除节点的左节点
                cur->left=root->left;
                //安置删除节点的右节点
                //TreeNode* tmp=root;
                root=root->right;
                //delete tmp;
                return root;
            }
        }
        //有条件的递归调用
        if(root->val>key)root->left=deleteNode(root->left,key);
        if(root->val<key)root->right=deleteNode(root->right,key);
        return root;
    }
};

669. 修剪二叉搜索树(中等难度)

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        //终止条件
        if(root==NULL)return NULL;
    	//当前值需要裁剪
        if(root->val<low)//如果当前值小于裁剪区间,说明需要修剪
        {
            return trimBST(root->right,low,high);//左节点一定不符合,只需要检查右节点
        }
        else if(root->val>high)//如果当前值大于裁剪区间,说明需要修剪
        {
            return trimBST(root->left,low,high);//右节点一定不符合,只需要检查左节点
        }
    	//当前值不需要裁剪,但是其子节点可能需要
        root->left=trimBST(root->left,low,high);
        root->right=trimBST(root->right,low,high);
        return root;
    }
};

二、最近公共祖先

235. 二叉搜索树的最近公共祖先(简单难度)

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先(简单难度)

方法一:直接使用普通二叉树的方法

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL) return NULL;
        if(root->val==p->val||root->val==q->val) 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 left;
        if(left==NULL&&right!=NULL) return right;
        return NULL;
    }
};

方法一:有序递归子区间

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL)return NULL;
        //如果p和q的值都小于当前节点的值,说明最近公共祖先在当前节点的左子树上
        if(p->val<root->val&&q->val<root->val) return lowestCommonAncestor(root->left,p,q);
        //如果p和q的值都小于当前节点的值,说明最近公共祖先在当前节点的右子树上
        if(p->val>root->val&&q->val>root->val) return lowestCommonAncestor(root->right,p,q);
        //否则说明最近公共祖先就是当前节点
        return root;
    }
};

三、中序遍历

897. 递增顺序搜索树(简单难度)

剑指 Offer II 052. 展平二叉搜索树(简单难度)

class Solution {
public:
    TreeNode* pre;
    void dfs(TreeNode* cur){
        if(cur==nullptr) return;
        dfs(cur->left);
        // 处理节点
        pre->right=cur;
        cur->left=nullptr;
        pre=cur;
        dfs(cur->right);
    }
    TreeNode* increasingBST(TreeNode* root) {
        TreeNode* myHead=new TreeNode(-1);
        pre=myHead;
        dfs(root);
        return myHead->right;
    }
};

剑指 Offer 54. 二叉搜索树的第k大节点(简单难度)

class Solution {
public:
    int res=0;//最终结果
    int count=0;//计数
    void dfs(TreeNode* root,int k){
        if(root==NULL) return ;
        //倒序:右,根,左
        dfs(root->right,k);
        ++count;
        if(k==count) {
            res=root->val;
            return;
        }
        dfs(root->left,k);
    }
    int kthLargest(TreeNode* root, int k) {
        dfs(root,k);
        return res;
    }
};

230. 二叉搜索树中第K小的元素(中等难度)

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

class Solution {
public:
    int res=0;//最终结果
    int count=0;//计数
    void dfs(TreeNode* root,int k){
        if(root==NULL) return ;
        //正序:左,根,右
        dfs(root->left,k);
        // 进入节点
        ++count;
        if(k==count) {
            res=root->val;
            return;
        }
        dfs(root->right,k);
    }
    int kthSmallest(TreeNode* root, int k) {
        dfs(root,k);
        return res;
    }
};

285. 二叉搜索树中的中序后继(中等难度)

剑指 Offer II 053. 二叉搜索树中的中序后继(中等难度)

class Solution {
public:
    vector<TreeNode*> arr;//中序遍历数组
    void dfs(TreeNode* root){
        if(root==NULL) return;
        dfs(root->left);
        arr.push_back(root);
        dfs(root->right);
    }
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        // 中序递归,得到数组
        dfs(root);
        // 遍历数组,找到该节点
        int i=0;
        while(i<arr.size()&&arr[i]->val!=p->val)++i;
        // 如果i正好是最后一个节点,返回null
        if(i==arr.size()-1) return NULL;
        // 如果i不是最后一个节点,返回其后继
        else return arr[i+1];
    }
};

98. 验证二叉搜索树(中等难度)

class Solution {
public:
    vector<int> arr;
    void dfs1(TreeNode* root){
        if(root==nullptr) return;
        dfs1(root->left);
        arr.push_back(root->val);
        dfs1(root->right);
    }
    long long maxVal = LONG_MIN; // 左子树中的默认最大值,测试数据中有LONG_MIN
    bool dfs2(TreeNode* root){
        if(root==nullptr) return true;
        bool left=dfs2(root->left);
        // 中序遍历,maxVal为left子树中的最大值
        if (maxVal < root->val) maxVal = root->val;
        else return false;
        bool right=dfs2(root->right);
        return left&&right;
    }
    bool isValidBST(TreeNode* root) {
        // 方法一
        // dfs1(root);
        // for(int i=1;i<arr.size();++i){
        //     if(arr[i-1]>=arr[i]) return false;
        // }
        // return true;
        // 方法二
        return dfs2(root);
    }
};

426. 将二叉搜索树转化为排序的双向链表(中等难度)

class Solution {
private:
    Node* pre=NULL; // 当前访问节点的前一个节点
    Node* head=NULL;// 双向循环链表的头结点
public:
    Node* treeToDoublyList(Node* root) {
        if(root==NULL) return NULL;
        // 采用中序遍历的方式遍历二叉搜索树,修改为双向链表
        dfs(root);
        // 修改为循环链表
        head->left = pre;//头结点指向尾结点
        pre->right = head;//尾结点指向头结点
        return head;
    }
    // 中序遍历
    void dfs(Node* cur){
        // 终止条件
        if(cur==NULL) return;
        // 递归左节点 
        dfs(cur->left);
        // 处理节点head,pre,cur
        if(pre) pre->right=cur;// 如果pre不为空,说明双向链表上已经有节点,其指向当前节点
        else head=cur;// 如果pre为空,说明双向链表没有节点,设置头结点
        cur->left=pre;// 当前节点指向上一个
        pre=cur;// 更新上一个节点
        // 递归右节点
        dfs(cur->right);
    }
};

四、后序遍历

剑指 Offer 33. 二叉搜索树的后序遍历序列(中等难度)

class Solution {
public:
    // 后序遍历序列[左子树区间,右子树区间,根节点]
    bool verifyPostorder(vector<int>& postorder) {
        return dfs(postorder,0,postorder.size()-1);
    }
    // 在[left,right]中进行递归遍历,
    bool dfs(vector<int>& postorder,int left,int right){
        // 终止条件(right是根节点,此时只有一个节点)
        if(left>=right) return true;
        // 遍历[left,right],查找第一个大于根节点的节点,此为右区间第一个节点
        int m=left;
        while(m<=right){
            if(postorder[m]>=postorder[right]) break;
            else m++;
        }
        // 遍历右子树区间[m,right-1],查看是否有比right小的值
        for(int i=m;i<right;i++){
            if(postorder[i]<=postorder[right])
                return false;
        }
        // 继续遍历左子树区间[left,m-1]和右子树区间[m,right-1]
        return dfs(postorder,left,m-1)&&dfs(postorder,m,right-1);

    }
};

五、不同的二叉搜索树

96. 不同的二叉搜索树(中等难度)

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。n的范围在[1,19]

输入:n = 3
输出:5

点评

class Solution {
public:
    /*动态规划题
    【状态定义】dp[i]表示i个节点的二叉搜索树的个数
    【状态转换】以dp[3]为例,dp[3]=元素1为根的个数+元素2为根的个数+元素3为根的个数
    元素1为根的个数=右子树有2个元素的个数*左子树有0个元素的个数=dp[2]*dp[0]
    元素2为根的个数=右子树有1个元素的个数*左子树有1个元素的个数=dp[1]*dp[1]
    元素3为根的个数=右子树有0个元素的个数*左子树有2个元素的个数=dp[0]*dp[2]
    得dp[3]==dp[2]*dp[0]+dp[1]*dp[1]+dp[0]*dp[2]
    假设j为根节点个数,范围在[1,i]
    则 dp[i]+=元素j为根的个数
    =右子树有i-j个元素的个数*左子树有j-1个元素的个数
    =dp[i-j]*dp[j-1]
    【初始状态】根据递推公式,dp[0]需要初始化
    dp[0]表示0个节点的二叉搜索树的个数,可以视为空树,设为1
    【遍历顺序】根据递推公式,dp[i]的推导依赖于j,dp[i-j]在dp[i]之前得出
    所以i的范围是正序,在[1,n],j不能超过i,范围在[1,n]
    */
    int numTrees(int n) {
        vector<int> dp(n + 1);
        dp[0] = 1;
        // i表示节点数,范围在[1,n]
        for (int i = 1; i <= n; i++) {
            // j表示j为根时的情况,范围在[1,i]
            for (int j = 1; j <= i; j++) {
                dp[i] += dp[j - 1] * dp[i - j];// 递归公式
            }
        }
        return dp[n];
    }
};

95. 不同的二叉搜索树 II(中等难度)

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回所有满足题意的二叉搜索树。n的范围在[1,8]

输入:n = 3
输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]

点评

该题和96的不同点在于,其需要返回所有不同的二叉搜索树,其n的范围缩小到8,就减少了计算量,可以用深度优先搜索。96中的动态规划无法用来求解该题。

class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        return dfs(1, n);
    }
    vector<TreeNode*> dfs(int start,int end){
        if(start>end) return {nullptr};
        vector<TreeNode*> resTrees;
        // 遍历所有为n的节点,作为当前的根节点
        for(int i=start;i<=end;++i){
             // 获得所有可行的左子树集合
            vector<TreeNode*> leftTrees = dfs(start, i - 1);
            // 获得所有可行的右子树集合
            vector<TreeNode*> rightTrees = dfs(i + 1, end);
            // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
            for (auto& left:leftTrees) {
                for (auto& right:rightTrees) {
                    TreeNode* currTree = new TreeNode(i);
                    currTree->left = left;
                    currTree->right = right;
                    resTrees.emplace_back(currTree);//加入当前节点
                }
            }
        }
        return resTrees;
    }
};

六、有序数组

108. 将有序数组转换为二叉搜索树(简单难度)

    // 方法一:递归法
   TreeNode* sortedArrayToBST1(vector<int>& nums) {
    if(nums.size()==0)return NULL;
    //确定切割点
    int index = nums.size()/2;
    //构造根节点
    TreeNode* root=new TreeNode(nums[index]);
    //终止条件
    if(nums.size()==1)return root;
    //切割数组
    vector<int> leftNums(nums.begin(),nums.begin()+index);
    vector<int> rightNums(nums.begin()+index+1,nums.end());
    //递归调用
    root->left = sortedArrayToBST1(leftNums);
    root->right = sortedArrayToBST1(rightNums);
    return root;
}
// 方法二
TreeNode* preOrder(vector<int>& nums,int left,int right) {
    if(left>right)return NULL;
    //确定切割点
    int index = left + ((right - left) / 2);
    //构造根节点
    TreeNode* root=new TreeNode(nums[index]);
    //递归调用
    root->left = preOrder(nums,left,index-1);
    root->right = preOrder(nums,index+1,right);
    return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
    TreeNode* root = preOrder(nums, 0, nums.size() - 1);
    return root;
}

538. 把二叉搜索树转换为累加树(中等难度)

1038. 从二叉搜索树到更大和树(中等难度)

class Solution {
public:
    //反中序遍历
    void DinOrder(TreeNode* root,int& pre) { // 右中左遍历
        if(root==NULL) return;
        DinOrder(root->right,pre);//右
        root->val += pre;//当前新节点值=前一节点值+当前旧节点值
        pre = root->val;//更新前一节点值
        DinOrder(root->left,pre);//中
    }
    TreeNode* bstToGst(TreeNode* root) {
        int pre= 0;
        DinOrder(root,pre);
        return root;
    }
};

501. 二叉搜索树中的众数(简单难度)

class Solution {
public:
    //中序遍历数组
    void inOrder(TreeNode* node,TreeNode*& pre,int &curTimes,int& maxTimes,vector<int>& result){
        if(node==NULL) return;
        inOrder(node->left,pre,curTimes,maxTimes,result);
        //如果当前节点是第一个节点
        if (pre==NULL) curTimes=1;
        //如果当前节点值等于前一个节点值
        else if(node->val == pre->val) curTimes++;
        //如果当前节点值不等于前一个节点值
        else curTimes=1;
        pre = node;
        //如果当前计数大于最大计数
        if (curTimes > maxTimes){
            result.clear();
            result.push_back(node->val);
            maxTimes = curTimes;
        }
        //如果当前计数等于最大计数
        else if (curTimes == maxTimes) result.push_back(node->val);
        inOrder(node->right,pre,curTimes,maxTimes,result);
    }
    vector<int> findMode(TreeNode* root) {
        vector<int> result;//众数数组
        TreeNode* pre=NULL;//前一个节点
        int curTimes=1;//计数器
        int maxTimes=0;//
        inOrder(root,pre,curTimes,maxTimes,result);
        return result;
    }
};

530. 二叉搜索树的最小绝对差(简单难度)

783. 二叉搜索树节点最小距离(简单难度)

class Solution {
public:
    //将搜索二叉树转换为有序数组
    void inOrder(TreeNode* root,TreeNode*& pre,int& result){
        if(root==NULL) return;
        inOrder(root->left,pre,result);
        //如果当前节点是第一个
        if(pre==NULL) result=INT_MAX;
        //如果当前节点不是第一个
        else result=min(result,root->val-pre->val);
        pre=root;//更新前一节点
        inOrder(root->right,pre,result);
    }
    int getMinimumDifference(TreeNode* root) {
        TreeNode* pre=NULL;//前一节点
        int result=INT_MAX;
        inOrder(root,pre,result);//获取有序数组
        return result;
    }
};