LeetCode brush notes binary tree binary search tree properties

Keywords: C++ Algorithm data structure leetcode

Introduction to binary search tree

Binary search tree (BST) is a special binary tree: for each parent node, the value of its left child node is less than or equal to the value of the parent node, and the value of its right child node is greater than or equal to the value of the parent node. Therefore, for a binary lookup tree, we can find out whether a value exists within the time of O(nlogn): starting from the root node, if the value of the current node is greater than the lookup value, we will go down to the left; if the value of the current node is less than the lookup value, we will go down to the right. At the same time, because the binary lookup tree is ordered, the result of traversing its order is the ordered array.

The implementation of a binary lookup tree is as follows:

template <class T>
class BST {
    struct Node {
        T data;
        Node* left;
        Node* right;
    };
    
    Node* root;
    
    Node* makeEmpty(Node* t) {
        if (t == NULL) return NULL;
        makeEmpty(t->left);
        makeEmpty(t->right);
        delete t;
        return NULL;
    }
    
    Node* insert(Node* t, T x) {
        if (t == NULL) {
            t = new Node;
            t->data = x;
            t->left = t->right = NULL;
        } else if (x < t->data) {
            t->left = insert(t->left, x);
        } else if (x > t->data) {
            t->right = insert(t->right, x);
        }
        return t;
    }
    
    Node* find(Node* t, T x) {
        if (t == NULL) return NULL;
        if (x < t->data) return find(t->left, x);
        if (x > t->data) return find(t->right, x);
        return t;
    }
    
    Node* findMin(Node* t) {
        if (t == NULL || t->left == NULL) return t;
        return findMin(t->left);
    }
    
    Node* findMax(Node* t) {
        if (t == NULL || t->right == NULL) return t;
        return findMax(t->right);
	}
    
    Node* remove(Node* t, T x) {
        ode* temp;
        if (t == NULL) return NULL;
        else if (x < t->data) t->left = remove(t->left, x);
        else if (x > t->data) t->right = remove(t->right, x);
        else if (t->left && t->right) {
            temp = findMin(t->right);
            t->data = temp->data;
            t->right = remove(t->right, t->data);
        } else {
            temp = t;
            if (t->left == NULL) t = t->right;
            else if (t->right == NULL) t = t->left;
            delete temp;
        }
        return t;
    }

public:
    BST(): root(NULL) {}
    ~BST() {
        root = makeEmpty(root);
    }
    void insert(T x) {
    	insert(root, x);
    }
    void remove(T x) {
    	remove(root, x);
    }
};  

538 convert binary search tree into cumulative tree

Give a binary search tree with different node values. Please convert it into a Greater Sum Tree so that the new value of each node is equal to the sum of the values greater than or equal to node.val in the original tree.

Input a binary search tree and output an accumulation tree

Input: root = [3,2,4,1]
Output: [7,9,4,10]

Resolution:

This problem can be solved by reverse middle order traversal. Middle order traversal first traverses the left node, then the root node, and finally the right node.

The characteristic of binary search tree is that the left subtree of node is less than the node value, and the right subtree of node is greater than the node value; The cumulative tree is cumulative, which is a node greater than or equal to the current node value; Therefore, as long as you traverse from the bottom and right of the binary search tree, you can accumulate node values from bottom to top.

That is, the current node value is the maximum cumulative sum of its right subtree plus its own node value, and the maximum cumulative sum appears in the cumulative value of the lowest and leftmost node of the left subtree when there is a left subtree in the right subtree.

According to the node value accumulation order of the accumulation tree: the right node accumulates first, then the root node accumulates based on the right node value, and finally the left node accumulates based on the root node value. Therefore, this problem adopts reverse middle order traversal, first traversing the right node, then traversing the root node, and finally traversing the left node; At the same time, a cumulative global variable is required to store the sum of the values of the nodes that have been traversed when traversing the current node.

In short, the binary search tree is traversed from large to small. Each node is traversed, and its own node value is added to update the cumulative sum value, and the cumulative sum value is taken as its new value.

class Solution {
    // Stores the sum of the values of the nodes that have been traversed when traversing the current node
    int sum = 0;
public:
    TreeNode* convertBST(TreeNode* root) {
        if(!root){
            return root;
        }
        // First traverse the right node
        convertBST(root->right);
        // Traversing the root node
        sum+=root->val;
        root->val = sum;
        // Finally, traverse the left node
        convertBST(root->left);
        return root;
    }
};

235 nearest common ancestor of binary search tree

Given a binary search tree, find the nearest common ancestor of two specified nodes in the tree

Enter a binary search tree and output a node representing the nearest common ancestor of two specified nodes

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 0, q = 3
Output: 2
Explanation: the nearest common ancestor of node 0 and node 3 is 2.

Resolution:

The characteristic of binary search tree is that the left subtree of node is less than the node value, and the right subtree of node is greater than the node value.

Using this feature, you can find the nearest common ancestor by using only one traversal:

Traverse from the root node. If the values of p and q are less than the values of the current node, it means that p and q should be in the left subtree of the current node and move the current node to its left child node.

If the values of p and q are greater than the values of the current node, it means that p and q should be in the right subtree of the current node and move the current node to its right child node.

If the value of the current node does not meet the above two requirements, that is, p and q are located in the left and right subtrees of the current node, then the current node is the nearest common ancestor of p and q.

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

236 nearest common ancestor of binary tree

Given a binary tree, find the nearest common ancestor of two specified nodes in the tree

Input a binary tree and output a node to represent the nearest common ancestor of two specified nodes

Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 6, q = 7
Output: 5
Explanation: the nearest common ancestor of node 6 and node 7 is node 5

Resolution:

