您的位置:首页 > 其它

LintCode 解题记录 17.5.31(tag:Array)

2017-06-01 10:36 274 查看
最近由于毕业答辩,碰巧撞上端午假期,所以一直没有静下心来做题。今天终于清闲下来了,于是刷一刷题。

第一题 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: