【算法】【贪心】贪心算法解决两道基础股票买卖问题-【力扣-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]minPriminPri+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这个利润区间里面只交一次手续费。
与此同时,这一步还有一个作用,更新minPriprices[i]-fee之后,我们可以判断prices[i]的下一个数,也就是prices[i+1]是否是本利润区间里面的数,换句话说,prices[i]到底是不是本利润区间的最后一个数。
如果下一次进循环的时候,是将会走进if()里面的,那代表本利润区间在prices[i]结束,如果走进的是第一个else if()里面的,代表本利润区间还没有结束,但是是不用算利润的情况,如果走进的是第二个else if()里面,代表还有利润还可以继续拿,prices[i]也当然不是该区间最后一个数了。

尾声

看到这里,相信伙伴们对这两道经典的股票问题已经有了一定理解了。当然,股票问题在力扣上还有很多很多,解决这些问题是动态规划的专长。但是,学算法的我们,理解各种算法是必须的,而这两道题其实就是贪心算法应用的很好的例子。
最后,如果你感觉在这篇文章里学到东西的话,千千万万不要忘了点赞收藏关注后再离开哦!

posted @ 2022-01-28 23:29  背包Yu  阅读(13)  评论(0编辑  收藏  举报  来源