力扣LeetCode-二叉树

二叉树

基本知识

1. 二叉树的递归遍历

  1. 前序遍历

    class Solution {
    public:
        void traversal(TreeNode* cur, vector& vec) {
            if (cur == NULL) return;
            vec.push_back(cur->val);    // 中
            traversal(cur->left, vec);  // 左
            traversal(cur->right, vec); // 右
        }
        vector preorderTraversal(TreeNode* root) {
            vector result;
            traversal(root, result);
            return result;
        }
    };
  2. 中序遍历

    void traversal(TreeNode* cur, vector& vec) {
        if (cur == NULL) return;
        traversal(cur->left, vec);  // 左
        vec.push_back(cur->val);    // 中
        traversal(cur->right, vec); // 右
    }
  3. 后序遍历

    void traversal(TreeNode* cur, vector& vec) {
        if (cur == NULL) return;
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
        vec.push_back(cur->val);    // 中
    }

2. 二叉树的迭代遍历

  1. 前序遍历

    class Solution {
    public:
        vector preorderTraversal(TreeNode* root) {
            vector result;
            stack st;
            if (root != NULL) st.push(root);
            while (!st.empty()) {
                TreeNode* node = st.top();
                if (node != NULL) {
                    st.pop();
                    if (node->right) st.push(node->right);  // 右
                    if (node->left) st.push(node->left);    // 左
                    st.push(node);                          // 中
                    st.push(NULL);
                } else {
                    st.pop();
                    node = st.top();
                    st.pop();
                    result.push_back(node->val);
                }
            }
            return result;
        }
    };
  2. 中序遍历

    class Solution {
    public:
        vector inorderTraversal(TreeNode* root) {
            vector result;
            stack st;
            if (root != NULL) st.push(root);
            while (!st.empty()) {
                TreeNode* node = st.top();
                if (node != NULL) {
                    st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                    if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈)
    ​
                    st.push(node);                          // 添加中节点
                    st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。
    ​
                    if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
                } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                    st.pop();           // 将空节点弹出
                    node = st.top();    // 重新取出栈中元素
                    st.pop();
                    result.push_back(node->val); // 加入到结果集
                }
            }
            return result;
        }
    };
  3. 后序遍历

    class Solution {
    public:
        vector postorderTraversal(TreeNode* root) {
            vector result;
            stack st;
            if (root != NULL) st.push(root);
            while (!st.empty()) {
                TreeNode* node = st.top();
                if (node != NULL) {
                    st.pop();
                    st.push(node);                          // 中
                    st.push(NULL);
    ​
                    if (node->right) st.push(node->right);  // 右
                    if (node->left) st.push(node->left);    // 左
    ​
                } else {
                    st.pop();
                    node = st.top();
                    st.pop();
                    result.push_back(node->val);
                }
            }
            return result;
        }
    };

3. 二叉树的层序遍历

class Solution {
public:
    vector> levelOrder(TreeNode* root) {
        queue que;
        if (root != NULL) que.push(root);
        vector> result;
        while (!que.empty()) {
            int size = que.size();
            vector vec;
            // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }
};

典型例题

1. LeetCode 101.对称二叉树

题目

给定一个二叉树,检查它是否是镜像对称的。

思路

方法一:采用递归法,遍历根节点左右两棵树,对比相对应节点是否都相等;

方法二:采用迭代法,用同一个栈同时存入取出左右两棵树的节点指针,检查是否相等;

代码

方法一

class Solution {
public:
    bool compare(TreeNode*left, TreeNode*right){
        if(!left&&right)return false;
        else if(left&&!right)return false;
        else if(!left&&!right)return true;
        else if(left->val!=right->val)return false;
        
​
        return (compare(left->left, right->right) && compare(left->right, right->left));
    }
    bool isSymmetric(TreeNode* root) {
        if(!root)return true;
        else return compare(root->left, root->right);
    }
};

方法二

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
       stackst;
       if(!root)return true;
       st.push(root->left);
       st.push(root->right);
       while(!st.empty()){
           TreeNode*right = st.top();
           st.pop();
           TreeNode*left = st.top();
           st.pop();
           if(!left&&right)return false;
           if(left&&!right)return false;
           if(!left&&!right) continue;
           if(left->val!=right->val)return false;

           st.push(left->left);
           st.push(right->right);
           st.push(left->right);
           st.push(right->left);
       }
       return true;
    }
};

