Special topic of force buckle binary search tree

Posted by ttroy on Mon, 27 Dec 2021 11:16:40 +0100

After brushing the binary tree, we then worked hard on the way of binary search tree~

Binary search tree properties

Search in binary search tree

LC question 700

Iterative method

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

Recursive method

TreeNode* searchBST(TreeNode* root, int val) {
    if(!root || root->val == val) return root;
    if(val > root->val) return searchBST(root->right, val);//If the function has a return value, you must return here
    if(val < root->val) return searchBST(root->left, val);
    return NULL;
}

There's nothing to say, just pay attention= For null judgment, root - > Val can be used only if it is not empty

Validate binary search tree

LC question 98

I fell into a trap: I thought that as long as the left node of the current node is < the current value, the right node of the current node is > the current value, but it is required that all values on the left subtree of the current node are < the current value, and all values on the right subtree of the current node are > the current value. I want to find the maximum value of the left subtree and the minimum value of the right subtree. But there is a more ingenious method - just traverse the array in middle order and judge whether the array is orderly. (I didn't expect it was really confused)

Recursive method

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

Or you don't have to open the array and judge it directly in the middle order (record the previous one and compare it with the current value)
return when root - > left cannot be judged. Because a tree is a search binary tree, its left and right subtrees are search binary trees.

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

Iterative method

Using stack to simulate middle order traversal? I forgot it all (because I didn't understand it thoroughly at that time)

Note: it is not necessary to push the root node into the stack to simulate the middle order traversal (because the middle order does not start from the following node)

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

Minimum absolute difference of binary search tree

LC question 530

Recursive method

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

Or you don't have to open the array and record the value of the previous node in the middle order.
Note: this pre pointer must be defined globally

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

Iterative method

Continue to review using the stack to simulate the middle order

Note that the root node is not stacked at the beginning

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

summary

The binary search tree for the maximum value and difference value should take into account that the binary tree is orderly and make good use of this feature.

Mode in binary search tree

LC question 501

Recursive method

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

Or just traverse once, or the old routine. Record the previous nodes. Because the middle order traversal binary search tree is orderly, if it is the mode, it must be concentrated in the adjacent area.

vector<int> res;
int max_count, count;
TreeNode* pre = NULL;
void solve(TreeNode* cur){
    if(cur->left) solve(cur->left);
    if(!pre) count = 1; //First node
    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;
}

Iterative method

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

Nearest common ancestor of binary tree

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

The solution to this problem is wonderful! I have nothing but praise!
What are you doing there? I'm sweating a lot because I traverse the middle order and take the middle elements. Sure enough, the sword is at the wrong edge again (lll ¬ ω ¬)

Nearest common ancestor of binary search tree

LC question 235

Recursive method

Traverse the search binary tree from bottom to top. Once the value of cur is found between p and q, the nearest common ancestor is found.
Because the search binary tree has the characteristics of medium order, the value between the two must be its father.

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

It's different from the standard procedure. I think it's in line with the logic of the preamble, and I don't think I need to return a value (I don't need to traverse the whole tree)

Let's look at the iterative solution (reuse properties)

Iterative method

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

Modification and construction of binary search tree

Insertion of binary tree search tree

LC question 701

Personally, I think it's quite simple (the data it gives is not strong, and there are many methods. I'm the simplest to insert it directly into the leaves)

Iterative method

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

Recursive method

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

Delete node of binary search tree

LC 450

Ah, it's hard to delete!
But be sure to master!

Termination condition: if the node to be deleted is not found, return to root directly

Single layer recursive logic:
① The left and right children are empty (the target node is a leaf node), which can be deleted directly
② The left child is empty and the right child is filled
③ The right child is empty and the left child is filled
④ The left and right children are not empty. Place the left child of the node to be deleted at the left child of the leftmost leaf of the right child, and then the right child fills the space


If you find yourself in the devil, stop the loss in time and think about the logic

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

The most difficult problem here is to deal with the problems of both left and right children (remember to delete the original root node)
In addition, if you modify, just return. Recursion will eventually return to the root. Note that the left and right children of the root are still used to catch the unmodified places, otherwise the return value is incomplete.

Pruning binary search tree

LC question 669

It's not very difficult (Mastering logic)

TreeNode* trimBST(TreeNode* root, int low, int high) {
    if(!root) return NULL;
    if(root->val < low){//If its values are smaller than low, its left child is smaller
        if(root->right){//Youzi still has some hope
            root = root->right;
            return trimBST(root, low, high);
        }else return NULL;
    }
    if(root->val > high){//If its values are smaller than high, its right child is larger
        if(root->left){//Zuo Zi still has some hope
            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;
}

Construct a binary search tree

LC question 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);
}

Using the order of binary search tree, we can find that it is twice as simple as the previous construction of binary tree!

Convert binary search tree into cumulative tree

LC question 538

I know the order is right, middle and left, but I can't think of it because I'm hungry~

You need to use pre to record the value of the previous node. I know this. If you are full and energetic, it will be solved easily (if you know the traversal order, there will be no problem to embarrass me)

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

awesome! It's true that I can eat very much recently@_@

Topics: Binary tree