<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">leetcode第188题,Best Time to Buy and Sell Stock IV题目如下:</span>

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.





Note:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

int maxProfit(int k, int prices[], int n)

对题目的分析

股票有涨有落,题目要求在已知未来股票走势情况下,对一支股票进行买卖,取得最大的收入,而且要求用户在卖股票之前必须先买这支股票。
转化成数学问题就是有一个数组,求得最多2k个点,b1,b2,...,bk. s1,s2,...,sk, 其中bi表示买入的时间点,si表示卖出的时间点。满足0<=b1<s1<b2<s2<b3<s3<...<bk<sk<n。

思路分析

此问题类似动态规划问题,对于给的数组,f(k,n)不小于f(n,k-1),当f(k,n)=f(k-1,n)时,认为对这一数组进行操作不会增加最后的数值。设数组为p[n]

对于已知的b1,b2....b(k-1),s1,s2,..s(k-1),如何得到下一组买入时间和卖出时间呢?对于最终的a(k),b(k),一定存在某个i,使得s(i)<B<S<b(i+1),或者b(i)<S<B<s(i)。
  • 第一种情况:s(i)<B<S<b(i+1)  这种情况下,在s(i)时刻,股票仍然在市场上,在B时刻,买入股票,在S时刻卖出股票。因此B和S一定是在(s(i),b(i+1))中满足B<S且p[S]-p[B]最大的两个下标。
  • 第二种情况:b(i)<S<B<s(i)   这种情况下,在b(i)时刻,股票在用户手中,用户在S时刻卖出股票,在B时刻买入,再次在S时刻卖出。因此B和S一定是在(b(i),s(i))中满足p[S]-p[B]最大且B>S的两个下标。
每一次增加k时,每一个i<k都有两个以上的<B,S>二元组,那么应该选取哪个i呢?
k次买卖后,整个数组分为了最多2k+1段,在(b(i),s(i))中,股票在用户手中,其他段,股票在市场上。在每一段上,用户能继续获得的利润的计算方式不同。如果我们计算每一段用户能获得的最大利润,继续操作时,我们从所有段中选择继续获得利润最大的一段。在这一段上进行买入卖出或卖出买入,即为下一操作能获得的最大利润。注意到每一次操作都会将1段拆为3段。
如下图所示:


我们使用两个最大堆,第一个堆存放在市场上的时间段,其中每个元素包含最大的用户可以获得的最大利润。第二个堆存放在用户手中的时间段,其中每个元素包含用户可以增长的最大利润。在每一次操作中,我们分别从两个堆中去的最大值,比较两者,选择较大值H,将最大利润增加H,然后维护两个堆。即删除一个堆中的最大值,然后向两个堆中插入总共3个元素。
当操作k次或者两个堆中最大元素为0或两个堆均为空时,即可宣告结束。

数据结构设计

设计两个堆,两个堆均用数组表示,以节省内存空间。由于k的值为变量,且可能很大。为了节省内存开支,因此首先申请长度50的数组来存储堆,当数组满时,将数组扩增到原来两倍大小。两个堆的元素如下:
struct buysells//股票在用户手中的时间段
{
	//股票在beginDay最低,在endDay最高,increaseProfit = p(sellDay)-p(buyDay)
	//beginDay<sellDay<buyDay<endDay
	int beginDay;//用户在beginDay买入
	int endDay;//用户在beginday卖出
	int buyDay;//若想获得最大利润增长,需再次在buyDay买入
	int sellDay;//若想获得最大利润增长,需再次在sellDay卖出
	int increaseProfit;//最大的利润增长价值
};
struct freetimes//股票在市场上的时间段
{
	//startDay<=buyDay<sellDay<=endDay
	//profit = p(sellDay)-p(buyDay)
	int startDay;//股票在市场上第一天
	int endDay;//股票在市场上最后一天
	int buyDay;//若想获得最大利润,需在buyDay买入
	int sellDay;//若想蝴蝶最大利润,需在sellDay卖出
	int profit;//用户可以获得最大利润
};

代码