2. LeetCode 110.平衡二叉树

题目

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

思路

  1. 函数返回该节点下树的深度;

  2. 若该节点左右子树已判断不平衡则返回-1;

代码

方法一(递归)

class Solution {
public:
    int getDepth(TreeNode*root){
        if(!root)return 0;
        int left_depth = getDepth(root->left);
        if(left_depth == -1)return -1;
        int right_depth = getDepth(root -> right);
        if(right_depth == -1)return -1;
        return abs(left_depth-right_depth)>1?-1:max(left_depth, right_depth)+1;
    }
    bool isBalanced(TreeNode* root) {
        return getDepth(root)==-1?false:true;
    }
};

方法二(迭代)

  1. 构造求深度函数;

  2. 对每一个节点对比其左右子树深度是否平衡;

class Solution {
private:
    int getDepth(TreeNode* cur) {
        stack st;
        if (cur != NULL) st.push(cur);
        int depth = 0; // 记录深度
        int result = 0;
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                st.push(node);                          // 中
                st.push(NULL);
                depth++;
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左

            } else {
                st.pop();
                node = st.top();
                st.pop();
                depth--;
            }
            result = result > depth ? result : depth;
        }
        return result;
    }

public:
    bool isBalanced(TreeNode* root) {
        stack st;
        if (root == NULL) return true;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // 中
            st.pop();
            if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {
                return false;
            }
            if (node->right) st.push(node->right);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return true;
    }
};

3. LeetCode 257.二叉树的所有路径

题目

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

思路

  1. 遍历到一个新的节点时,先将该节点加入到路径;

  2. 若该节点无子节点,则找到一个到达叶节点的路径,加入到最终结果中;

  3. 若该节点有子节点,则将路径添加箭头后遍历子节点;

代码

方法一(递归法)

class Solution {
public:
    void traverse(TreeNode*p, string path, vector&result){
        path += to_string(p->val);
        if(!p->left&&!p->right){
            result.push_back(path);
            return ;
        }
        if(p->left){
            traverse(p->left, path + "->", result);
        }
        if(p->right){
            traverse(p->right, path+"->", result);
        }
    }

    vector binaryTreePaths(TreeNode* root) {
        vectorresult;
        string path;
        if(!root)return result;
        traverse(root, path, result);
        return result;
    }
};

方法二(迭代法)

class Solution {
public:
    vector binaryTreePaths(TreeNode* root) {
        stack treeSt;// 保存树的遍历节点
        stack pathSt;   // 保存遍历路径的节点
        vector result;  // 保存最终路径集合
        if (root == NULL) return result;
        treeSt.push(root);
        pathSt.push(to_string(root->val));
        while (!treeSt.empty()) {
            TreeNode* node = treeSt.top(); treeSt.pop(); // 取出节点 中
            string path = pathSt.top();pathSt.pop();    // 取出该节点对应的路径
            if (node->left == NULL && node->right == NULL) { // 遇到叶子节点
                result.push_back(path);
            }
            if (node->right) { // 右
                treeSt.push(node->right);
                pathSt.push(path + "->" + to_string(node->right->val));
            }
            if (node->left) { // 左
                treeSt.push(node->left);
                pathSt.push(path + "->" + to_string(node->left->val));
            }
        }
        return result;
    }
};

4. LeetCode 404.左叶子之和

题目

计算给定二叉树的所有左叶子之和。

思路

方法一(递归法)

  1. 遇到叶节点时返回0;

  2. 遇到非叶节点时,判断其是否为左叶节点的父节点,若是则返回该值与左子树递归值(实际为0)与右子树递归值之和,若不是则返回左子树递归值(可能非0)与右子树递归值之和;

方法二(迭代法)

  1. 正常对树进行遍历;

  2. 对于每一个节点,判断若为左叶节点父节点,则累加左叶节点值;

代码

方法一

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == NULL) return 0;
        int midValue = 0;
        if (root->left != NULL && root->left->left == NULL && root->left->right == NULL) {
            midValue = root->left->val;
        }
        return midValue + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
    }
};

方法二

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        stack st;
        if (root == NULL) return 0;
        st.push(root);
        int result = 0;
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
                result += node->left->val;
            }
            if (node->right) st.push(node->right);
            if (node->left) st.push(node->left);
        }
        return result;
    }
};

