LintCode 解题记录17.10.21

来源:互联网 发布:汽车维修入库软件 编辑:程序博客网 时间:2024/05/21 11:26

前言

刷题的进度还是非常慢的,照这个进度,115道题估计过年前都刷不完。。真的是怠惰啊。

House Robber

题目描述

给定一个数组,要求每次不能取相邻的两个数,求能取到所有数的最大和。

思路

初读一遍,发现是最优化问题(最大化),于是就思考最优化的两种思路 贪心和Dp。这里采用Dp的思想。我自己的思路就是采用 局部最优化与全局最优化的思想。设local[i]表示抢了第i家所能获得的最大利润,global[i]表示到第i家时所能获得的最大利润。那么递归关系如下:
local[i] = num[i] + global[i-2]; (1)
global[i] = max{local[i], global[i-1]}; (2)
写出这个递推式的思路如下:
1.针对local[i],假设我抢了第i家,那么第i-1家就不能抢,此时所能获得的最大利润就是抢劫前i-2家所能获得的最大利润加上第i家的利润,于是就得到式1
2.针对global[i],就可以分为两种情况,抢劫第i家和没有抢劫第i家,如果抢劫了第i家,所能获得的最大利润就是local[i],如果没有抢劫第i家,那么所能所得的最大利润就是抢劫了前i-1家所能获得的最大利润。
综合一下,得到下面的递推关系:
global[i] = max(global[i-2]+num[i], global[i-1]),我们只要初始化global[0],global[1]就好了,其中global[0] = num[0], global[1] = max(num[0], num[1]);

挑战

O(n)时间复杂度,O(1)空间复杂度。
显然上面的思路是O(n)的时间复杂度,O(n)的空间复杂度,接下来就是优化空间复杂度。于是我维护3个变量分别代表global[i],global[i-1],global[i-2]即可。然后只需要顺序遍历即可。

代码

    long long houseRobber(vector<int> &A) {        // write your code here        if (A.size() == 0) return 0;        if (A.size() == 1) return A[0];        long long res = 0, before = A[0], pre = max(A[0], A[1]);        //res = global[i], pre = global[i-1], before = global[i-2];        for (int i = 2; i < A.size(); i++) {            res = max(before + A[i], pre);            //i即将++,更新before与pre            before = pre;             pre = res;        }        return res;    }

House Robber II

题目描述

还是这个小偷,现在是这个数组首尾相连,问你最大的偷取价值。

思路

其实这题就是上一题稍微变形一下,即如果偷了第一家,那么就不能偷最后一家;如果偷了最后一家,那么就不能偷第一家,实际上这道题只需要用两次House Robber中的方法,最后返回两者中的较大值即可。

代码

    int houseRobber2(vector<int> nums) {        // write your code here        if (nums.size() == 0) return 0;        if (nums.size() == 1) return nums[0];        //houseRobber即为上一道题的方法        int a = houseRobber(vector<int>(nums.begin(), nums.end()-1));        int b = houseRobber(vector<int>(nums.begin()+1, nums.end()));        return max(a, b);    }

House RobberIII

题目描述

现在这片房子既不是用数组表示,也不是用圆圈表示,而是用了一颗二叉树来表示。不能直接抢劫两个直接相连的房子,仍然返回这个最大的利润值。

思路

树的题目自然是递归来做。此题中,对于某一节点,显然是分是否抢劫该节点两种情况来进行讨论。如果是对于两种情况分别进行递归,那恐怕会获得超时错误。因为你相当于搜索了这棵树两遍。为了避免这种情况,用一个长度为2的数组来进行存储数据,res[0]表示没有抢劫该节点,而res[1]表示抢劫了该节点。那么对于某一节点,首先递归计算其左子树得到left数组,然后递归计算右子树得到right,那么于是有
res[0] = max(left[0], left[1]) + max(right[0], right[1]) ; //没有抢劫该节点
res[1] = left[0] + right[0] + node->val; //抢劫了该节点

代码

    int houseRobber3(TreeNode * root) {        // write your code here        vector<int> res = helper(root);        return max(res[0], res[1]);    }    vector<int> helper(TreeNode *node) {        if (node == NULL) return vector<int>(2, 0);        vector<int> left = helper(node->left);        vector<int> right = helper(node->right);        vector<int> res(2, 0);        res[0] = max(left[0], left[1]) + max(right[0], right[1]);        res[1] = left[0] + right[0] + node->val;        return res;    }

Unique Binary Search Trees II

题目描述

给定一个n,产生所有不同的二叉搜索树,每一颗树都存储着1-n这n个数。

思路

递归。对于一个范围[start, end],从中任远一个作为根节点,然后把该范围划分成了左右两个数组。然后用左边的数组递归得到其左子树集合,以及同样的右子树集合。最后对于左子树集合中的每一个节点,与右子树集合中的每一个节点,拼接成一棵树,并把该数的根节点放入最终的答案中。

代码

    vector<TreeNode *> generateTrees(int n) {        // write your code here        if (n < 0) {            return vector<TreeNode*> {NULL};        }        return helper(1, n);    }    vector<TreeNode *> helper(int start, int end) {        vector<TreeNode *> res;        if (start > end) {            res.push_back(NULL);            return res;        }        for (int i = start; i <= end; i++) {            vector<TreeNode *> left = helper(start, i-1);            vector<TreeNode *> right = helper(i+1, end);            for (auto l: left) {                for (auto r: right) {                    TreeNode *root = new TreeNode(i);                    root->right = r;                    root->left = l;                    res.push_back(root);                }            }        }        return res;    }

注意

这道题可以说是非常能体现树、递归、以及二元查找树的紧密联系了。题目挺经典的。

K SumII

题目描述

给定了n个数,从中选取k个数,使他们的和为target。

思路

dfs。注意一下递归终止的条件。

代码

    vector<vector<int>> kSumII(vector<int> &A, int k, int target) {        // write your code here        vector<vector<int>> res;        vector<int> tmp;        dfs(res, tmp, A, k, target, 0);        return res;    }    void dfs(vector<vector<int>> &res, vector<int> tmp, vector<int> A, int k, int target, int idx) {        if (target <= 0 || k <= 0) {            if (target == 0 && k == 0) {                res.push_back(tmp);            }            return;        }        for (int i = idx; i < A.size(); i++) {            tmp.push_back(A[i]);            dfs(res, tmp, A, k-1, target-A[i], i+1);            tmp.pop_back();        }    }