分治算法刷题

1.路径总和I,II,III

2.二叉树的最小深度

3.和最小的子树

4.平均值最大的子树(关于子树问题好像都可以直接用分治来做,该题为模板)

5.二叉树的公共祖先I,II,III

 


第一题:路径总和 

https://leetcode.com/problems/path-sum/

功力下降了,以前会的题目竟然得重新写起来debug了好久....忘记了这个测试案例

class Solution {
public:
 
    bool hasPathSum(TreeNode* root, int sum) {
        if(root == NULL) return false;
        sum = sum - root->val;
        if(root->left == NULL && root->right == NULL )
            return sum == 0;
        return hasPathSum(root->left,sum) || hasPathSum(root->right,sum);
    }
};

一开始得写法是回溯,现在看啥都是回溯.....

class Solution {
public:
    bool isHave;
    bool hasPathSum(TreeNode* root, int sum) {
        if(root == NULL) return false;
        isHave = false;
        int cursum = 0;
        dfs(root,sum,0);
        return isHave;
    }
    void dfs(TreeNode* root,int sum,int cursum){
        if(root->left == nullptr && root->right == nullptr){
            if(cursum + root->val == sum) isHave = true;  
            return;
        }

        cursum = cursum + root->val;
        if(root->left!=nullptr)
            dfs(root->left,sum,cursum);
        if(root->right!=nullptr)
            dfs(root->right,sum,cursum);
        cursum = cursum - root->val;

    }
};

 

题目二:路径总和II

https://leetcode-cn.com/problems/path-sum-ii/

打印出所有路径 

嘻嘻,

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        if(root == NULL) return result;
        dfs(root,sum,0);
        return result;
    }
    void dfs(TreeNode* root,int sum,int Cursum){
        if(root == NULL) return;

        path.push_back(root->val);
        Cursum = Cursum + root->val;

        if(root->left == NULL && root->right == NULL){
            if(Cursum == sum) {
                result.push_back(path);
            }
        }

        dfs(root->left,sum,Cursum);
        dfs(root->right,sum,Cursum);

        Cursum = Cursum - root->val;
        path.pop_back();

    }
};

那不就是回溯得专场了嘛?

 

第三题:路径总和III

https://leetcode-cn.com/problems/path-sum-iii/

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

class Solution {
public:

    //分治得思想
    int pathSum(TreeNode* root, int sum) {
        if(root == NULL) return 0;
        int Cur = count(root,sum);//经过当前节点的路径有多少条和为sum的
        int Left = pathSum(root->left,sum);//左边有多少条和为sum得路径
        int Right = pathSum(root->right,sum);//右边有多少条和为sum的路径
        return Cur + Left + Right;
    }

    //count函数返回的是以root为根的满足和为sum的路径有多少条
    int count(TreeNode* root,int sum){
        if(root == NULL ) return 0;
        int Cur = (sum == root->val) ? 1 : 0;
        int Left = count(root->left,sum-root->val);
        int Right = count(root->right,sum-root->val);
        return Cur + Left + Right;
    }
};

 

题目三:二叉树的最小深度

这种二叉树的题目从根节点到叶节点需要特别注意测试案例[1,2],[]这两种情况,在第一题已经踩过坑了,没想到这里又踩了,果真得多练习同类型得题目才能加深记忆...

  • 叶子节点的定义是左孩子和右孩子都为 null 时叫做叶子节点
  • 当 root 节点左右孩子都为空时,返回 1
  • 当 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度
  • 当 root 节点左右孩子都不为空时,返回左右孩子较小深度的节点值

这类题目得模板如下:

class Solution {
public:
    int minDepth(TreeNode* root) {
        if(root==NULL) return 0; //针对[]情况
        if(root->left == NULL &&  root->right == NULL) return 1;//对于[1 2]的情况
        int left = INT_MAX;
        int right = INT_MAX;
        if(root->left)//左边子树得最小高度
             left = minDepth(root->left);
        if(root->right)
             right = minDepth(root->right);//右边子树得最小高度
        return min(left,right)+1;
    }
};

 

 

题目四:和最小的子树 https://www.lintcode.com/problem/minimum-subtree/description

给一棵二叉树, 找到和为最小的子树, 返回其根节点。输入输出数据范围都在int内。

class Solution {
public:
    /**
     * @param root: the root of binary tree
     * @return: the root of the minimum subtree
     */
    TreeNode * findSubtree(TreeNode * root) {
        // write your code here
        minVale = INT_MAX;
        PathSum(root);
        return result;

    }
    
    int PathSum(TreeNode* root){
        if(root == NULL) return 0;
        int left = PathSum(root->left);//左边的和
        int right = PathSum(root->right);//右边的和
        if(left + right + root->val < minVale){
            result = root;
            minVale = left + right + root->val;
        }
        return root->val + left + right;
    }
private:
    TreeNode* result;
    int minVale;
};

另外一种做法为:也是重点:

//返回值类型
class ResultType{
public:
    int m_Value;//当前和
    TreeNode* m_root;//返回的节点
    int min_Value;//
    ResultType(int val,int minValue,TreeNode* root){
        m_Value = val;
        m_root = root;
        min_Value = minValue;
    }
};


