[LEECODE]算法进阶自练习7-8 二叉树&二叉搜索树

223 阅读7分钟

[LEECODE]算法进阶自练习7-8 二叉树&二叉搜索树

7.二叉树

简单
  1. 二叉树的最小深度 leetcode 111  递归解法:
    int minDepth(TreeNode* root) {
        if(!root) return 0;
        if(root->left == nullptr && root->right == nullptr) return 1;
        int minDep = INT_MAX;
        if(root->left){
            minDep = min(minDep,minDepth(root->left));
        }
        if(root->right){
            minDep = min(minDep,minDepth(root->right));
        }
        return minDep + 1; // +1是加上当前节点
    }

 广度优先解法:

int minDepth(TreeNode* root) {
        if(!root) return 0;
        int ret = 0;
        queue<pair<TreeNode*, int>> queue;
        queue.emplace(root,1);
        while (!queue.empty()) {
            TreeNode *node = queue.front().first;
            int depth = queue.front().second;
            queue.pop();
            if (node->left == nullptr && node->right == nullptr) {
                return depth;
            }
            if (node->left != nullptr) {
                queue.emplace(node->left, depth + 1);
            }
            if (node->right != nullptr) {
                queue.emplace(node->right, depth + 1);
            }
        }
        return 0;
    }
  1. 二叉树的最近公共祖先 leetcode 236  递归解法,理解思路可以参考这里(leetcode-cn.com/problems/lo…
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        /*
            1.如果 p 和 q 都存在,则返回它们的公共祖先;
            2.如果只存在一个,则返回存在的一个;
            3. 如果 p 和 q 都不存在,则返回NULL
        */
        if(!root) return nullptr;
        if(root == p || root == q) return root;
        
        TreeNode* l = lowestCommonAncestor(root->left,p,q);
        TreeNode* r = lowestCommonAncestor(root->right,p,q);
        if(!l) return r; // p q在右子树  注意这里判断条件是先判断为空的, 不然走不到 l&&r
        if(!r) return l; // p q在左子树  注意这里判断条件是先判断为空的, 不然走不到 l&&r
        if(l && r) return root; // p q在左右两个子树
        return nullptr;
    }

 记录父节点,然后遍历查找最后的一个共同的父节点。

class Solution {
public:
    unordered_map<int, TreeNode*> fa;
    unordered_map<int, bool> vi;
    void dfs(TreeNode* root){
        if(root->left){
            fa[root->left->val] = root;
            dfs(root->left);
        }
        if(root->right){
            fa[root->right->val] = root;
            dfs(root->right);
        }
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        fa[root->val] = nullptr;
        dfs(root);
        while(p != nullptr){
            vi[p->val] = true;
            p = fa[p->val];
        }
        while(q != nullptr){
            if(vi[q->val]) return q;
            q = fa[q->val];
        }
        return nullptr;
    }
};
中等
  1. 对称二叉树 leetcode 101  递归解法:
    bool check(TreeNode* l, TreeNode* r){
        if(!l && !r) return true; // 左右节点都为空了
        if(!l || !r) return false; // 有一个节点不为空
        return l->val == r->val && check(l->left,r->right) && check(l->right,r->left);
    }
    bool isSymmetric(TreeNode* root) {
        if(!root) return true;
        return check(root->left, root->right);
    }

 层序遍历解法:

    bool check(TreeNode* l, TreeNode* r){
        queue<TreeNode*> queue;
        queue.push(l);queue.push(r);
        while(!queue.empty()){
            TreeNode* tl = queue.front(); queue.pop();
            TreeNode* tr = queue.front(); queue.pop();
            if(!tl && !tr) continue;
            if((!tl || !tr) || tl->val != tr->val) return false;
            queue.push(tl->left);
            queue.push(tr->right);
            queue.push(tl->right);
            queue.push(tr->left);
        }
        return true;
    }
    bool isSymmetric(TreeNode* root) {
        if(!root) return true;
        return check(root->left, root->right);
    }
  1. 二叉树的锯齿形层次遍历 leetcode 103
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        if(root == nullptr) return {};
        vector<vector<int>> ret;
        queue<TreeNode*> queue;
        queue.push(root);
        int level = 0;
        while(!queue.empty()){
            
            int tsize = queue.size();
            vector<int> vt;
            while(tsize > 0){
                TreeNode* node = queue.front();
                queue.pop();
                if(level%2 == 0) {
                    vt.push_back(node->val);
                }
                else{
                    vt.insert(vt.begin(),node->val);
                }
                if(node->left) queue.push(node->left);
                if(node->right) queue.push(node->right); 
                tsize--;
            }
            level++;
            ret.push_back(vt);
        }
        return ret;
    }
  1. 二叉树的层序遍历 102  上面那道题删除几行代码就行了。
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(root == nullptr) return {};
        vector<vector<int>> ret;
        queue<TreeNode*> queue;
        queue.push(root);
        int level = 0;
        while(!queue.empty()){
            
            int tsize = queue.size();
            vector<int> vt;
            while(tsize > 0){
                TreeNode* node = queue.front();
                queue.pop();
                vt.push_back(node->val);
                if(node->left) queue.push(node->left);
                if(node->right) queue.push(node->right); 
                tsize--;
            }
            level++;
            ret.push_back(vt);
        }
        return ret;
    }
