【算法】【贪心】贪心算法解决两道基础股票买卖问题-【力扣-122,力扣-714】贪心算法基础入门题目超详细讲解
【算法】【贪心】贪心算法解决两道基础股票买卖问题-【力扣】贪心算法基础入门题目超详细讲解
先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️
了解过算法的朋友都很清楚,解决股票系列的问题,是动态规划的专长。但是与此同时贪心算法也是可以解决的,在这里博主将会介绍两道比较经典的股票问题。
OJ.122 买卖股票的最佳时机II
OJ.714 买卖股票的最佳时机含手续费
在开始今天的内容之前,博主再次重复一次贪心算法的灵魂和核心:通过局部最优达到整体最优
。这几个字我们一定不能忘记!
本篇建议收藏后食用~
文章目录
OJ.122 买卖股票的最佳时机ll
题目描述
算法分析和代码实现
在这里我提供两种算法思路,第一种更好想到,第二种代码简单,更加巧妙
思路1
我们可以画出每天股票价格的折线图,我们一看图我们就理解了。
例子1:
prices=[7,1,5,3,6,4]
此时的价格折线图为
很明显,我们只需要在第二天低价买下,第三天高价卖出;第四天买下,第五天卖出,就可以获得最大利润。
这就是贪心算法:局部最优达到整体最优。
例子2:
prices=[7,1,3,5,7,6,8]
此时的价格折线图为:
由以上两个例子我们可以得出我们的解题思路:只需要找到上升斜坡的两端即可。
接下来我们就可以开始实现了。
思路1代码实现
int maxProfit(int* prices, int pricesSize) {
int* cur = prices;//定义遍历数组的指针
int* end = prices + pricesSize;//找到循环结束的标志
int sum = 0;//利润总和
while (cur < end) {
int* head = cur;//定义head指针走在前面,找到峰顶的位置
while (cur + 1 < end && *cur >= *(cur + 1)) {//判断是否越界一定要放在&&左边,否则可能起不到判断的作用导致越界访问使OJ过不了
cur++;
head++;//如果一直递减,两个指针++;
}
if (cur == end) {
break;
}
while (head + 1 < end && *head < *(head + 1)) {//这个循环用head找到顶峰的位置。
head++;
}
if ( head == end - 1) {//走到最后一个元素可以直接跳出了
sum += *head - *cur;
break;
}
else {
sum += *head - *cur;//峰顶值减去峰底值
cur = head;
}
}
return sum;
}
思路2(更优)
【如果搞清楚了,利润是可以分解的,那么这道题目就变得更简单了】
我们以刚才的例子2为例:
例子2:
prices=[7,1,3,5,7,6,8]
此时的价格折线图为:
这一次我们不再通过两个指针
分别指向峰底和峰顶这个方式直接计算2-5,6-7的利润,我们直接将每一天的利润加起来。
什么意思:我们从第二天开始,将每一天和前一天股票价格的差值写出来,我们可以得到一个数组dif=[-6,2,2,2,-1,2],此时,我们将这个数组里面大于0的数加起来不就可以了吗?
贪心算法:通过局部最优找到整体最优,在这个思路中,我不管买入以后第三天还是不是比第二天价格更高,我只关心第二天是不是比今天高,是,我就将利润加上,这就是局部最优了。
思路2代码实现
int maxProfit(int* prices, int pricesSize) {
int sum = 0;
for (int i = 1; i < pricesSize; i++) {
sum+= max(prices[i] - prices[i - 1], 0);
}
return sum;
}
其中
max()函数
的功能就是求出两个参数的最大值,通过这种方法,我们可以将正数加在一起。
如果是C语言实现的伙伴我们在此之前写一个max()函数
就可以了,这个很简单 如果是C++的伙伴我们直接调用包装好的库函数就可以了。
OJ.714 买卖股票的最佳时机含手续费
题目描述
算法分析和代码实现
思路:
这道题和OJ.122的区别在于,上一题我们是不用在乎手续费问题的,这意味着我们买卖股票的时候,只要有利润,我们就可以直接算上局部最优。也就是上一题只有两种情况——直接赚和直接亏两种,所以我们直接取赚的就可以了。
但是在现在这题里面就不止两种情况了,与此同时,我们不能无忧无虑的买卖股票,我们要把手续费这个因素考虑进去。
在这种情况下,我们买入的时间肯定是价格最低的时候是最好的,这个很容易想到,因此此时,我们不能单,纯的直接找增区间了,我们要用一个变量记录最低的价格,记录好最低价格之后,在我们遍历数组的过程中,我们有三种情况需要分析:
1.prices[i]
比minPri
(所记录的最小价格)小,这个时候说明我们的minPri
需要更新了,prices[i]
才是最小的
2.prices[i]
在minPri
和minPri+fee
之间,这种情况下,我们是肯定不能卖出的,因为卖出的话因为有手续费的原因,其实是亏本的
3.prices[i]
比minPri+fee
大,这个时候我们就要把利润算进去了
代码实现:
int maxProfit(int* prices, int pricesSize, int fee){
int sum=0;
int minPri=prices[0];//定义minPri,并从数组第二个元素开始遍历
for(int i=1;i<pricesSize;i++){
if(prices[i]<minPri){//更新minPri
minPri=prices[i];
}
//情况2:不买不卖
else if(prices[i]>=minPri&&prices[i]<=minPri+fee){
continue;
}
else if(prices[i]>minPri+fee){
sum+=prices[i]-minPri-fee;//计算费用
minPri=prices[i]-fee;//这一步非常关键
}
}
return sum;
}
对于这一步的理解:minPri=prices[i]-fee;
这一步其实是非常关键的一步
我们举一个例子:
假设prices=[1,3,2,8,11,3,7];
此时我们遍历到8这个数的时候,是进入到第三种情况的,此时我们计算了利润8-1-2=5,但是,我们收获利润的区间是还没有结束的,什么意思呢:其实就是到8的时候,我们是没有将股票卖出的,我们是准备到11才把股票卖出,8不是这个minPri为1这个利润区间的最后一个数,所以,我们的minPir=price[i]-fee,这样可以保证我们在以1为minPri这个利润区间里面只交一次手续费。
与此同时,这一步还有一个作用,更新minPri
为prices[i]-fee
之后,我们可以判断prices[i]
的下一个数,也就是prices[i+1]
是否是本利润区间里面的数,换句话说,prices[i]
到底是不是本利润区间的最后一个数。
如果下一次进循环的时候,是将会走进if()
里面的,那代表本利润区间在prices[i]
结束,如果走进的是第一个else if()
里面的,代表本利润区间还没有结束,但是是不用算利润的情况,如果走进的是第二个else if()
里面,代表还有利润还可以继续拿,prices[i]
也当然不是该区间最后一个数了。
尾声
看到这里,相信伙伴们对这两道经典的股票问题已经有了一定理解了。当然,股票问题在力扣上还有很多很多,解决这些问题是动态规划的专长。但是,学算法的我们,理解各种算法是必须的,而这两道题其实就是贪心算法应用的很好的例子。
最后,如果你感觉在这篇文章里学到东西的话,千千万万不要忘了点赞收藏关注后再离开哦!