5. LeetCode 112.路径总和

题目

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一(递归)

  1. 若到达叶节点时,判断如果目标值减为0,则返回true,否则返回false;

  2. 若到达非叶节点时,返回左右子树递归结果或值;

方法二(迭代)

  1. 构造元素为pair类型的栈,pair元素前项为节点指针,后项为从根节点到该节点之前路径节点值之和;

  2. 正常进行前序遍历,若到达叶节点且与目标值相等,则返回true;

  3. 遍历结束后仍然未找到相等的叶节点,则返回false;

代码

方法一(递归)

class solution {
public:
    bool haspathsum(treenode* root, int sum) {
        if (root == null) return false;
        if (!root->left && !root->right && sum == root->val) {
            return true;
        }
        return haspathsum(root->left, sum - root->val) || haspathsum(root->right, sum - root->val);
    }
};

方法二(迭代)

class solution {

public:
    bool haspathsum(treenode* root, int sum) {
        if (root == null) return false;
        // 此时栈里要放的是pair<节点指针,路径数值>
        stack> st;
        st.push(pair(root, root->val));
        while (!st.empty()) {
            pair node = st.top();
            st.pop();
            // 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
            if (!node.first->left && !node.first->right && sum == node.second) return true;

            // 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if (node.first->right) {
                st.push(pair(node.first->right, node.second + node.first->right->val));
            }

            // 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if (node.first->left) {
                st.push(pair(node.first->left, node.second + node.first->left->val));
            }
        }
        return false;
    }
};

6. LeetCode 106.从中序与后序遍历序列构造二叉树

题目

根据一棵树的中序遍历与后序遍历构造二叉树。

思路

方法一

  1. 利用递归函数根据前序遍历数组与中序遍历数组所需元素下标范围,构造该范围内的节点;

  2. 判断前序遍历范围,若左右下标相等则返回NULL,若仅相差1,说明该节点为叶节点,直接构造该节点进行返回;

  3. 若前序遍历范围,左右下标相差大于1,说明该节点存在子节点,则以前序数组范围左边界元素为分界点用于分割中序数组,在中序数组所给范围中遍历中序数组寻找该数值得到对应值下标;

  4. 根据对应值的下标计算该节点左子树与右子树的中序数组与前序数组下标范围(前序数组下标范围的计算可利用中序数组已知的左子树与右子树中节点个数);

  5. 利用递归构造该节点的左子树与右子树;

方法二

  1. 利用全局变量pre_idx保存递归过程中前序数组的遍历位置,全局变量in_order用于保存中序数组中元素值与其下标之间的对应关系,键为元素值,值为元素下标;

  2. 利用递归函数根据前序遍历数组所需元素范围,构造该范围内的节点;

  3. 每次递归就按照pre_idx递增的顺序向外取值,该值即为中序数组的分界值,也为该节点值,构造该节点,按照in_order的对应找到中序数组中的对应下标,递归构造该节点的左子树与右子树;

  4. 之所以可以根据前序遍历顺序依次选值,是因为按照该顺序每选择出的一个值都是按照递归顺序进行构造中序遍历数组范围中所需要的分界点;

代码

方法一

class Solution {
public:
    TreeNode* traversal(vector& preorder, int preorderBegin, int preorderEnd, vector& inorder, int inorderBegin, int inorderEnd){
        if(preorderBegin == preorderEnd)return NULL;
        int root_val = preorder[preorderBegin];
        TreeNode*root =new TreeNode(root_val);
        if(preorderEnd - preorderBegin == 1)return root;
        int cut;
        for(cut = inorderBegin; cut < inorderEnd; cut++){
            if(inorder[cut] == root_val)break;
        }
        int left_inorder_begin = inorderBegin;
        int left_inorder_end = cut;
        int right_inorder_begin = cut+1;
        int right_inorder_end = inorderEnd;

        int left_preorder_begin = preorderBegin+1;
        int left_preorder_end = preorderBegin+1+cut - left_inorder_begin;
        int right_preorder_begin = left_preorder_end;
        int right_preorder_end = preorderEnd;

        root->left = traversal(preorder, left_preorder_begin, left_preorder_end, inorder, left_inorder_begin, left_inorder_end);
        root->right = traversal(preorder, right_preorder_begin, right_preorder_end, inorder, right_inorder_begin, right_inorder_end);
        return root;
    }