困难
  1. 二叉树的序列化与反序列化 leetcode 297
class Codec {
public:
    void levelOrder(TreeNode* root, string& ret){
        if(root == nullptr) return;
        queue<TreeNode*> qu;
        qu.push(root);
        while(!qu.empty()){
            TreeNode* node = qu.front();
            qu.pop();
            if(node){
                qu.push(node->left);
                qu.push(node->right);
                ret += to_string(node->val);
                ret += ",";
            }
            else{
                ret += "$,";
            }
        }
    }
    bool getData(string_view& data, int& number){
        int n = data.size();
        int i = 0;
        bool isNumber = true;
        number = 0;
        bool negative = false;
        while(i<n){
            if(data[i] == '-'){
                negative = true;
                i++;
            }
            else if(data[i] ==  '$'){
                isNumber = false;
                i++;
            }else if(data[i] == ','){
                data = data.substr(i+1);
                if(negative) number = -number;
                return isNumber;
            }
            else{
                number = number*10 + (data[i++] - '0');
            }
        }
        return isNumber;
    }
    TreeNode* deLevelOrder(string_view data){
        int number;
        queue<TreeNode*> qu;
        getData(data, number);
        TreeNode* root = new TreeNode(number);
        qu.push(root);
        while(!qu.empty()){
            TreeNode* node = qu.front();
            qu.pop();
            if(getData(data, number)){
                node->left = new TreeNode(number);
                qu.push(node->left);
            }
            if(getData(data, number)){
                node->right = new TreeNode(number);
                qu.push(node->right);
            }
        }
        return root;
    }
    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string ret;
        levelOrder(root,ret);
        return ret;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data.empty()) return nullptr;
        return deLevelOrder(data);
    }
};

8.二叉搜索树

简单
  1. 二叉搜索树节点最小距离 leetcode 783  注意此题是求任意两个节点差值 递归解法:
class Solution {
public:
    vector<int> nums;
    void collection(TreeNode* root){
        if(!root) return;
        if(root->left) collection(root->left);
        nums.push_back(root->val);
        if(root->right) collection(root->right);
    }
    int minDiffInBST(TreeNode* root) {
        int min = INT_MAX;
        collection(root);
        for(int i = 1; i< nums.size(); i++){
            min = min > abs(nums[i] - nums[i-1]) ? abs(nums[i] - nums[i-1]) : min;
        }
        return min;
    }
};

 深度优先遍历解法:

class Solution {
public:
    vector<int> nums;
    void collection(TreeNode* root){
        if(!root) return;
        stack<TreeNode*> st;
        while(root || !st.empty()){
            while(root){
                st.push(root);
                root = root->left;
            }
            if(!st.empty()){
                root = st.top();
                st.pop();
                nums.push_back(root->val);
                root = root->right;
                
            }   
        }
    }
    int minDiffInBST(TreeNode* root) {
        int min = INT_MAX;
        collection(root);
        for(int i = 1; i< nums.size(); i++){   
            min = min > abs(nums[i] - nums[i-1]) ? abs(nums[i] - nums[i-1]) : min;
        }
        return min;
    }
};
  1. 二叉搜索树的最近公共祖先 leetcode 235  递归解法,理解思路可以参考这里(leetcode-cn.com/problems/lo…
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        /*
            1.如果 p 和 q 都存在,则返回它们的公共祖先;
            2.如果只存在一个,则返回存在的一个;
            3. 如果 p 和 q 都不存在,则返回NULL
        */
        if(!root) return nullptr;
        if(root == p || root == q) return root;
        
        TreeNode* l = lowestCommonAncestor(root->left,p,q);
        TreeNode* r = lowestCommonAncestor(root->right,p,q);
        if(!l) return r; // p q在右子树  注意这里判断条件是先判断为空的, 不然走不到 l&&r
        if(!r) return l; // p q在左子树  注意这里判断条件是先判断为空的, 不然走不到 l&&r
        if(l && r) return root; // p q在左右两个子树
        return nullptr;
    }

 记录父节点,然后遍历查找最后的一个共同的父节点。

