算法经典面试题(5)——二叉树

*题目编号为Leetcode中对应的题号。
某位大佬的Leetcode题解参考链接

二叉树

  • 完全二叉树:除了最后一层,所有层的节点数达到最大,最后一层的所有节点在最左侧。(堆就是二叉树)

  • 满二叉树:所有层的节点数达到最大。

  • 平衡二叉树:没一个节点的左右子树的高度差不超过1.

  • 二分搜索树:每个节点的键值大于左孩子、小于右孩子,以左右孩子为根的子树仍然为二分搜索树。

  • 二分搜索树中的一个节点大于其右子树中的所有节点,小于其左子树中的所有节点。

  • 具有天然递归条件

        // 前序遍历
        void preorder(TreeNode* node){
          
          
            if(node == NULL)				// 递归终止条件
                return;
            
            cout<<node->val<<endl;			// 递归过程
            preorder(node->left);
            preorder(node->right);
        }
    
  • 无非是广度优先遍历和深度优先遍历;与高度有关、有路径有关

  1. (104二叉树的最大深度) 给定一个二叉树,找出其最大深度。

    二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

    说明: 叶子节点是指没有子节点的节点。

        int maxDepth(TreeNode* root) {
          
          
            if(root==NULL)
            return 0;
            
            int depth_l=0, depth_r=0;
            if(root->left)
                depth_l=maxDepth(root->left);
            if(root->right)
                depth_r=maxDepth(root->right);
        
            return max(depth_l,depth_r)+1;
        }
    
  2. (111二叉树的最小深度) 给定一个二叉树,找出其最小深度。

    最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

        class Solution {
          
          
        public:
            int minDepth(TreeNode* root) {
          
          
        
                if(root == NULL)
                    return 0;
        
                if(root->left == NULL && root->right == NULL)
                return 1;
        
            int ret = INT_MAX;
                if(root->left)
                    ret = min(ret, 1 + minDepth(root->left));
                if(root->right)
                    ret = min(ret, 1 + minDepth(root->right));
                return ret;
            }
        };
    
  3. (226翻转二叉树) 翻转一棵二叉树。

    示例:

    输入:
         4
       /   \
      2     7
     / \   / \
    1   3 6   9
    输出:
         4
       /   \
      7     2
     / \   / \
    9   6 3   1
    
        TreeNode* invertTree(TreeNode* root) {
          
          
        	if(root==NULL)
                return NULL;
            
            invertTree(root->left);
            invertTree(root->right);
            swap(root->left,root->right);
            return root;
        }
    
  4. (100相同的二叉树) 给定两个二叉树,编写一个函数来检验它们是否相同。

    如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

        class Solution {
          
          
        public:
            bool isSameTree(TreeNode* p, TreeNode* q) {
          
          
        
                if(!p && !q) return true;
                if(!p || !q) return false;
                if(p->val != q->val) return false;
                return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
            }
        };
    
  5. (102二叉树的层序遍历)

        // 基础层序遍历
        // 使用队列实现BFS
        vector<int> levelOrder(TreeNode* root) {
          
          
            vector<int> ret;
            if (root == NULL)
                return ret;
        
            queue<TreeNode*> q;
            q.push(root);
            while (!q.empty())
            {
          
          
                TreeNode* node = q.front();
                ret.push_back(node->val);
                q.pop();
        
                if (node->left)
                    q.push(node->left);
                if (node->right)
                    q.push(node->right);
            }
            return ret;
        }
        // 使用队列实现
        vector<vector<int>> levelOrder(TreeNode* root) {
          
          
        
            vector<vector<int>> res;
            if(root == NULL)
                return res;
        
            queue<pair<TreeNode*,int>> q;
            q.push(make_pair(root, 0));
        
            while(!q.empty()){
          
          
        
                TreeNode* node = q.front().first;
                int level = q.front().second;
                q.pop();
        
                if(level == res.size())
                    res.push_back(vector<int>());
                assert( level < res.size() );
        
                res[level].push_back(node->val);
                if(node->left)
                    q.push(make_pair(node->left, level + 1 ));
                if(node->right)
                    q.push(make_pair(node->right, level + 1 ));
            }
        
            return res;
        }
        // 使用递归实现
        class Solution {
          
          
        public:
            vector<vector<int>> levelOrder(TreeNode* root) {
          
          
                vector<vector<int>> ans;
                pre(root, 0, ans);
                return ans;
            }
            
            void pre(TreeNode *root, int depth, vector<vector<int>> &ans) {
          
          
                if (!root) return ;
                if (depth >= ans.size())
                    ans.push_back(vector<int>());
                ans[depth].push_back(root->val);
                pre(root->left, depth + 1, ans);
                pre(root->right, depth + 1, ans);
            }
        };
    
  6. (101对称二叉树) 给定一个二叉树,检查它是否是镜像对称的。

        // Recursive
        class Solution {
          
          
        public:
            bool isSymmetric(TreeNode* root) {
          
          
        
                if(root == NULL)
                    return true;
        
                return is_mirror(root, root);
            }
        
        private:
            bool is_mirror(TreeNode* root1, TreeNode* root2){
          
          
        
                if(root1 == NULL && root2 == NULL)
                    return true;
        
                if(root1 == NULL || root2 == NULL)
                    return false;
        
                if(root1->val != root2->val)
                    return false;
        
                return is_mirror(root1->left, root2->right) &&
                       is_mirror(root1->right, root2->left);
            }
        };
        
        /// Non-Recursive
        /// Using two queues to level traverse the root in different directions
        /// 利用队列实现BFS(层序遍历)
        /// Time Complexity: O(n)
        /// Space Complexity: O(n)
        class Solution {
          
          
        public:
            bool isSymmetric(TreeNode* root) {
          
          
        
                if(root == NULL)
                    return true;
        
                queue<TreeNode*> q1, q2;
                q1.push(root);
                q2.push(root);
                while(!q1.empty() && !q2.empty()){
          
          
                    TreeNode* node1 = q1.front();
                    q1.pop();
        
                    TreeNode* node2 = q2.front();
                    q2.pop();
        
                    if(node1 == NULL && node2 == NULL)
                        continue;
        
                    if(node1 == NULL || node2 == NULL)
                        return false;
        
                    if(node1->val != node2->val)
                        return false;
        
                    q1.push(node1->left);
                    q1.push(node1->right);
        
                    q2.push(node2->right);
                    q2.push(node2->left);
                }
        
                return q1.empty() && q2.empty();
            }
        };
    
  7. (222完全二叉树的节点个数) 给出一个完全二叉树,求出该树的节点个数。

    示例:

    输入: 
        1
       / \
      2   3
     / \  /
    4  5 6
    输出: 6
    
        // 常规解法:BFS或DFS
        // 法一:使用队列进行BFS
        // 法二:使用DFS
        class Solution {
          
          
        public:
            int countNodes(TreeNode* root) {
          
          
                if (root == NULL)
                    return 0;
        
                countNodes(root->left);
                countNodes(root->right);
                return ++count;
            }
        private:
            int count=0;
        };
        /// Recursion
        /// Time Complexity: O(h^2) where h is the height of the tree
        /// Space Complexity: O(h)
        class Solution {
          
          
        
        public:
            int countNodes(TreeNode* root) {
          
          
        
                if(root == NULL)
                    return 0;
        
                int leftLeft = leftHeight(root->left);
                int leftRight = rightHeight(root->left);
                if(leftLeft == leftRight)
                    return 1 + ((1<<leftLeft) - 1) + countNodes(root->right);
        
                assert(leftLeft == leftRight + 1);
                return 1 + ((1<<leftRight) - 1) + countNodes(root->left);
            }
        
        private:
            int leftHeight(TreeNode* root){
          
          
                if(root == NULL)
                    return 0;
                return 1 + leftHeight(root->left);
            }
        
            int rightHeight(TreeNode* root){
          
          
                if(root == NULL)
                    return 0;
                return 1 + rightHeight(root->right);
            }
        };
    
  8. (110平衡二叉树) 给定一个二叉树,判断它是否是高度平衡的二叉树。

    示例:

        3
       / \
      9  20
        /  \
       15   7
    返回true
           1
          / \
         2   2
        / \
       3   3
      / \
     4   4
    返回false
    
        class Solution {
          
          
        public:
            bool isBalanced(TreeNode* root) {
          
          
                return height(root) != -1;
            }
            // 返回树的高度 -1为不平衡
            int height(TreeNode* root){
          
          
                if(root == nullptr)
                    return 0;
                int left = height(root->left);
                if(left == -1) 
                    return -1;
                int right = height(root->right);
                if(right == -1)
                    return -1;
                return abs(left - right) > 1 ? -1 : 1 + max(left, right);
            }
        };
    
  9. (404左叶子之和) 计算给定二叉树的所有左叶子之和。

    示例:

        3
       / \
      9  20
        /  \
       15   7
    在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
    
        class Solution {
          
          
        
        private:
            int res = 0;
        
        public:
            int sumOfLeftLeaves(TreeNode* root) {
          
          
        
                if(!root) return 0;
        
                dfs(root, false);
                return res;
            }
        
        private:
            void dfs(TreeNode* node, bool isLeft){
          
          
        
                if(!node->left && !node->right){
          
          
                    if(isLeft) res += node->val;
                    return;
                }
        
                if(node->left) dfs(node->left, true);
                if(node->right) dfs(node->right, false);
            }
        };
    
  10. (257二叉树的所有路径) 给定一个二叉树,返回所有从根节点到叶子节点的路径。

       输入:
          1
        /   \
       2     3
        \
         5
       输出: ["1->2->5", "1->3"]
       解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
    
        vector<string> binaryTreePaths(TreeNode* root) {
          
          
    
        vector<string> res;
    
        if(root == NULL)
            return res;
    	
        // 如果是叶子节点
        if(root->left == NULL && root->right == NULL){
          
          
            res.push_back(to_string(root->val));
            return res;
        }
    
        // 找到左子树的所有路径
        vector<string> leftPaths = binaryTreePaths(root->left);
        for(int i = 0 ; i < leftPaths.size() ; i ++)
            res.push_back(to_string(root->val) + "->" + leftPaths[i]);
    
        // 找到右子树的所有路径
        vector<string> rightPaths = binaryTreePaths(root->right);
        for(int i = 0 ; i < rightPaths.size() ; i ++)
            res.push_back(to_string(root->val) + "->" + rightPaths[i]);
    
        return res;
    }
    
    // 存储为数组
    vector<vector<int>> binaryTreePaths2(TreeNode* root) {
          
          
        vector<vector<int>> ret;
        if (root == NULL)
            return ret;
    
        vector<int> tres;
        dfs(root, tres, ret);
        return ret;
    }
    void dfs(TreeNode* node, vector<int>& tres, vector<vector<int>>& ret)
    {
          
          
        tres.push_back(node->val);
    
        if (!node->left && !node->right)
            ret.push_back(tres);
    
        if (node->left)
            dfs(node->left, tres, ret);
        if (node->right)
            dfs(node->right, tres, ret);
    
        tres.pop_back();
        return;
    }
    
  11. (112路径总和) 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

    示例:
    给定如下二叉树,以及目标和 sum = 22

      		  5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
    

    返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2

    bool hasPathSum(TreeNode* root, int sum) {
          
          
        if(root==NULL)
            return false;
        
        if(root->val==sum && !root->left && !root->right)
            return true;
        
        if(root->left && hasPathSum(root->left, sum-root->left->val))
            return true;
            
        if(root->right && hasPathSum(root->right, sum-root->right->val))
            return true;
        
        return false;
    }
    // 法二:
    bool hasPathSum(TreeNode* root, int sum) {
          
          
        if (root == NULL)
            return false;
    
        return dfs(root, 0, sum);
    }
    
    bool dfs(TreeNode* node, int tsum, int sum)
    {
          
          
        tsum += node->val;
    
        if (!node->left && !node->right)
        {
          
          
            if (tsum == sum)
                return true;
        }
    
        if (node->left && dfs(node->left, tsum, sum))
            return true;
        if (node->right && dfs(node->right, tsum, sum))
            return true;
    
    
        tsum -= node->val;
        return false;
    }
    
  12. (113路径总和II) 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
    示例
    给定如下二叉树,以及目标和 sum = 22,

           5
          / \
         4   8
        /   / \
       11  13  4
      /  \    / \
     7    2  5   1
    

    返回:

     [
        [5,4,11,2],
        [5,8,4,5]
     ]
    
     class Solution {
          
          
     
     public:
         vector<vector<int>> pathSum(TreeNode* root, int sum) {
          
          
     
             vector<vector<int>> res;
             if(!root)
                 return res;
     
             vector<int> tres;
             dfs(root, tres, 0, sum, res);
             return res;
         }
     
     private:
         void dfs(TreeNode* node, vector<int>& tres, int tsum,
                  int sum, vector<vector<int>>& res){
          
          
     
             tres.push_back(node->val);
             tsum += node->val;
     
             if(!node->left && !node->right){
          
          
                 if(tsum == sum)
                     res.push_back(tres);
             }
             if (node->left)
                 dfs(node->left, tres, tsum, sum, res);
     
             if (node->right)
                 dfs(node->right, tres, tsum, sum, res);
     
             tres.pop_back();
             return;
         }
     };
    
  13. (129求根到叶子节点数字之和) 给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。例如,从根到叶子节点路径 1->2->3 代表数字 123

    计算从根到叶子节点生成的所有数字之和。

    示例:

    输入: [1,2,3]
        1
       / \
      2   3
    输出: 25
    解释:
    从根到叶子节点路径 1->2 代表数字 12.
    从根到叶子节点路径 1->3 代表数字 13.
    因此,数字总和 = 12 + 13 = 25.
    
    输入: [4,9,0,5,1]
        4
       / \
      9   0
     / \
    5   1
    输出: 1026
    解释:
    从根到叶子节点路径 4->9->5 代表数字 495.
    从根到叶子节点路径 4->9->1 代表数字 491.
    从根到叶子节点路径 4->0 代表数字 40.
    因此,数字总和 = 495 + 491 + 40 = 1026.
    
    class Solution {
          
          
    public:
        int sumNumbers(TreeNode* root) {
          
          
    
            if(!root) return 0;
    
            int res = 0;
            dfs(root, 0, res);
            return res;
        }
    
    private:
        void dfs(TreeNode* node, int tnum, int& sum){
          
          
    
            tnum = tnum * 10 + node->val;
    
            if(!node->left && !node->right)
                sum += tnum;
            else{
          
          
                if(node->left)
                    dfs(node->left, tnum, sum);
                if(node->right)
                    dfs(node->right, tnum, sum);
            }
        }
    };
    
  14. (437 路径总和III) 给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
    示例:

     root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
         
         	  10
              /  \
             5   -3
            / \    \
           3   2   11
          / \   \
         3  -2   1
         
         返回 3。和等于 8 的路径有:
         
         1.  5 -> 3
         2.  5 -> 2 -> 1
         3.  -3 -> 11
    
  15. (235 二分搜索树的最近公共祖先) 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
    例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