    TreeNode* buildTree(vector& preorder, vector& inorder) {
        if(inorder.size() == 0 || preorder.size() == 0) return NULL;

        return traversal(preorder, 0, preorder.size(), inorder, 0, inorder.size());
    }
};

方法二

class Solution {
    int pre_idx;
    unordered_mapin_order;
    
public:
    TreeNode* creat(int inleft,int inright,vectorinorder,vectorpreorder){
    if(inleft>inright)
    return nullptr;
    int root_val=preorder[pre_idx];
    TreeNode *root=new TreeNode(root_val);
    int index=in_order[root_val];
    pre_idx++;
    root->left=creat(inleft,index-1,inorder,preorder);
    root->right=creat(index+1,inright,inorder,preorder);
    return root;
    }
    
    TreeNode* buildTree(vectorpreorder,vectorinorder){
    pre_idx=0;
    int idx=0;
    int i;
    for(auto &i:inorder){
        in_order[i]=idx++;
    }
    return creat(pre_idx,preorder.size()-1,inorder,preorder);
    }
};

7. LeetCode 617.合并二叉树

题目

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一(递归)

  1. 利用递归函数构造某一个节点两个二叉树对应位置的合并;

  2. 若两棵二叉树中某一个节点为空时,则直接返回另一棵二叉树;

  3. 若都非空时,则将两个节点值相加作为新节点值,并递归构造该节点的左子树与右子树;

方法二(迭代)

  1. 采用层序遍历,且每次循环中只处理队列中的一个节点;

  2. 处理每一个节点时,将两棵树的该节点值进行相加;

  3. 当两棵树的对应该节点的左节点都非空时,则将这两个节点都加入两棵树的对应队列,右节点都非空时同理;

  4. 若第一棵树的左节点为空,而第二棵树的左节点非空时,将第二棵树的左节点指针直接赋给第一棵树的左节点指针,右节点同理;

  5. 遍历结束时返回根节点;

代码

方法一(递归)

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
        if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
        // 修改了t1的数值和结构
        t1->val += t2->val;                             // 中
        t1->left = mergeTrees(t1->left, t2->left);      // 左
        t1->right = mergeTrees(t1->right, t2->right);   // 右
        return t1;
    }
};

方法二(迭代)

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL) return t2;
        if (t2 == NULL) return t1;
        queue que;
        que.push(t1);
        que.push(t2);
        while(!que.empty()) {
            TreeNode* node1 = que.front(); que.pop();
            TreeNode* node2 = que.front(); que.pop();
            // 此时两个节点一定不为空,val相加
            node1->val += node2->val;

            // 如果两棵树左节点都不为空,加入队列
            if (node1->left != NULL && node2->left != NULL) {
                que.push(node1->left);
                que.push(node2->left);
            }
            // 如果两棵树右节点都不为空,加入队列
            if (node1->right != NULL && node2->right != NULL) {
                que.push(node1->right);
                que.push(node2->right);
            }

            // 当t1的左节点 为空 t2左节点不为空,就赋值过去
            if (node1->left == NULL && node2->left != NULL) {
                node1->left = node2->left;
            }
            // 当t1的右节点 为空 t2右节点不为空,就赋值过去
            if (node1->right == NULL && node2->right != NULL) {
                node1->right = node2->right;
            }
        }
        return t1;
    }
};

8. LeetCode 700.二叉搜索树中的搜索

题目

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

思路

方法一(递归)

  1. 按照二叉搜索树的性质递归寻找节点;

  2. 找到则直接返回该节点,若找到叶节点仍然未找到则直接返回NULL;

方法二(迭代)

  1. 只需要按照当前节点值与目标值就可以判断下一步节点的方向,一条线路,因此迭代方法较为简单;

代码

方法一(递归)

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (root == NULL || root->val == val) return root;
        if (root->val > val) return searchBST(root->left, val);
        else return searchBST(root->right, val);
    }
};

方法二(迭代)

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        while (root != NULL) {
            if (root->val > val) root = root->left;
            else if (root->val < val) root = root->right;
            else return root;
        }
        return NULL;
    }
};

9. LeetCode 98.验证二叉搜索树

题目

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 中序遍历二叉搜索树,将各节点依次存储进数组;

  2. 判断生成的数组是否递增;