Binary tree does not have the characteristics of binary search tree, so it is necessary to recursively traverse and find. But the idea is the same. It is judged whether p and q are located in the left and right subtrees of the current node respectively.

The traversal method of depth first search is adopted to recursively traverse the left and right subtrees of the current node:

Recursive termination condition: the current node is a leaf node or just a p or q node.

The left and right subtrees are traversed recursively from top to bottom, and the results are returned from bottom to top to ensure that the returned common ancestor is the nearest.

If the return result of recursive traversal of the left and right subtrees is true, it means that p and q are located in the left and right subtrees of the current node and directly return to the current node. If the recursive traversal result of the left subtree is false, it means that p and q are located in the right subtree of the current node, and the right node is returned; If the recursive traversal result of the right subtree is false, it means that p and q are located in the left subtree of the current node, and the left node is returned; If the return result of recursive traversal of the left and right subtrees is false, it means that p and q are not in the subtree with the current node as the root node.

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

Minimum absolute difference of 530 binary search tree

Given a binary search tree with non negative nodes, please calculate the minimum absolute value of the difference between any two nodes in the tree.

Input a binary search tree and output an integer to represent the minimum value of the absolute value of the difference between any two nodes in the tree

Input: [1,null,3,null,null,2]

Output: 1

Explanation: the minimum absolute difference is 1, where the absolute value of the difference between 2 and 1 is 1 (or 2 and 3).

Resolution:

The characteristic of binary search tree is that the left subtree of node is less than the node value, and the right subtree of node is greater than the node value. Then the middle order traversal sequence using binary search tree is an increasing sequence. This feature can be used to solve this problem quickly.

There are two ways to calculate the minimum value of the absolute value of the difference between any two nodes. One is to store the middle order traversal sequence of the binary search tree in an array, and then traverse the array to calculate the minimum difference. Another idea is to record the value of the previous node in the middle order traversal process, and calculate the minimum value of the absolute value of the difference between the two nodes in the traversal process.

class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        if(!root){
            return 0;
        }
        vector<int> inorder;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while(cur || !s.empty()){
            if(cur){
                s.push(cur);
                cur = cur->left;
            }else{
                cur = s.top();
                s.pop();
                inorder.push_back(cur->val);
                cur = cur->right;
            }
        }
        int ans = INT_MAX;
        for(int i=1;i<inorder.size();++i){
            ans = min(ans,inorder[i]-inorder[i-1]);
        }
        return ans;
    }
    
    // One pass traversal method: use pre to record the value of the previous node
    int getMinimumDifference(TreeNode* root) {
        if(!root){
            return 0;
        }
        int pre = -1, ans = INT_MAX;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while(cur || !s.empty()){
            if(cur){
                s.push(cur);
                cur = cur->left;
            }else{
                cur = s.top();
                s.pop();
                if(pre==-1){
                    pre = cur->val;
                }else{
                    ans = min(ans,cur->val-pre);
                    pre = cur->val;
                }
                cur = cur->right;
            }
        }
        return ans;
    }
};

897 incremental sequential search tree

Given a binary search tree, please traverse it in the middle order and rearrange it into an incremental search tree, so that the leftmost node in the tree becomes the root node of the tree, and each node has no left child node and only one right child node.

Input a binary search tree and output an incremental sequential search tree with only right nodes

Input: root = [5,3,6,2,4, null, 8,1, null, null, 7,9]
Output: [1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]

Resolution:

This topic and Minimum absolute difference of 530 binary search tree Similarly, you can also solve the problem from the idea that the middle order traversal of the binary search tree is an increasing sequence. First set a short node, and then insert each node traversed into the right node of the previous node using the tail interpolation method. In the middle order traversal process, insert the current node into the right node of the previous node until the traversal is completed.

It should be noted that in the process of inserting nodes, the left node of the current node should be empty. Because the left nodes in the binary search tree are smaller than the current node, they have been inserted into the result tree. If the left node is not set to null, a loop may occur.

class Solution {
public:
    TreeNode* increasingBST(TreeNode* root) {
        if(!root){
            return root;
        }
        // Create a short node
        TreeNode* ans = new TreeNode();
        TreeNode* tail = ans;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while(cur || !s.empty()){
            if(cur){
                s.push(cur);
                cur = cur->left;
            }else{
                cur = s.top();
                s.pop();
                // Make the current node the right node of the previous node
                tail->right = cur;
                tail = cur;
                // Set the left node of the current node to null to avoid looping
                cur->left = nullptr;
                cur = cur->right;
            }
        }
        return ans->right;
    }
};

653 sum of two IV - input BST

Given a binary search tree and a target result k, if there are two elements in the BST and their sum is equal to the given target result, it returns true.

Input a binary search tree and the target value k, and output a Boolean value to indicate whether the sum of two elements is the target value k

Input: root = [5,3,6,2,4,null,7], k = 9
Output: true

Resolution:

Seeing the sum of two numbers, it's easy to think of using a hash table. One of the simplest ideas is to traverse the binary search tree and store all elements in the hash table. Then, traverse the hash table to find the sum of the two numbers.

class Solution {
public:
    bool findTarget(TreeNode* root, int k) {
        unordered_set<int> hash;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while(cur || !s.empty()){
            if(cur){
                s.push(cur);
                cur = cur->left;
            }else{
                cur = s.top();
                s.pop();
                hash.insert(cur->val);
                cur = cur->right;
            }
        }
        
        for(const auto elem: hash){
            if(hash.find(k-elem) != hash.end() && k-elem != elem){
                return true;
            }
        }
        return false;
    }
};

reference material

LeetCode 101: easy to brush with you (C + +) Chapter 14 pointer to the tree of three swordsmen

Posted by markthien on Thu, 18 Nov 2021 19:53:01 -0800