分治法求解
思路:在[low,high]区间取中间mid,将问题细化,变成了3种情况
- 最大子数组在[low,mid]区间;
- 最大子数组在[mid+1,high]区间;
- 最大子数组的起始在[low,mid]区间,终点在[mid+1,high]区间;
对于前2种,使用递归来查找,当找到low=mid(或mid+1=high),说明找到了
以上3种情况比较最大子数组的和,谁大即为所要的结果。
典型应用案例:
17天的股票,求哪天买入哪天卖出收益最大。(即求最大子数组问题)
天数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
价格 | 100 | 113 | 110 | 85 | 105 | 102 | 86 | 63 | 81 | 101 | 94 | 106 | 101 | 79 | 94 | 90 | 97 |
变化 | 13 | -3 | -25 | 20 | -3 | -16 | -23 | 18 | 20 | -7 | 12 | -5 | -22 | 15 | -4 | 7 |
该问题拟合成求最大子数组问题,及组成的数值和最大。
C#编程求解:
由于考虑到计算的结果得到的是包含起始买入时间,结束卖出时间和最大收益值三个变量,故此先定义一个结构体变量。
struct SubArray
{
public int startIndex;
public int endIndex;
public int total;
}
实现分治算法的灵魂核心算法
static SubArray GetMaxSubArray(int low,int high,int[] array)
{
if (low == high)
{
SubArray subArray;
subArray.startIndex = low;
subArray.endIndex = high;
subArray.total = array[low];
return subArray;
}
int mid=(low+high)/ 2;
SubArray subArray1 = GetMaxSubArray(low, mid, array);
SubArray subArray2 = GetMaxSubArray(mid + 1, high, array);
//第3种情况
//在[low mid]区间找最大数组[i,mid]
int total1 = array[mid];
int startIndex = mid;
int totalTemp = 0;
for (int i = mid; i >=low ; i--)
{
totalTemp += array[i];
if (totalTemp > total1)
{
total1 = totalTemp;
startIndex = i;
}
}
//在[mid+1,high]区间找最大子数组[mid+1,j]
int total2 = array[mid + 1];
int endIndex = mid + 1;
totalTemp = 0;
for (int j = mid+1; j < high; j++)
{
totalTemp += array[j];
if (totalTemp > total2)
{
total2 = totalTemp;
endIndex = j;
}
}
SubArray subArray3;
subArray3.startIndex = startIndex;
subArray3.endIndex = endIndex;
subArray3.total = total1 + total2;
//比较三种大情况下的值
if(subArray3.total>=subArray1.total && subArray3.total >= subArray2.total)
{
return subArray3;
}
else if(subArray2.total >= subArray1.total && subArray2.total >= subArray3.total)
{
return subArray2;
}
else
{
return subArray1;
}
}
在主函数中执行调用实现分治算法的方法(此处利用控制台环境实现)
static void Main(string[] args)
{
int[] priceArray = { 100, 113, 110, 85, 105, 102, 96, 63, 81, 101, 94, 106, 101, 79, 94, 90, 97 };
int[] priceFluctuationArray = new int[priceArray.Length - 1];//价格波动数值(后一天的减去前一天的数值)
for (int i = 1; i < priceArray.Length; i++)
{
priceFluctuationArray[i - 1] = priceArray[i] - priceArray[i - 1];
}
SubArray subArray = GetMaxSubArray(0,priceFluctuationArray.Length-1, priceFluctuationArray);
Console.WriteLine("从第{0}天开始买入", subArray.startIndex);
Console.WriteLine("从第{0}天进行卖出", subArray.endIndex + 1);
Console.ReadKey();
}