方法二

  1. 采用全局变量pre存储中序遍历前一个节点;

  2. 按照中序遍历判断对于每一个节点是否都大于前一个节点(第一个节点除外);

  3. 递归返回左子树与右子树的判断结果,返回取与操作后值;

方法三

  1. 采用迭代法利用栈进行中序遍历,采用pre记录前一个节点;

  2. 判断当前节点值是否都大于等于前一个节点(第一个节点除外),否则返回false;

  3. 如果遍历正常结束,返回true;

代码

方法一

class Solution {
private:
    vector vec;
    void traversal(TreeNode* root) {
        if (root == NULL) return;
        traversal(root->left);
        vec.push_back(root->val); // 将二叉搜索树转换为有序数组
        traversal(root->right);
    }
public:
    bool isValidBST(TreeNode* root) {
        vec.clear(); // 不加这句在leetcode上也可以过,但最好加上
        traversal(root);
        for (int i = 1; i < vec.size(); i++) {
            // 注意要小于等于,搜索树里不能有相同元素
            if (vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
};

方法二

class Solution {
public:
    TreeNode* pre = NULL; // 用来记录前一个节点
    bool isValidBST(TreeNode* root) {
        if (root == NULL) return true;
        bool left = isValidBST(root->left);

        if (pre != NULL && pre->val >= root->val) return false;
        pre = root; // 记录前一个节点

        bool right = isValidBST(root->right);
        return left && right;
    }
};

方法三

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        stack st;
        TreeNode* cur = root;
        TreeNode* pre = NULL; // 记录前一个节点
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) {
                st.push(cur);
                cur = cur->left;                // 左
            } else {
                cur = st.top();                 // 中
                st.pop();
                if (pre != NULL && cur->val <= pre->val)
                return false;
                pre = cur; //保存前一个访问的结点

                cur = cur->right;               // 右
            }
        }
        return true;
    }
};

10. LeetCode 501. 二叉搜索树中的众数

题目

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

假定 BST 有如下定义:

结点左子树中所含结点的值小于等于当前结点的值 结点右子树中所含结点的值大于等于当前结点的值 左子树和右子树都是二叉搜索树

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 适用于普遍二叉树;

  2. 采用unorded_map类型变量map记录每个节点值出现次数,键为节点值,值为该值出现次数;

  3. 利用递归遍历整棵树,完善map变量;

  4. 对map进行排序,取出现次数最多的值加入最终结果;

方法二

  1. 中序遍历整棵树,pre记录前一个节点,maxcount记录最大出现次数,count记录目前值出现次数;

  2. 对于第一个节点,其pre为NULL,则将count设置为1,对于剩余节点,若该节点值与前一个节点值相等,则count加1,否则count赋值为1;

  3. 若count值等于maxcount最大出现次数,说明可能存在多个众数,则将该值加入最终结果,若count大于maxcount,则更新maxcount,清空最终结果result,并将该值加入最终结果;

  4. 遍历结束,返回result;

方法三

  1. 采用迭代法进行树的中序遍历,其余处理与方法二相同;

代码

方法一

class Solution {
private:

void searchBST(TreeNode* cur, unordered_map& map) { // 前序遍历
    if (cur == NULL) return ;
    map[cur->val]++; // 统计元素频率
    searchBST(cur->left, map);
    searchBST(cur->right, map);
    return ;
}
bool static cmp (const pair& a, const pair& b) {
    return a.second > b.second;
}
public:
    vector findMode(TreeNode* root) {
        unordered_map map; // key:元素,value:出现频率
        vector result;
        if (root == NULL) return result;
        searchBST(root, map);
        vector> vec(map.begin(), map.end());
        sort(vec.begin(), vec.end(), cmp); // 给频率排个序
        result.push_back(vec[0].first);
        for (int i = 1; i < vec.size(); i++) {
            // 取最高的放到result数组中
            if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
            else break;
        }
        return result;
    }
};

方法二