class Solution {
public:
    unordered_map<int, TreeNode*> fa;
    unordered_map<int, bool> vi;
    void dfs(TreeNode* root){
        if(root->left){
            fa[root->left->val] = root;
            dfs(root->left);
        }
        if(root->right){
            fa[root->right->val] = root;
            dfs(root->right);
        }
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        fa[root->val] = nullptr;
        dfs(root);
        while(p != nullptr){
            vi[p->val] = true;
            p = fa[p->val];
        }
        while(q != nullptr){
            if(vi[q->val]) return q;
            q = fa[q->val];
        }
        return nullptr;
    }
};
中等
  1. 验证二叉搜索树 leetcode 98  递归解法:左子树的值要小于根节点的值,右子树的值要大于根节点的值
    bool helper(TreeNode* root, long upper, long lower){
        if(root == nullptr) return true;
        if(root->val >= upper || root->val <= lower) return false;
        return helper(root->left, root->val, lower) && helper(root->right, upper, root->val); // 左子树更新当前最大值, 右子树更新当前最小值
    }
    bool isValidBST(TreeNode* root) {
        return helper(root, LONG_MAX, LONG_MIN);
    }

 中序遍历判断是否有序

    bool isValidBST(TreeNode* root) {
        if(root == nullptr) return true;
        long prev = (long)INT_MIN-1;
        stack<TreeNode*> st;
        while(!st.empty() || root != nullptr){
            while(root != nullptr){
                st.push(root);
                root = root->left;
            }
            if(!st.empty()){
                root = st.top();
                st.pop();
                if(root->val <= prev) return false; // 前面一个值较大则不是二叉搜索树
                prev = root->val;
                root = root->right;
            }
        }
        return true;
    }
  1. 二叉搜索树的范围和 leetcode 938  这个题目很简单,但是题目没有说明白,乍一看很懵,看了题解才知道这样啊。如果根节点值>=L&& <=R 计算左右子树和,如果根节点值<L计算右子树,如果根节点值>R计算左子树即可,这样递归就很简单了。
class Solution {
public:
    int num = 0;
    void rangeSum(TreeNode* root, int L, int R){
        if(!root) return;
        if(root->val >= L && root->val <= R){
            num += root->val;
            rangeSum(root->right,L,R);
            rangeSum(root->left,L,R);
        }
        if(root->val > R){
            rangeSum(root->left,L,R);
        }
        if(root->val < L){
            rangeSum(root->right,L,R);
        }
    }
    int rangeSumBST(TreeNode* root, int L, int R) {
        rangeSum(root, L, R);
        return num;
    }
};

 非递归版本就更容易了,直接全部遍历然后计算和就行了,这里其实应该可以优化如果在L和R之间的去遍历就可以了,其他的就没必要处理了。

class Solution {
public:
    int num = 0;
    void rangeSum(TreeNode* root, int L, int R){
        if(!root) return;
        stack<TreeNode*> st;
        while(!st.empty() || root){
            while(root){
                st.push(root);
                root = root->left;
            }
            if(!st.empty()){
                root = st.top();
                st.pop();
                if(root->val >= L && root->val <= R){
                    num += root->val;
                }
                root = root->right;
            }
        } 
    }
    int rangeSumBST(TreeNode* root, int L, int R) {
        rangeSum(root, L, R);
        return num;
    }
};
  1. 存在重复元素 III leetcode 220  暴力算法求解,不能AC。
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        int nsize = nums.size();
        for(int i=0; i<nsize; i++){
            for(int j=max(i-k,0); j<i; j++){
                if(abs((long)nums[i] - nums[j]) <= (long)t){
                    return true;
                }
            }
        }
        return false;
    }

 暴力求解的缺点是在于查询的时候是线性查找的,用set做滑动窗口,查询使用平衡二叉树来解决每次查询k范围的问题,降低为查询O(logK); lower_bound返回的是map/set中小于等于给定值的最大值迭代器,upper_bound返回的大于给定值的第一个值得迭代器。

    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        set<long> window; // 滑动窗口
        int nsize = nums.size();
        for(int i=0; i<nsize; i++){
            auto it = window.lower_bound((long)nums[i]-t); // 找到大于或者等于 nums[i]-t 的最小的数字
            if(it != window.end() && *it <= (long)nums[i] + t){ // 如果找到了并且这个数字大于nums[i]+t
                return true;
            }
            window.insert(nums[i]);
            if(window.size() == k+1){
                window.erase(nums[i-k]);
            }
        }
        return false;
    }