struct buysells//股票在用户手中的时间段
{
	//股票在beginDay最低,在endDay最高,increaseProfit = p(sellDay)-p(buyDay)
	//beginDay<sellDay<buyDay<endDay
	int beginDay;//用户在beginDay买入
	int endDay;//用户在beginday卖出
	int buyDay;//若想获得最大利润增长,需再次在buyDay买入
	int sellDay;//若想获得最大利润增长,需再次在sellDay卖出
	int increaseProfit;//最大的利润增长价值
};
typedef struct buysells buysell;
typedef buysell *buysellpointer;

struct freetimes//股票在市场上的时间段
{
	//startDay<=buyDay<sellDay<=endDay
	//profit = p(sellDay)-p(buyDay)
	int startDay;//股票在市场上第一天
	int endDay;//股票在市场上最后一天
	int buyDay;//若想获得最大利润,需在buyDay买入
	int sellDay;//若想蝴蝶最大利润,需在sellDay卖出
	int profit;//用户可以获得最大利润
};
typedef struct freetimes freetime;
typedef freetime * freetimepointer;

//扩展数组到原来的两倍,其中*bsppNumLimit为原来的数组最大长度
//由于数组中存的是buysellpointer,因此需要指向指针的指针做为参数,buysellpointer **
void expandBSPHeap(buysellpointer **heap, int *bsppNumLimit)
{
	//分配两倍的数组空间,每个数组中存放的是buysellpointer
	buysellpointer* newheap = (buysellpointer*)malloc(*bsppNumLimit *2 * sizeof(buysellpointer));

	for (int i = 0; i < *bsppNumLimit; i++)
	{
		newheap[i] = (*heap)[i];
	}
	free(*heap);//释放原有的buysellpointer数组
	*heap = newheap;//由于需要改变heap,因此使用指针
	*bsppNumLimit = *bsppNumLimit * 2;
}

void insertBSP(buysellpointer BSP,buysellpointer **heap, int *length, int *bsppNumLimit)
{
	if(*length == *bsppNumLimit)
		expandBSPHeap(heap,bsppNumLimit);

	(*heap)[(*length)++] = BSP;
	int index = *length-1;
	buysellpointer tempBSP = BSP;
	//向堆中插入元素
	while (index!=0 && (*heap)[(index+1)/2-1]->increaseProfit < tempBSP->increaseProfit )
	{
		(*heap)[index] = (*heap)[(index+1)/2-1];
		index = (index+1)/2 -1;
	}
	(*heap)[index] = BSP;
}

void  deleteBSP(buysellpointer *heap, int *length)
{
	//删除最大元素
	if(*length == 0)
		return;
	(*length)--;
	free(heap[0]);//释放空间
	heap[0] = heap[*length];
	buysellpointer tempBSP = heap[*length];
	int index = 0;
	int maxChildIndex = 1;
	while (maxChildIndex < *length)
	{
		if(maxChildIndex + 1 < *length)
			if(heap[maxChildIndex+1]->increaseProfit > heap[maxChildIndex]->increaseProfit)
				maxChildIndex++;
		if(heap[maxChildIndex]->increaseProfit > tempBSP->increaseProfit)
		{
			heap[index] = heap[maxChildIndex];
			heap[maxChildIndex] =  tempBSP;
			index = maxChildIndex;
			maxChildIndex = (index+1)*2 - 1;
		}
		else
			break;
	}
}

void expandFTPHeap(freetimepointer **heap, int *fppNumLimit)
{
	freetimepointer* newheap = (freetimepointer*)malloc(*fppNumLimit *2 * sizeof(buysellpointer));

	for (int i = 0; i < *fppNumLimit; i++)
	{
		newheap[i] = (*heap)[i];
	}
	free(*heap);
	*heap = newheap;
	*fppNumLimit = *fppNumLimit * 2;
}