class Solution {
private:
    int maxCount; // 最大频率
    int count; // 统计频率
    TreeNode* pre;
    vector result;
    void searchBST(TreeNode* cur) {
        if (cur == NULL) return ;

        searchBST(cur->left);       // 左
                                    // 中
        if (pre == NULL) { // 第一个节点
            count = 1;
        } else if (pre->val == cur->val) { // 与前一个节点数值相同
            count++;
        } else { // 与前一个节点数值不同
            count = 1;
        }
        pre = cur; // 更新上一个节点

        if (count == maxCount) { // 如果和最大值相同,放进result中
            result.push_back(cur->val);
        }

        if (count > maxCount) { // 如果计数大于最大值频率
            maxCount = count;   // 更新最大频率
            result.clear();     // 很关键的一步,不要忘记清空result,之前result里的元素都失效了
            result.push_back(cur->val);
        }

        searchBST(cur->right);      // 右
        return ;
    }

public:
    vector findMode(TreeNode* root) {
        count = 0;
        maxCount = 0;
        TreeNode* pre = NULL; // 记录前一个节点
        result.clear();

        searchBST(root);
        return result;
    }
};

方法三

class Solution {
public:
    vector findMode(TreeNode* root) {
        stack st;
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        int maxCount = 0; // 最大频率
        int count = 0; // 统计频率
        vector result;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                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;
                }
                if (count == maxCount) { // 如果和最大值相同,放进result中
                    result.push_back(cur->val);
                }

                if (count > maxCount) { // 如果计数大于最大值频率
                    maxCount = count;   // 更新最大频率
                    result.clear();     // 很关键的一步,不要忘记清空result,之前result里的元素都失效了
                    result.push_back(cur->val);
                }
                pre = cur;
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

11. LeetCode 236.二叉树的最近公共祖先

题目

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

  1. 以递归返回值方式传递该节点左右子树中是否存在p、q节点,当返回值为空时,表示该子树未找到,非空时表示找到;

  2. 当左右子树均返回非空时,表示p、q分别来自左右子树,则该节点为公共祖先;

  3. 当左子树返回为空,右子树非空时,表示左子树未找到,右子树找到节点,此时存在两种情况,一种为右子树中找到一个节点,另一种为右子树中找到两个节点,但不论哪种情况,都可以直接返回右子树这个非空值,传递该子树已找到节点的信息;

    • 对于第一种情况,右子树找到一个节点,继续向上传递过程中一定可以找到汇总节点,在汇总节点进行处理即可;

    • 对于第二种情况,就是直接向上返回汇总节点,传递信息即可;

  4. 当左右子树返回都为空时,直接返回NULL;

代码

class Solution {
public:
    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;
        if(!left && right)return right;
        if(left && !right)return left;
        return NULL;
    }
};

12. LeetCode 701.二叉搜索树中的插入操作

题目

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 采用带返回值的递归函数;

  2. 寻找节点应该出现的位置,构造节点,并返回;

方法二

  1. 采用不带返回值的递归函数;

  2. 采用全局变量parent记录当前节点的父节点,以便于在到达空节点时创建节点并将其与父节点联系在一起;

  3. 由于没有返回值,所以当原树的树根为空时,需要在主函数中单独处理,创建只含有这一个节点的树并返回;

方法三

  1. 采用迭代法寻找到插入节点的位置,需要用parent指针记录每个节点的父节点,用于最终插入;

  2. 创建目标节点,与parent节点比较后插入到其左边或右边;

代码

方法一

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == NULL) {
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if (root->val > val) root->left = insertIntoBST(root->left, val);
        if (root->val < val) root->right = insertIntoBST(root->right, val);
        return root;
    }
};

方法二

class Solution {
private:
    TreeNode* parent;
    void traversal(TreeNode* cur, int val) {
        if (cur == NULL) {
            TreeNode* node = new TreeNode(val);
            if (val > parent->val) parent->right = node;
            else parent->left = node;
            return;
        }
        parent = cur;
        if (cur->val > val) traversal(cur->left, val);
        if (cur->val < val) traversal(cur->right, val);
        return;
    }

public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        parent = new TreeNode(0);
        if (root == NULL) {
            root = new TreeNode(val);
        }
        traversal(root, val);
        return root;
    }
};

方法三

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == NULL) {
            TreeNode* node = new TreeNode(val);
            return node;
        }
        TreeNode* cur = root;
        TreeNode* parent = root; // 这个很重要,需要记录上一个节点,否则无法赋值新节点
        while (cur != NULL) {
            parent = cur;
            if (cur->val > val) cur = cur->left;
            else cur = cur->right;
        }
        TreeNode* node = new TreeNode(val);
        if (val < parent->val) parent->left = node;// 此时是用parent节点的进行赋值
        else parent->right = node;
        return root;
    }
};

13. LeetCode 450.删除二叉搜索树中的节点

