算法题|-分治法解决最大子数组问题
分治法就是将一个复杂难解决问题拆成一些容易解决的小问题,再依次解决而最终解决整个问题
new int[] { 2, -3, 4, 67, 6 }
这样一个下标为0到4的数组,要找最大子数组,需要将其拆分成两个子数组,mid=(0+4)/2
即为0~mid的左数组和mid+1~4的右数组
最大子数组可能会出现在以下三个地方
- 左数组中的某个最大子数组
- 右数组中的某个最大子数组
- 以mid为界,向左找到一个最大数组,向右找到一个最大数组,将两个数组合并
第三种情况非常容易得到,通过遍历查找就可以解决,而要从新的数组中找到又一个新的数组很明显应该用到递归的思想
下面直接贴代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestFun2 { class Program { static void Main(string[] args) { Program p = new Program(); SubArray s1 = GetMaxSubArray(0, 4, new int[] { 2, -3, 4, 67, 6 }); Console.WriteLine(s1.startIndex+" "+s1.endIndex+" "+s1.total); Console.ReadKey(); } #region 分治法解决最大数组问题 //用于存储数组起始和结束下标以及数组和的大小的结构体 struct SubArray { public int startIndex;//起始下标 public int endIndex;//结束下标 public int total;//数组和 } /// <summary> /// 获得最大子数组 /// 思路: /// 1.对于一个数组找最大子数组,我们先找到中点,中点将数组分为左右两个数组 /// 2.最大子数组可能是左数组中的最大子数组 /// 3.也可能是右数组中的最大子数组 /// 4.也可能是以中点为界,中点至左边某个下标的最大子数组加上中点至右边某个下标的最大子数组 /// </summary> /// <param name="low">起始</param> /// <param name="high">结束</param> /// <param name="array">数组</param> /// <returns>返回一个最大的子数组</returns> static SubArray GetMaxSubArray(int low, int high, int[] array) { //递归终止条件:起始下标和终止下标相等时 if (low==high) { SubArray subarray_last; subarray_last.startIndex = low; subarray_last.endIndex = high; subarray_last.total = array[low]; return subarray_last; } //找到中点,对左右两边分别进行递归查找 int mid = (low + high) / 2; //找到左右数组中的最大数组 SubArray a1 = GetMaxSubArray(low, mid, array); SubArray a2 = GetMaxSubArray(mid + 1, high, array); //找到左右数组a1和a2后还需要找到中间的数组a3 //首先处理中点左边的最大子数组问题 从【low,mid】找到最大子数组[i,mid] int startIndex = mid;//作为中间数组a3的起始坐标 int biggestArray_left = array[mid];//当前最大的中点左边子数组 int counter = 0;//计数器 for (int i = mid; i >= low; i--) { counter += array[i]; if (counter>biggestArray_left) { biggestArray_left = counter; startIndex = i; } } //处理中点右边的最大子数组问题 从【mid+1,high】找到最大子数组[mid+1,j] int endIndex = mid + 1; int biggestArray_right = array[mid+1]; counter = 0; for (int i = mid+1; i <= high; i++) { counter += array[i]; if (counter>biggestArray_right) { biggestArray_right = counter; endIndex = i; } } //中间的最大子数组a3即刚刚找到的两个数组的合并 SubArray a3; a3.startIndex = startIndex; a3.endIndex = endIndex; a3.total = biggestArray_left + biggestArray_right; //找到三个子数组后对其进行比较取最大的 return (a1.total > a2.total ? a1 : a2).total > a3.total ? (a1.total > a2.total ? a1 : a2) : a3; } #endregion } }