void insertFTP(freetimepointer FTP,freetimepointer **heap, int *length, int *fppNumLimit)
{
	if(*length == *fppNumLimit)
		expandFTPHeap(heap,fppNumLimit);

	(*heap)[(*length)++] = FTP;
	int index = *length-1;
	freetimepointer tempFTP = FTP;
	while (index!=0 && (*heap)[(index+1)/2-1]->profit < tempFTP->profit )
	{
		(*heap)[index] = (*heap)[(index+1)/2-1];
		index = (index+1)/2 -1;
	}
	(*heap)[index] = tempFTP;
}

void  deleteFTP(freetimepointer *heap, int *length)
{
	if(*length == 0)
		return;
	(*length)--;
	free(heap[0]);
	heap[0] = heap[*length];
	freetimepointer tempFTP = heap[*length];
	int index = 0;
	int maxChildIndex = 1;
	while (maxChildIndex < *length)
	{
		if(maxChildIndex + 1 < *length)
			if(heap[maxChildIndex+1]->profit > heap[maxChildIndex]->profit)
				maxChildIndex++;
		if(heap[maxChildIndex]->profit > tempFTP->profit)
		{
			heap[index] = heap[maxChildIndex];
			heap[maxChildIndex] =  tempFTP;
			index = maxChildIndex;
			maxChildIndex = (index+1)*2 - 1;
		}
		else
			break;
	}
}

//找到股票在市场期间能获得的最大利润
void findmaxprofit(int prices[],freetimepointer ftp)
{
	int minDay = ftp->startDay;
	ftp->buyDay = minDay;
	ftp->sellDay = minDay;
	ftp->profit = 0;
	for (int i = ftp->startDay+1; i <= ftp->endDay; i++)
	{
		minDay = (prices[minDay] > prices[i]) ? i : minDay;
		if (prices[i] - prices[minDay] > ftp->profit)
		{
			ftp->buyDay = minDay;
			ftp->sellDay = i;
			ftp->profit = prices[i] - prices[minDay];
		}
	}
}

//找到股票在用户手中期间能获得的最大利润增长
void findMaxIncreaseProfit(int prices[],buysellpointer bsp)
{
	int maxDay = bsp->beginDay;
	bsp->buyDay = maxDay;
	bsp->sellDay = maxDay;
	bsp->increaseProfit = 0;
	for (int i = bsp->beginDay+1; i < bsp->endDay; i++)
	{
		maxDay = (prices[i] > prices[maxDay]) ? i : maxDay;
		if (prices[maxDay] - prices[i] > bsp->increaseProfit)
		{
			bsp->buyDay = i;
			bsp->sellDay = maxDay;
			bsp->increaseProfit = prices[maxDay] - prices[i];
		}
	}

}