题目

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点; 如果找到了,删除它。

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 采用递归方式寻找指定节点;

  2. 删除节点时有以下五种情况:

    • 第一种情况:没找到删除的节点,遍历到空节点直接返回;

    • 找到删除的节点

      • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点

      • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点

      • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点

      • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

方法二

  1. 普通二叉树的删除方式(没有使用搜索树的特性,遍历整棵树),用交换值的操作来删除目标节点。

  2. 代码中目标节点(要删除的节点)被操作了两次:

    • 第一次是和目标节点的右子树最左面节点交换。

    • 第二次直接被NULL覆盖了。

方法三

  1. 采用迭代法;

  2. 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上;

  3. 并返回目标节点右孩子为新的根节点;

代码

方法一

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr) return root; // 第一种情况:没找到删除的节点,遍历到空节点直接返回了
        if (root->val == key) {
            // 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
            if (root->left == nullptr && root->right == nullptr) {
                ///! 内存释放
                delete root;
                return nullptr;
            }
            // 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点
            else if (root->left == nullptr) {
                auto retNode = root->right;
                ///! 内存释放
                delete root;
                return retNode;
            }
            // 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
            else if (root->right == nullptr) {
                auto retNode = root->left;
                ///! 内存释放
                delete root;
                return retNode;
            }
            // 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置
            // 并返回删除节点右孩子为新的根节点。
            else {
                TreeNode* cur = root->right; // 找右子树最左面的节点
                while(cur->left != nullptr) {
                    cur = cur->left;
                }
                cur->left = root->left; // 把要删除的节点(root)左子树放在cur的左孩子的位置
                TreeNode* tmp = root;   // 把root节点保存一下,下面来删除
                root = root->right;     // 返回旧root的右孩子作为新root
                delete tmp;             // 释放节点内存(这里不写也可以,但C++最好手动释放一下吧)
                return root;
            }
        }
        if (root->val > key) root->left = deleteNode(root->left, key);
        if (root->val < key) root->right = deleteNode(root->right, key);
        return root;
    }
};

方法二

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr) return root;
        if (root->val == key) {
            if (root->right == nullptr) { // 这里第二次操作目标值:最终删除的作用
                return root->left;
            }
            TreeNode *cur = root->right;
            while (cur->left) {
                cur = cur->left;
            }
            swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。
        }
        root->left = deleteNode(root->left, key);
        root->right = deleteNode(root->right, key);
        return root;
    }
};

方法三

class Solution {
private:
    // 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上
    // 并返回目标节点右孩子为新的根节点
    // 是动画里模拟的过程
    TreeNode* deleteOneNode(TreeNode* target) {
        if (target == nullptr) return target;
        if (target->right == nullptr) return target->left;
        TreeNode* cur = target->right;
        while (cur->left) {
            cur = cur->left;
        }
        cur->left = target->left;
        return target->right;
    }
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr) return root;
        TreeNode* cur = root;
        TreeNode* pre = nullptr; // 记录cur的父节点,用来删除cur
        while (cur) {
            if (cur->val == key) break;
            pre = cur;
            if (cur->val > key) cur = cur->left;
            else cur = cur->right;
        }
        if (pre == nullptr) { // 如果搜索树只有头结点
            return deleteOneNode(cur);
        }
        // pre 要知道是删左孩子还是右孩子
        if (pre->left && pre->left->val == key) {
            pre->left = deleteOneNode(cur);
        }
        if (pre->right && pre->right->val == key) {
            pre->right = deleteOneNode(cur);
        }
        return root;
    }
};

14. LeetCode 669.修剪二叉搜索树

题目

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 采用递归法对整棵树每个合法节点的左右子树进行重新划分;

  2. 利用递归函数的返回值直接返回以该节点为根节点的子树中符合要求的小子树,替代以该节点为根节点的大树;

  3. 若目前节点合法,则将其左右递归子树的返回值赋值给该节点的左右指针,返回该节点的指针;

  4. 若目前节点小于左边界值,则其左子树中一定不存在符合要求的节点,而右子树中仍然可能存在合法的节点,直接返回右子树的递归返回值;

  5. 若目前节点大于右边界值,则其右子树中一定不存在符合要求的节点,而左子树中仍然可能存在合法的节点,直接返回左子树的递归返回值;

