分治算法
分治策略:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小),则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设汁策略叫做分治法。
可使用分治法求解的一些经典问题
(1)二分搜索
(2)大整数乘法
(3)Strassen矩阵乘法
(4)棋盘覆盖
(5)合并排序
(6)快速非序
(7)线性时间选择
(8)最接近点对问题
(9)循环赛日程表
(10)汉诺塔
分治算法–最大子数组问题
暴力求解
internal class Program
{
static void Main(string[] args)
{
int[] priceArray = { 100, 113, 110, 85, 105, 102, 86, 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];
}
int total = priceFluctuationArray[0];//默认数组的第一个元素是最大子数组
int minIndex = 0;
int maxIndex = 0;
for(int i = 0;i < priceFluctuationArray.Length; i++)
{
//取得以i为子数组起点的所有子数组
for(int j = i; j < priceFluctuationArray.Length; j++)
{
//先确定i后,确定j,求i到j的数组和,用for循环求和
int totalTemp = 0; //临时 最大的子数组和
for(int index = i; index < j + 1; index++)
{
totalTemp += priceFluctuationArray[index];
}
if(totalTemp > total)
{
total = totalTemp;
minIndex = i;
maxIndex = j;
}
}
}
Console.WriteLine("在第"+minIndex+"买入");
Console.WriteLine("在第"+maxIndex+1+"卖出");
}
}
分治求解
拆解问题
- 求数组【low,high】最大子数组问题
- 取mid中间值,把区间划分为【low,mid】【mid + 1,high】
- i , j是最大子数组的开始索引和结束索引,那i,j位置只有三种情况
- i,j同时位于低区间
- i,j同时位于高区间
- i 位于低区间,j 位于高区间
- 得到三种情况的最大子数组,然后再比较哪个情况子数组最大
- 问题就转化为了先求【low,mid】的最大子数组,这个问题就和原问题是类似的,大问题变成了小问题
- 同理,求【mid + 1,high】也是一样
- 第三种情况可以直接使用for循环解决,因为已经确定mid,所以先求【i,mid】的最大数组,遍历一遍就可以确定 i 的位置,同理,求【mid + 1,j 】的最大数组,因为已经确定一端mid + 1, 只需要遍历一遍就可以确定 j 的位置,最后,两个最大数组相加就是最大子数组
internal class Program
{
//最大子数组的结构体
struct SubArray
{
public int startIndex;
public int endIndex;
public int total;//和
}
static void Main(string[] args)
{
int[] priceArray = { 100, 113, 110, 85, 105, 102, 86, 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(subArray.startIndex);
Console.WriteLine(subArray.endIndex);
Console.WriteLine( "我们在第"+subArray.startIndex+"天买入"+",第"+(subArray.endIndex+1)+"天卖出");
}
static SubArray GetMaxSubArray(int low, int high, int[] array)//取得array数组,从low到high的最大子数组,返回结构体(开始索引、结束索引、子数组的和)
{
//终止条件,求一个值的最大子数组
if (low == high)
{
SubArray subArray0;
subArray0.startIndex = low;
subArray0.endIndex = high;
subArray0.total = array[low];
return subArray0;
}
int mid = (low + high) / 2; //低区间[low,mid] 高区间[mid=1,high]
//1. 低区间的最大子数组
SubArray subArray1 = GetMaxSubArray(low, mid, array);
//2. 高区间的最大子数组
SubArray subArray2 = GetMaxSubArray(mid+1, high, array);
//3. 从[low,mid]找到最大子数组[i,mid]
int total1 = array[mid];
int startIndex = mid;
//临时存放最大子数组的和
int totalTemp = 0;
//从从mid到low找最大数组,已经确定一端为mid,往左遍历
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(subArray1.total>=subArray2.total && subArray1.total >= subArray3.total)
{
return subArray1;
}
else if (subArray2.total >= subArray1.total && subArray2.total >= subArray3.total)
{
return subArray2;
}
else
{
return subArray3;
}
}
}