class Solution {
public:
    /**
     * @param root: the root of binary tree
     * @return: the root of the minimum subtree
     */
    TreeNode * findSubtree(TreeNode * root) {
        // write your code here
        return  helper(root)->m_root;
    }
    ResultType* helper(TreeNode* root){
        if(root==NULL) {
            return new ResultType(0,INT_MAX,NULL);
        }
        ResultType* left = helper(root->left);//左边的信息
        ResultType* right = helper(root->right);//右边的信息
        
        ResultType* cur = new ResultType(left->m_Value + right->m_Value + root->val,left->m_Value + right->m_Value + root->val,root);//构造信息
        
        
        if(left->min_Value <= cur->min_Value ){
            cur->min_Value = left->min_Value;
            cur->m_root = left->m_root;
        }
       if(right->min_Value <= cur->min_Value ){
            cur->min_Value = right->min_Value;
            cur->m_root = right->m_root;
        }
        return cur;
    }
    
};

 

题目5:具有最大平均值的子树https://www.lintcode.com/problem/subtree-with-maximum-average/description

最佳实践模板:

class ResultType {
public:
    int sum, size;
    ResultType():sum(0), size(0) {}
    ResultType(int _sum, int _size): sum(_sum), size(_size) {}
};

class Solution {
public:
    /**
     * @param root the root of binary tree
     * @return the root of the maximum average of subtree 
     */
    TreeNode* findSubtree2(TreeNode* root) {
        // Write your code here
        helper(root);
        return node;
    }

    ResultType helper(TreeNode* root) {
        if (root == NULL) {
            return ResultType();
        }
        ResultType left = helper(root->left);
        ResultType right = helper(root->right);
        
        ResultType result = ResultType(left.sum + right.sum + root->val,
                                       left.size + right.size + 1);

        if (node == NULL || result.sum * data.size >= data.sum * result.size) {
            data = result;
            node = root;
        }

        return result;
    }

private:
    TreeNode* node = NULL;//把需要找到的值存在这里
    ResultType data;
};

题目6:二叉搜索树的最低公共祖先

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        int left = p->val;
        int right = q->val;
        int cur = root->val;

        if(cur > left && cur > right){
            return lowestCommonAncestor(root->left,p,q);
        }
        else if(cur < left && cur < right){
          return  lowestCommonAncestor(root->right,p,q);
        }
        else{
            return root;
        }
        TreeNode* node = root;
    }
};

题目7:二叉树的最低公共祖先https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

这个解法应该是比较好的解法了

//判断左右子树是否包含p,q来做这道题目,情况分析如下:
//1.左右子树各包含一个节点,那么返回root
//2.左右子树都为空,返回null
//3.左右子树其中一个不为空,则返回不为空的那个

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL || 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){
            return left;
        }

        if(right){
            return right;
        }

        return NULL;
    }
};

题目7:

给一棵二叉树和二叉树中的两个节点,找到这两个节点的最近公共祖先LCA

两个节点的最近公共祖先,是指两个节点的所有父亲节点中(包括这两个节点),离这两个节点最近的公共的节点。

每个节点除了左右儿子指针以外,还包含一个父亲指针parent,指向自己的父亲。

class ReturnType{
       bool visitA,bool vivitB;
       TreeNode* LCA;
};

解法为:遍历到父节点,然后用单链表的方式做,参考单链表的找第一个相交的点。

 

题目8:最低公共祖先III https://www.lintcode.com/problem/lowest-common-ancestor-iii/description

输入: 
{4, 3, 7, #, #, 5, 6}
3 5
5 6
6 7 
5 8
输出: 
4
7
7
null
解释:
  4
 / \
3   7
   / \
  5   6

LCA(3, 5) = 4
LCA(5, 6) = 7
LCA(6, 7) = 7
LCA(5, 8) = null
class ResultType {
public:
    bool a_exist, b_exist;
    TreeNode* node;
    ResultType(bool a, bool b, TreeNode* n) {
        a_exist = a;
        b_exist = b;
        node = n;
    }
};

class Solution {
public:
    /**
     * @param root: The root of the binary tree.
     * @param A and B: two nodes
     * @return: Return the LCA of the two nodes.
     */
    TreeNode *lowestCommonAncestor3(TreeNode* root, TreeNode* A, TreeNode* B) {
        // write your code here
        ResultType rt = helper(root, A, B);
        if (rt.a_exist && rt.b_exist)
            return rt.node;
        else
            return NULL;
    }
    
    ResultType helper(TreeNode* root, TreeNode* A, TreeNode* B) {
        if (root == NULL)
            return ResultType(false, false, NULL);
            
        ResultType left_rt = helper(root->left, A, B);
        ResultType right_rt = helper(root->right, A, B);
        
        bool a_exist = left_rt.a_exist || right_rt.a_exist || root == A;
        bool b_exist = left_rt.b_exist || right_rt.b_exist || root == B;
        
        if (root == A || root == B)
            return ResultType(a_exist, b_exist, root);

        if (left_rt.node != NULL && right_rt.node != NULL)
            return ResultType(a_exist, b_exist, root);
        if (left_rt.node != NULL)
            return ResultType(a_exist, b_exist, left_rt.node);
        if (right_rt.node != NULL)
            return ResultType(a_exist, b_exist, right_rt.node);

        return ResultType(a_exist, b_exist, NULL);
    }
};

 

 

总结套路:碰到二叉树的问题,可以使用ReturnType定义各种各样的结构,就想想整棵树在该问题上的结果和左右儿子在该问题上的结果之间的联系是什么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值