方法二

  1. 采用迭代法对树进行剪枝;

  2. 在剪枝的时候,可以分为三步:

    • 将root移动到[L, R] 范围内,注意是左闭右闭区间

    • 剪枝左子树

    • 剪枝右子树

代码

方法一

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root == nullptr) return nullptr;
        if (root->val < low) return trimBST(root->right, low, high);
        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;
    }
};

方法二

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int L, int R) {
        if (!root) return nullptr;

        // 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
        while (root != nullptr && (root->val < L || root->val > R)) {
            if (root->val < L) root = root->right; // 小于L往右走
            else root = root->left; // 大于R往左走
        }
        TreeNode *cur = root;
        // 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况
        while (cur != nullptr) {
            while (cur->left && cur->left->val < L) {
                cur->left = cur->left->right;
            }
            cur = cur->left;
        }
        cur = root;

        // 此时root已经在[L, R] 范围内,处理右孩子大于R的情况
        while (cur != nullptr) {
            while (cur->right && cur->right->val > R) {
                cur->right = cur->right->left;
            }
            cur = cur->right;
        }
        return root;
    }
};

15. LeetCode 108.将有序数组转换为二叉搜索树

题目

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 采用递归函数构造左右边界范围内的子树(左右边界采用左闭右开);

  2. 每次递归时按照该范围中的中间值作为父节点进行划分,并将中间值左右两部分范围递归调用返回值赋值给该节点的左右指针,返回该节点指针;

方法二

  1. 迭代法

  2. 通过三个队列来模拟,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。

代码

方法一

class Solution {
private:
    TreeNode* traversal(vector& nums, int left, int right) {
        if (left > right) return nullptr;
        int mid = left + ((right - left) / 2);
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = traversal(nums, left, mid - 1);
        root->right = traversal(nums, mid + 1, right);
        return root;
    }
public:
    TreeNode* sortedArrayToBST(vector& nums) {
        TreeNode* root = traversal(nums, 0, nums.size() - 1);
        return root;
    }
}

方法二

class Solution {
public:
    TreeNode* sortedArrayToBST(vector& nums) {
        if (nums.size() == 0) return nullptr;

        TreeNode* root = new TreeNode(0);   // 初始根节点
        queue nodeQue;           // 放遍历的节点
        queue leftQue;                 // 保存左区间下标
        queue rightQue;                // 保存右区间下标
        nodeQue.push(root);                 // 根节点入队列
        leftQue.push(0);                    // 0为左区间下标初始位置
        rightQue.push(nums.size() - 1);     // nums.size() - 1为右区间下标初始位置

        while (!nodeQue.empty()) {
            TreeNode* curNode = nodeQue.front();
            nodeQue.pop();
            int left = leftQue.front(); leftQue.pop();
            int right = rightQue.front(); rightQue.pop();
            int mid = left + ((right - left) / 2);

            curNode->val = nums[mid];       // 将mid对应的元素给中间节点

            if (left <= mid - 1) {          // 处理左区间
                curNode->left = new TreeNode(0);
                nodeQue.push(curNode->left);
                leftQue.push(left);
                rightQue.push(mid - 1);
            }

            if (right >= mid + 1) {         // 处理右区间
                curNode->right = new TreeNode(0);
                nodeQue.push(curNode->right);
                leftQue.push(mid + 1);
                rightQue.push(right);
            }
        }
        return root;
    }
};

16. LeetCode 538.把二叉搜索树转换为累加树

题目

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。 节点的右子树仅包含键 大于 节点键的节点。 左右子树也必须是二叉搜索树。 注意:本题和 1038: 力扣 相同

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

方法一

  1. 利用pre变量记录前一个节点的值;

  2. 采用递归法反中序遍历整棵树,对于每个节点累加上前一个节点值;

方法二

  1. 采用迭代法反中序遍历整棵树;

  2. 同样采用pre记录前一个节点的值;

代码

方法一

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

方法二

class Solution {
private:
    int pre; // 记录前一个节点的数值
    void traversal(TreeNode* root) {
        stack st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) {
                st.push(cur);
                cur = cur->right;   // 右
            } else {
                cur = st.top();     // 中
                st.pop();
                cur->val += pre;
                pre = cur->val;
                cur = cur->left;    // 左
            }
        }
    }
public:
    TreeNode* convertBST(TreeNode* root) {
        pre = 0;
        traversal(root);
        return root;
    }
};

你可能感兴趣的:(LeetCode,leetcode,c++)