img
示例:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

说明:

  • 所有节点的值都是唯一的。

  • p、q 为不同节点且均存在于给定的二叉搜索树中。

    // 定义:在以root为根节点的二分搜索树中,找到节点p和q的最近公共祖先节点
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
          
          
        if(root==NULL || p==NULL || q==NULL)
            return NULL;
        
     	//if((root->val>p->val && root->val<q->val) || (root->val<p->val && root->val>q->val))
        //    return root;
        //if(root->val==p->val || root->val==q->val)
        //    return root;
        
        if(root->val>p->val && root->val>q->val)
            return lowestCommonAncestor(root->left, p, q);
        
        if(root->val<p->val && root->val<q->val)
            return lowestCommonAncestor(root->right, p, q);
        
        return root;
    }
    
  1. (98验证二分搜索树) 给定一个二叉树,判断其是否是一个有效的二叉搜索树。

    // 中序遍历一颗二分搜索树将得到一个有序数组
    class Solution {
          
          
    public:
        bool isValidBST(TreeNode* root) {
          
          
    
            vector<int> vec;
            inOrder(root, vec);
            for(int i = 1 ; i < vec.size() ; i ++)
                if(vec[i-1] >= vec[i])
                    return false;
            return true;
        }
    
    private:
        void inOrder(TreeNode* node, vector<int>& vec){
          
          
    
            if(node == NULL)
                return;
    
            inOrder(node->left, vec);
            vec.push_back(node->val);
            inOrder(node->right, vec);
        }
    };
    
    // 递归版
    class Solution {
          
          
    public:
        bool isValidBST(TreeNode* root) {
          
          
    
            return isValidBST(root, INT_MIN, INT_MAX);
        }
    
    private:
        bool isValidBST(TreeNode* node, int min, int max){
          
          
    
            if(node == NULL)
                return true;
    
            if(node->val < min || node->val > max)
                return false;
    
            if(node->left != NULL && node->left->val >= node->val)
                return false;
    
            if(node->right != NULL && node->right->val <= node->val)
                return false;
    
            return isValidBST(node->left, min, node->val - 1) && isValidBST(node->right, node->val + 1, max);
        }
    };
    
  2. (450删除二分搜索树中的节点) 给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

    示例:

    root = [5,3,6,2,4,null,7]
    key = 3
        5
       / \
      3   6
     / \   \
    2   4   7
    
    给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
    
    一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
        5
       / \
      4   6
     /     \
    2       7
    
    另一个正确答案是 [5,2,6,null,4,null,7]。
    
        5
       / \
      2   6
       \   \
        4   7
    
         class Solution {
          
          
         public:
             TreeNode* deleteNode(TreeNode* root, int key) {
          
          
         
                 if(!root)
                     return NULL;
         
                 if(key < root->val){
          
          
                     root->left = deleteNode(root->left, key);
                     return root;
                 }
         
                 if(key > root->val){
          
          
                     root->right = deleteNode(root->right, key);
                     return root;
                 }
         
                 if(!root->right) return root->left;
         
                 if(!root->left) return root->right;
         
                 TreeNode* p = root, *minnode = root->right;
                 while(minnode->left){
          
          
                     p = minnode;
                     minnode = minnode->left;
                 }
         
                 root->val = minnode->val;
                 root->right = deleteMinNode(root->right);
                 return root;
             }
         
         private:
             TreeNode* deleteMinNode(TreeNode* root){
          
          
         
                 if(!root->left) return root->right;
                 root->left = deleteMinNode(root->left);
                 return root;
             }
         };
    
  3. (108将有序数组转换为二分搜索树) 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

    示例:

    给定有序数组: [-10,-3,0,5,9],
    一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
    
          0
         / \
       -3   9
       /   /
     -10  5
    
         class Solution {
          
          
         public:
             TreeNode* sortedArrayToBST(vector<int>& nums) {
          
          
         
                 if(nums.size() == 0)
                     return NULL;
                 return buildTree(nums, 0, nums.size() - 1);
             }
         
         private:
             TreeNode* buildTree(const vector<int>& nums, int l, int r){
          
          
         
                 if(l > r) return NULL;
                 if(l == r) return new TreeNode(nums[l]);
         
                 int mid = (l + r) / 2;
                 TreeNode* root = new TreeNode(nums[mid]);
                 root->left = buildTree(nums, l, mid - 1);
                 root->right = buildTree(nums, mid + 1, r);
                 return root;
             }
         };
    
  4. (230二分搜索树中第k小的元素) 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。

    示例:

    输入: root = [3,1,4,null,2], k = 1
       3
      / \
     1   4
      \
       2
    输出: 1
    
         // 中序遍历
         class Solution {
          
          
         
         private:
             int index;
         
         public:
             int kthSmallest(TreeNode* root, int k) {
          
          
                 index = 0;
                 return kthSmallestNode(root, k)->val;
             }
         
         private:
             TreeNode* kthSmallestNode(TreeNode* node, int k){
          
          
         
                 if(node == NULL)
                     return NULL;
         
                 TreeNode* res = kthSmallestNode(node->left, k);
                 if(res) return res;
         
                 index ++;
                 if(index == k)
                     return node;
         
                 return kthSmallestNode(node->right, k);
             }
         };
    
  5. (236二叉树中的最近公共祖先) 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

         // 找到两条路径
         class Solution {
          
          
         public:
             TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
          
          
         
                 vector<TreeNode*> path1;
                 dfs(root, p, path1);
         
                 vector<TreeNode*> path2;
                 dfs(root, q, path2);
         
                 TreeNode* res;
                 for(int i = 0; i < path1.size() && i < path2.size(); i ++)
                     if(path1[i] == path2[i]) res = path1[i];
                     else break;
                 return res;
             }
         
         private:
             bool dfs(TreeNode* node, TreeNode* target, vector<TreeNode*>& path){
          
          
         
                 if(!node) return false;
         
                 path.push_back(node);
                 if(node == target) return true;
         
                 if(dfs(node->left, target, path)) return true;
                 if(dfs(node->right, target, path)) return true;
         
                 path.pop_back();
                 return false;
             }
         };
         // 递归版
         class Solution {
          
          
         public:
             // 在root中寻找p和q
             // 如果p和q都在root所在的二叉树中, 则返回LCA
             // 如果p和q只有一个在root所在的二叉树中, 则返回p或者q
             // 如果p和q均不在root所在的二叉树中, 则返回NULL
             TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
          
          
         
                 if(root == NULL)
                     return root;
         
                 if(root == p || root == q)
                     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)
                     return left;
         
                 if(right != NULL)
                     return right;
         
                 return NULL;
             }
         };
    

猜你喜欢

转载自blog.csdn.net/qq_34731182/article/details/113640717