LintCode 解题记录 17.5.31(tag:Array)
2017-06-01 10:36
274 查看
最近由于毕业答辩,碰巧撞上端午假期,所以一直没有静下心来做题。今天终于清闲下来了,于是刷一刷题。
第一题 LintCode 132 Pattern
没做出来。上网看了别人的思路,用stack保存最大的数,用third保存次大的数,然后从后往前遍历。
代码:
至于为什么能想到这种做法,不是很清楚。该方法可以保证third值小于s.top()值且third值在s.top()值后面。
第二题 3Sum Closest
KSum系列题目的变型题。本质上和3Sum没什么区别,先排序然后用Two Pointers法。
时间复杂度为O(nlogn)+O(n^2)=O(n^2),空间复杂度为O(1)。(因为只用了几个变量)
第三题 Best Time to Buy and Sell Stock
题目概括的来说就是给定一个数组,寻找max (aj-ai) s.t. 0 <= i <= j < n
思路:顺序遍历,如果当前价格小于变量buy,那么更新buy继续遍历。否则说明此时应该以buy买入,然后再往后遍历,找到最大的卖价,更新profit。时间复杂度是O(n)。
在网上看到了一种思路更好的解法,比如我在第i天买入,能获得的最大利润就是在i ~ n天内的最高价卖出。这里采用后序遍历。
第四题 Best Time to Buy and Sell Stock II
上一题的变型,即可以进行多次交易。那么为了获得最大利润,就需要尽可能多的交易。也就是说如果明天的价格比今天的高,那么我就可以今天买入,明天卖出。然后明天同理。
先贴一个自己写的矬代码:
再贴一个别人写的代码:
总结:没有对比就没有伤害:-(
第五题 Best Time to Buy and Sell Stock III
上两题的变型,这里只能做两次交易。我的想法就是:先做第一次交易,然后在第一次交易的基础上寻找第二次交易。时间复杂度为O(n^2),不过也AC了。
代码:
官方思路:
最简单的方法就是对每一个时间点,将其所有两边的数组都执行一次Best Time to Buy and Sell Stock的解法,但这会带来O(n^2)的时间复杂度。实际上当计算prices[0]到prices[i]的收益时,我们已经计算过prices[0]到prices[i-1]的最大收益了,prices[0]到prices[i]的最大收益应该是当前卖出能获得的最大收益和prices[0]到prices[i-1]的最大收益中,较大的那一个。那么就可以以i点为分割,左半段最大收益的数组left,右半段最大的收益right,我们就可以遍历一遍这两个数组,找出最大的left+right组合。时间复杂度为O(n),空间复杂度也为O(n)。
第二种方法就是动态规划。我们维护两种量,一个是当前到达第i天时最大可以进行j次交易,最好的利润是多少(global[i][j]),另一个是当到达第i天时,最多可进行j次交易,且最后一次交易在当天卖出的最好利润是多少(local[i][j]),那么递推公式如下:
global[i][j] = max(global[i-1][j], local[i][j])
local[i][j] = max(global[i-1][j-1]+max(0,diff), local[i-1][j]+diff)
理解一下:
递推公式1:
第i天时最大可进行j次交易所获得的最好利润就是:1.第i天没有交易,那么就是global[i-1][j] 2.第i天进行了交易,那么就是local[i][j]。global[i][j] 取两者中的较大值。
递推公式2:
同样可以分为两种情况:1.第i天的交易导致的最好利润,就是global[i-1][j-1]+max(0, diff) 2.如果假设不是第i天的交易导致的最好利润,那么就是local[i-1][j]+diff。也就是说原本在第i-1天卖出的现在移到第i天卖出,那么所获得的最好利润就需要加上差价diff。
代码:
类似于背包问题中空间复杂度的优化方法,这里也可以采用一维数组来代替二维数组。由于覆盖的先后顺序关系,需要采用倒序遍历,这样可以取到正确的g[j-1]的值,而非被覆盖后的值。
这道动态规划的题目还是不是很熟练,多练练吧。贴两个此题的参考链接:
http://www.cnblogs.com/grandyang/p/4281975.html
http://blog.csdn.net/linhuanmars/article/details/23236995
第一题 LintCode 132 Pattern
没做出来。上网看了别人的思路,用stack保存最大的数,用third保存次大的数,然后从后往前遍历。
代码:
bool find132pattern(vector<int>& nums) { stack<int> s; int third = INT_MIN, len = nums.size(); for (int i = len-1; i >= 0; i--) { if (nums[i] < third) return true;//如果当前元素值小于third,说明找到了132Pattern,它们是nums[i], s.top(), third。 while (!s.empty() && s.top() < nums[i]) { //如果当前元素大于栈顶元素,则循环出栈更新third直到栈顶元素小于当前元素。 third = s.top(); s.pop(); } s.push(nums[i]);//将当前元素入栈 } return false; }
至于为什么能想到这种做法,不是很清楚。该方法可以保证third值小于s.top()值且third值在s.top()值后面。
第二题 3Sum Closest
KSum系列题目的变型题。本质上和3Sum没什么区别,先排序然后用Two Pointers法。
时间复杂度为O(nlogn)+O(n^2)=O(n^2),空间复杂度为O(1)。(因为只用了几个变量)
int threeSumClosest(vector<int> nums, int target) { int left, right, ret, sum, closest; left = right = ret = sum = 0, closest = INT_MAX; sort(nums.begin(), nums.end()); for (int i = 0; i < nums.size(); i++) { left = i+1; right = nums.size()-1; while (left < right) { sum = nums[i] + nums[left] + nums[right]; if (sum < target) left++; if (sum > target) right--; if (abs(sum - target) < closest) { closest = abs(sum - target); ret = sum; } if (closest == 0) break; } } return ret; }
第三题 Best Time to Buy and Sell Stock
题目概括的来说就是给定一个数组,寻找max (aj-ai) s.t. 0 <= i <= j < n
思路:顺序遍历,如果当前价格小于变量buy,那么更新buy继续遍历。否则说明此时应该以buy买入,然后再往后遍历,找到最大的卖价,更新profit。时间复杂度是O(n)。
int maxProfit(vector<int> &prices) { int ret = 0, buy = INT_MAX, sell = 0; for (int i = 0; i < prices.size();) { if (prices[i] < buy) { buy = prices[i++]; continue; } while (i < prices.size()-1 && prices[i] < prices[i+1]) i++; ret = max(ret, prices[i] - buy); i++; } return ret; }
在网上看到了一种思路更好的解法,比如我在第i天买入,能获得的最大利润就是在i ~ n天内的最高价卖出。这里采用后序遍历。
int maxProfit(vector<int> &prices) { int maxprice = 0, ret = 0; if (prices.size() == 0) return 0; for (int i = prices.size()-1; i >= 0; i--) { maxprice = max(maxprice, prices[i]); //用maxprice来记录第i天到第n天的最高卖价。 ret = max(ret, maxprice - prices[i]); } return ret; }
第四题 Best Time to Buy and Sell Stock II
上一题的变型,即可以进行多次交易。那么为了获得最大利润,就需要尽可能多的交易。也就是说如果明天的价格比今天的高,那么我就可以今天买入,明天卖出。然后明天同理。
先贴一个自己写的矬代码:
int maxProfit(vector<int> &prices) { int buy_price = INT_MAX, sell_price = INT_MIN, ret = 0; int i = 0; bool buy = true; while (i < prices.size()) { if (prices[i] <= buy_price && buy) { buy_price = prices[i]; } else if (prices[i] > buy_price && buy) { sell_price = prices[i]; buy = !buy; } else if (prices[i] > sell_price && !buy) { sell_price = prices[i]; } else if (prices[i] < sell_price && !buy) { ret += sell_price - buy_price; buy_price = prices[i]; buy = !buy; } i++; if (i == prices.size() && !buy) { ret += sell_price - buy_price; } } return ret; }
再贴一个别人写的代码:
int maxProfit(vector<int> &prices) { int ret = 0; for (int i = 1; i < prices.size(); i++) { if (prices[i] > prices[i-1]) { ret += prices[i] - prices[i-1]; } } return ret; }
总结:没有对比就没有伤害:-(
第五题 Best Time to Buy and Sell Stock III
上两题的变型,这里只能做两次交易。我的想法就是:先做第一次交易,然后在第一次交易的基础上寻找第二次交易。时间复杂度为O(n^2),不过也AC了。
代码:
int find(vector<int> &prices, int start) { int ret = 0, buy = INT_MAX; for (int i = start; i < prices.size(); i++) { if (prices[i] <= buy) { buy = prices[i]; continue; } ret = max(ret, prices[i] - buy); } return ret; } int maxProfit(vector<int> &prices) { int ret = 0, buy = INT_MAX, sell = 0, temp = 0; for (int i = 0; i < prices.size(); ) { if (prices[i] <= buy) { buy = prices[i++]; continue; } while (i < prices.size()-1 && prices[i] < prices[i+1]) { i++; } int temp = prices[i] - buy + find(prices, i+1); ret = max(ret, temp); i++; } return ret; }
官方思路:
最简单的方法就是对每一个时间点,将其所有两边的数组都执行一次Best Time to Buy and Sell Stock的解法,但这会带来O(n^2)的时间复杂度。实际上当计算prices[0]到prices[i]的收益时,我们已经计算过prices[0]到prices[i-1]的最大收益了,prices[0]到prices[i]的最大收益应该是当前卖出能获得的最大收益和prices[0]到prices[i-1]的最大收益中,较大的那一个。那么就可以以i点为分割,左半段最大收益的数组left,右半段最大的收益right,我们就可以遍历一遍这两个数组,找出最大的left+right组合。时间复杂度为O(n),空间复杂度也为O(n)。
int maxProfit(vector<int> &prices) { int len = prices.size(); if (len == 0) return 0; vector<int> left(len, 0); vector<int> right(len, 0); int leftmin = prices[0], rightmax = prices[len-1]; for (int i = 1; i < len; i++) { leftmin = min(leftmin, prices[i]); left[i] = max(left[i-1], prices[i] - leftmin); } for (int i = len-2; i >= 0; i--) { rightmax = max(rightmax, prices[i]); right[i] = max(right[i-1], rightmax - prices[i]); } int ret = INT_MIN; for (int i = 0; i < len; i++) { ret = max(ret, left[i]+right[i]); } return ret; }
第二种方法就是动态规划。我们维护两种量,一个是当前到达第i天时最大可以进行j次交易,最好的利润是多少(global[i][j]),另一个是当到达第i天时,最多可进行j次交易,且最后一次交易在当天卖出的最好利润是多少(local[i][j]),那么递推公式如下:
global[i][j] = max(global[i-1][j], local[i][j])
local[i][j] = max(global[i-1][j-1]+max(0,diff), local[i-1][j]+diff)
理解一下:
递推公式1:
第i天时最大可进行j次交易所获得的最好利润就是:1.第i天没有交易,那么就是global[i-1][j] 2.第i天进行了交易,那么就是local[i][j]。global[i][j] 取两者中的较大值。
递推公式2:
同样可以分为两种情况:1.第i天的交易导致的最好利润,就是global[i-1][j-1]+max(0, diff) 2.如果假设不是第i天的交易导致的最好利润,那么就是local[i-1][j]+diff。也就是说原本在第i-1天卖出的现在移到第i天卖出,那么所获得的最好利润就需要加上差价diff。
代码:
class Solution { public: int maxProfit(vector<int> &prices) { if (prices.empty()) return 0; int n = prices.size(), g [3] = {0}, l [3] = {0}; for (int i = 1; i < prices.size(); ++i) { int diff = prices[i] - prices[i - 1]; for (int j = 1; j <= 2; ++j) { l[i][j] = max(g[i - 1][j - 1] + max(diff, 0), l[i - 1][j] + diff); g[i][j] = max(l[i][j], g[i - 1][j]); } } return g[n - 1][2]; } };
类似于背包问题中空间复杂度的优化方法,这里也可以采用一维数组来代替二维数组。由于覆盖的先后顺序关系,需要采用倒序遍历,这样可以取到正确的g[j-1]的值,而非被覆盖后的值。
class Solution { public: int maxProfit(vector<int> &prices) { if (prices.empty()) return 0; int g[3] = {0}; int l[3] = {0}; for (int i = 0; i < prices.size() - 1; ++i) { int diff = prices[i + 1] - prices[i]; for (int j = 2; j >= 1; --j) { l[j] = max(g[j - 1] + max(diff, 0), l[j] + diff); g[j] = max(l[j], g[j]); } } return g[2]; } };
这道动态规划的题目还是不是很熟练,多练练吧。贴两个此题的参考链接:
http://www.cnblogs.com/grandyang/p/4281975.html
http://blog.csdn.net/linhuanmars/article/details/23236995
相关文章推荐
- LintCode 解题记录 17.5.22 (tag: Array)
- LintCode 解题记录17.5.13(tag: Union Find)
- LintCode 解题记录 17.5.19 (tag: Hash表2)
- LintCode 解题记录 2017.5.8 (字典树)
- LintCode 解题记录17.10.21
- LintCode 解题记录 Array 17.6.5
- LintCode解题记录 17.12.2 LinkedList
- LintCode 解题记录 17.10.14 拓扑排序
- LintCode 解题记录17.8.8 字符串处理4
- LintCode解题记录-Catalan Number
- LintCode 解题记录 17.5.8 (tag:二叉树)
- LintCode 解题记录17.4.27
- LintCode 解题记录 17.11.11
- LintCode 解题记录17.5.10(tag:线段树)
- LintCode 解题记录 Matrix专题
- LintCode 解题记录 字符串处理1.0 17.7.29
- LintCode 解题记录 17.10.5 递归
- LintCode 解题记录 7.11 ~ 7.16
- lintcode 容易题:Partition Array by Odd and Even 奇偶分割数组
- leetcode解题方案--080--Remove Duplicates from Sorted Array II