int maxProfit(int k, int prices[], int n) {

	int totalProfit=0;
	int fppNumLimit = 50;
	//股票在市场中的时间段组成的堆
	freetimepointer *fpp= (freetimepointer*)malloc(fppNumLimit*sizeof(freetimepointer));
	int fppNum = 0;
	int bsppNumLimit = 50;
	//股票在用户手中的时间的组成的堆
	buysellpointer *bspp = (buysellpointer*)malloc(bsppNumLimit*sizeof(freetimepointer));
	int bsppNum = 0;
	//初始化第一个股票在市场中的时间段
	fpp[0] = (freetimepointer)malloc(sizeof(freetime));
	fpp[0]->startDay = 0;
	fpp[0]->endDay = n-1;
	findmaxprofit(prices,fpp[0]);
	if(fpp[0]->profit == 0)
		return 0;
	fppNum++;

	for (int i = 0; i < k; i++)
	{
		int maxIncreaseProfit =0, maxProfit =  0;
		if(fppNum)
			maxProfit = fpp[0]->profit;
		if(bsppNum)
			maxIncreaseProfit = bspp[0]->increaseProfit;
		if(maxIncreaseProfit == 0 && maxProfit==0)//已经没有收入增长,堆为空或者没有增长空间。
			return totalProfit;
		if(maxIncreaseProfit > maxProfit)//用户持有的时间段进行卖出买入
		{
			buysellpointer tempBSP = bspp[0];
			buysellpointer tempBSP1 = NULL;
			if (tempBSP->sellDay -1 > tempBSP->beginDay +1)
			{
				tempBSP1 = (buysellpointer)malloc(sizeof(buysell));
				tempBSP1->beginDay = tempBSP->beginDay;
				tempBSP1->endDay = tempBSP->sellDay;
				findMaxIncreaseProfit(prices,tempBSP1);
				if(tempBSP->increaseProfit == 0)
					free(tempBSP1);
				else
					insertBSP(tempBSP1, &bspp, &bsppNum,&bsppNumLimit);
			}
			if(tempBSP->endDay -1 > tempBSP->buyDay+1)
			{
				tempBSP1 = (buysellpointer)malloc(sizeof(buysell));
				tempBSP1->beginDay = tempBSP->buyDay;
				tempBSP1->endDay = tempBSP->endDay;
				findMaxIncreaseProfit(prices,tempBSP1);
				if(tempBSP->increaseProfit == 0)
					free(tempBSP1);
				else
					insertBSP(tempBSP1, &bspp, &bsppNum,&bsppNumLimit);
			}
			if(tempBSP->buyDay -1 > tempBSP->sellDay +1)
			{
				freetimepointer tempFTP = (freetimepointer)malloc(sizeof(freetime));
				tempFTP->startDay = tempBSP->sellDay +1;
				tempFTP->endDay = tempBSP->buyDay - 1;
				findmaxprofit(prices, tempFTP);
				if(tempFTP->profit == 0)
					free(tempFTP);
				else
					insertFTP(tempFTP,&fpp,&fppNum,&fppNumLimit);
			}
			totalProfit += maxIncreaseProfit;
			deleteBSP(bspp,&bsppNum);
		}
		else
		{
			freetimepointer tempFTP =  fpp[0];
			int buyDay = tempFTP->buyDay;
			int sellDay = tempFTP->sellDay;
			freetimepointer tempFTP1 = NULL;
			if(buyDay > tempFTP->startDay + 1)
			{
				tempFTP1 = (freetimepointer)malloc(sizeof(freetime));
				tempFTP1->startDay = tempFTP->startDay;
				tempFTP1->endDay = buyDay -1;
				findmaxprofit(prices,tempFTP1);
				if(tempFTP1->profit == 0)
					free(tempFTP1);
				else
					insertFTP(tempFTP1,&fpp,&fppNum,&fppNumLimit);
			}
			if(tempFTP->endDay > tempFTP->sellDay + 1)
			{
				tempFTP1 = (freetimepointer)malloc(sizeof(freetime));
				tempFTP1->startDay = tempFTP->sellDay + 1;
				tempFTP1->endDay = tempFTP->endDay;
				findmaxprofit(prices,tempFTP1);
				if(tempFTP1->profit == 0)
					free(tempFTP1);
				else
					insertFTP(tempFTP1,&fpp,&fppNum,&fppNumLimit);
			}
			if (tempFTP->sellDay-1 > tempFTP->buyDay+1 )
			{
				buysellpointer tempBSP = (buysellpointer)malloc(sizeof(buysell));
				tempBSP->beginDay = tempFTP->buyDay;
				tempBSP->endDay = tempFTP->sellDay;
				findMaxIncreaseProfit(prices,tempBSP);
				if(tempBSP->increaseProfit == 0)
					free(tempBSP);
				else
					insertBSP(tempBSP,&bspp,&bsppNum,&bsppNumLimit);
			}
			totalProfit += maxProfit;
			deleteFTP(fpp,&fppNum);
		}
	}
	return totalProfit;
}

总结

这个题目花费了我一天多的时间,做为编程菜鸟的我还算可以,独立做出一道有一定难度的题目,还学会了不少知识。
  1. 不知道数组大小的情况下或在大小为变量的情况下,可以用malloc方法分配内存,如
    buysellpointer* newheap = (buysellpointer*)malloc(*bsppNumLimit *2 * sizeof(buysellpointer));
  2. 加强了对二重指针的理解,在我的解法中,还遇到了三层指针,如buysellpointer **heap,对指针、内存的理解更加深入了。
  3. 数组大小未知,可以先分配一定内存,如果不够,分配两倍内存,进行拷贝,然后返回。
  4. 对最大堆的插入和删除的理解深入,毕竟自己写代码。
  5. 不足在于代码太长,特别是两个不同类型的堆,每种操作要写两遍代码,十分不方便。shagnwang