困难
  1. 计算右侧小于当前元素的个数 leetcode 315  循环判断的暴力解法,不能AC:
    vector<int> countSmaller(vector<int>& nums) {
        int nsize = nums.size();
        vector<int> ret(nsize,0);
        for(int i = nsize-2; i>=0; i--){
            int count = 0;
            for(int j=nsize-1; j>i; j--){
                if(nums[j] < nums[i]){
                    count++;
                }
            }
            ret[i] = count;
        }
        return ret;
    }

 树状数组解法:

struct SegmentTreeNode{
    int start, end, count;
    SegmentTreeNode* left, *right;
    SegmentTreeNode(int _start, int _end):start(_start),end(_end),count(0),left(nullptr),right(nullptr){}
};
class Solution {
public:
    SegmentTreeNode* build(int start, int end){
        if(start > end) return nullptr;
        SegmentTreeNode* node = new SegmentTreeNode(start, end);
        if(start == end){
            node->count = 0;
        }
        else{
            int mid = (end - start)/2 + start;
            node->left  = build(start, mid);
            node->right = build(mid+1, end);
        }
        return node;
    }
    int count(SegmentTreeNode* node, int start, int end){
        if(node == nullptr || start > end) return 0;
        if(start == node->start && end == node->end) return node->count;
        int mid = node->start + (node->end - node->start)/2;
        int left_num = 0, right_num = 0;
        if(start <= mid){
            if(mid < end){
                left_num = count(node->left,start, mid);
            }
            else{
                left_num = count(node->left, start, end);
            }
        }
        if(mid < end){
            if(start <= mid){
                right_num = count(node->right,mid+1,end);
            }
            else{
                right_num = count(node->right,start,end);
            }
        }
        return left_num + right_num;
    }
    void insert(SegmentTreeNode* node, int index, int val){
        if(node->start == index && node->end == index){
            node->count += val;
            return;
        }
        int mid = node->start + (node->end - node->start)/2;
        if(index >= node->start && index<=mid){
            insert(node->left, index, val);
        }
        if(index > mid && index<=node->end){
            insert(node->right, index, val);
        }
        node->count =  node->left->count + node->right->count;
    }
    vector<int> countSmaller(vector<int>& nums) {
        int nsize = nums.size();
        vector<int> ret(nsize,0);
        if(nsize == 0) return ret;
        
        int n_min = nums[0], n_max = nums[0];
        for(int i=0; i<nsize; i++){
            n_min = min(n_min, nums[i]);
            n_max = max(n_max, nums[i]);
        }
        SegmentTreeNode* root = build(n_min, n_max);
        
        for(int i=nsize-1; i>=0; i--){
            ret[i] = count(root, n_min, nums[i]-1);
            insert(root, nums[i], 1);
        }
        return ret;
    }
    
};

 归并排序思路解法:

void mergeCount(vector<int>& nums, int start, int mid, int end, vector<int>& index, vector<int>& tmp, vector<int>& ret){
        for(int i=start; i<=end; i++) tmp[i] = index[i];
        int i = start, j = mid+1;
        for(int k=start; k<=end; k++){
            if(i>mid){
                index[k] = tmp[j];
                j++;
            }
            else if(j>end){
                index[k] = tmp[i];
                i++;
                ret[index[k]] += (end - mid);
            }else if(nums[tmp[i]] <= nums[tmp[j]]){
                index[k] = tmp[i];
                i++;
                ret[index[k]] += (j-mid-1);
            }
            else{
                index[k] = tmp[j];
                j++;
            }
        }
    }
    void mergeAndCountSmaller(vector<int>& nums, int start, int end, vector<int>& index, vector<int>& tmp, vector<int>& ret){
        if(start == end) return;
        int mid = start + (end - start)/2;
        mergeAndCountSmaller(nums, start, mid, index, tmp, ret);
        mergeAndCountSmaller(nums, mid+1, end, index, tmp, ret);
        if(nums[index[mid]] <= nums[index[mid+1]]) return;
        mergeCount(nums, start, mid, end, index, tmp, ret);
    }
    vector<int> countSmaller(vector<int>& nums) {
        int nsize = nums.size();
        vector<int> ret(nsize,0);

        if(nsize == 0) return ret;
        vector<int> tmp(nsize,0); // 归并临时空间
        vector<int> index(nsize,0); // 数组顺序
        for(int i=0; i<nsize; i++) index[i] = i;
        mergeAndCountSmaller(nums, 0, nsize-1, index, tmp, ret);
        return ret;
    }