leetcode 第188题,我的解法,Best Time to Buy and Sell Stock IV的更多相关文章

  1. 【LeetCode】Best Time to Buy and Sell Stock IV

    Best Time to Buy and Sell Stock IV Say you have an array for which the ith element is the price of a ...

  2. Java for LeetCode 188 Best Time to Buy and Sell Stock IV【HARD】

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  3. [LeetCode] Best Time to Buy and Sell Stock IV 买卖股票的最佳时间之四

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  4. LeetCode Best Time to Buy and Sell Stock IV

    原题链接在这里:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ 题目: Say you have an array ...

  5. 188. Best Time to Buy and Sell Stock IV leetcode解题笔记

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  6. 188. Best Time to Buy and Sell Stock IV

    题目: 链接: 题解: 测试: Reference:

  7. [LeetCode 121] - 买入与卖出股票的最佳时机(Best Time to Buy and Sell Stock)

    问题 假设你有一个数组,其中的第i个元素表示一只股票在第i天的价格. 如果只允许你完成一次交易(即买入并卖出股票一次),设计一个找出最大利润的算法. 初始思路 和122一样,基于买入与卖出股票的最佳时 ...

  8. [LeetCode] Best Time to Buy and Sell Stock with Cooldown 买股票的最佳时间含冷冻期

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  9. [LeetCode] Best Time to Buy and Sell Stock III 买股票的最佳时间之三

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

随机推荐

  1. [Java入门笔记] Java语言基础(二):常量、变量与数据类型

    常量与变量 什么是常量和变量 常量与变量都是程序在运行时用来存储数据一块内存空间 常量: 常量的值在程序运行时不能被改变,Java中声明常量必须使用final关键字.常量还可以分为两种意思: 第1种意 ...

  2. 测试Oracle 11gr2 RAC 非归档模式下,offline drop数据文件后的数据库的停止与启动测试全过程

    测试Oracle 11gr2 RAC 非归档模式下,offline drop数据文件后的数据库的停止与启动测试全过程 最近系统出现问题,由于数据库产生的日志量太大无法开启归档模式,导致offline的 ...

  3. html5——自定义属性

    自定义属性:添加前缀"data-" dataset属性:添加自定义属性后,通过元素的dataset属性来访问自定义属性的值(属性名没有data-前缀) data-name:data ...

  4. knockoutjs入门

    Knockoutjs入门 (概念相关)对于asp.net 后端开发人员来说我们几乎都接触过mvc框架.knockoutjs是MVVM模式对于我们来说有点陌生.Knockout是一个轻量级的UI类库,通 ...

  5. C/C++文字常量与常变量的概念与区别 分类: C/C++ 2015-06-10 22:56 111人阅读 评论(0) 收藏

    以下代码使用平台是Windows 64bits+VS2012. 在C/C++编程时,经常遇到以下几个概念:常量.文字常量.符号常量.字面常量.常变量.字符串常量和字符常量,网上博客资料也是千篇千律,不 ...

  6. 两表(多表)关联update的写法

    SQL Server示例: update a set a.gqdltks=b.gqdltks,a.bztks=b.bztks from landleveldata a,gdqlpj b where a ...

  7. Android+Sqlite 实现古诗阅读应用(三)

    往期传送门: Android+Sqlite 实现古诗阅读应用(一) Android+Sqlite 实现古诗阅读应用(二) 加入截图分享的功能. 很多应用都有分享的功能,我也想在我的古诗App里加入这个 ...

  8. HDU 5816 Hearthstone

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Descript ...

  9. 滑动的Button

    在介绍SwitchButton之前,先来看一下系统Button是如何实现的.源码如下: @RemoteView public class Button extends TextView { publi ...

  10. Linux系统如何查看版本信息

    输入"uname -a ",可显示电脑以及操作系统的相关信息.   输入"cat /proc/version",说明正在运行的内核版本.   输入"c ...