最大子数组问题

引出:从下图中求得,何时买入何时卖出,获得最大的利润值在这里插入图片描述
方法一:暴力求解

原理:对数组内每一个数A[i]进行遍历,然后遍历以它们为起点的子数组,比较各个子数组的大小,找到最大连续子数组

 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 startIndex = 0;
            int endIndex = 0;
            for (int i = 0; i < priceFluctuationArray.Length; i++)
            {
                //取得以i为子数组起点的 所有子数组 
                for (int j = i; j < priceFluctuationArray.Length; j++)
                {
                    //由i j 就确定了一个子数组
                    int totalTemp = 0;//临时 最大子数组的和 
                    for (int index = i; index < j + 1; index++)
                    {
                        totalTemp += priceFluctuationArray[index];
                    }
                    if (totalTemp > total)
                    {
                        //取得以i为子数组起点的 最大数组和的结果
                        total = totalTemp;
                        startIndex = i;
                        endIndex = j;
                    }
                }
            }
            Console.WriteLine("startindex : " + startIndex);//7
            Console.WriteLine("endIndex : " + endIndex);//10
            ////结果是第七天开始买,第11天开始卖,可获得最大利润
            Console.WriteLine("购买日期是第" + startIndex + "天  出售是第" + (endIndex + 1)+"天");
            Console.ReadKey();
        }
    }

方法二:分治法

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,
将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,
然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

 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[] pf = new int[priceArray.Length - 1];//价格波动的数组
            for (int i = 1; i < priceArray.Length; i++)
            {
                pf[i - 1] = priceArray[i] - priceArray[i - 1];
            }

            SubArray subArray = GetMaxSubArray(0, pf.Length - 1, pf);
            Console.WriteLine(subArray.startIndex);
            Console.WriteLine(subArray.endIndex);
            Console.WriteLine("我们在第" + subArray.startIndex + "天买入, 在第" + (subArray.endIndex + 1) + "天卖出");
            Console.ReadKey();
        }
		//这个方法是用来取得array 这个数组 从low到high之间的最大子数组
        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; // 低区间 [low,mid]  高区间[mid=1,high]

            SubArray subArray1 = GetMaxSubArray(low, mid, array);

            SubArray subArray2 = GetMaxSubArray(mid + 1, high, array);

            //从【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 (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;
            }
        }
    }
最大子数组是连续的若干数组元素,若其和是最大的,那么这个子数组就称为该数组的最大子数组,它是很多问题的抽象,比如购买股票,把相邻两天的股价之差作为数组元素,求在连续的某个时间段内买入股票的最佳时间和卖出股票的最佳时间就可以抽象为计算最大子数组问题 [^3]。 求解最大子数组问题有分治法和贪心算法(这里结合动态规划思想的变种贪心算法,即Kadane算法)等方法: - **分治法**:将数组 `X[1…n]` 分为 `X[1…n/2]` 和 `X[n/2 +1…n]`,递归求解子问题,包括数组 `X[1…n/2]` 的最大子数组 `S1`、数组 `X[n/2+1…n]` 的最大子数组 `S2`,然后计算跨中点的最大子数组 `S3`,数组 `X` 的最大子数组之和 `Smax = max{S1, S2, S3}`,时间复杂度为 $O(nlogn)$ [^5]。 ```python def find_max_crossing_subarray(arr, low, mid, high): left_sum = float('-inf') sum_val = 0 max_left = mid for i in range(mid, low - 1, -1): sum_val += arr[i] if sum_val > left_sum: left_sum = sum_val max_left = i right_sum = float('-inf') sum_val = 0 max_right = mid + 1 for j in range(mid + 1, high + 1): sum_val += arr[j] if sum_val > right_sum: right_sum = sum_val max_right = j return max_left, max_right, left_sum + right_sum def find_max_subarray(arr, low, high): if high == low: return low, high, arr[low] else: mid = (low + high) // 2 left_low, left_high, left_sum = find_max_subarray(arr, low, mid) right_low, right_high, right_sum = find_max_subarray(arr, mid + 1, high) cross_low, cross_high, cross_sum = find_max_crossing_subarray(arr, low, mid, high) if left_sum >= right_sum and left_sum >= cross_sum: return left_low, left_high, left_sum elif right_sum >= left_sum and right_sum >= cross_sum: return right_low, right_high, right_sum else: return cross_low, cross_high, cross_sum arr = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] low, high, max_sum = find_max_subarray(arr, 0, len(arr) - 1) print(f"最大子数组的起始下标: {low}, 结束下标: {high}, 最大和: {max_sum}") ``` - **Kadane算法**:从数组左边界开始,由左到右处理,记录到目前为止已处理过的最大子数组。若已知 `A[1...j]` 的最大子数组,`A[1...j+1]` 的最大子数组要么是 `A[1...j]` 的最大子数组,要么是某个子数组 `A[i...j+1]` 的最大子数组(`1 <= i <= j+1`),在已知 `A[1...j]` 的最大子数组的情况下,可以在线性时间内找出形如 `A[i...j+1]` 的最大子数组,时间复杂度为 $O(n)$ [^1]。 ```python def max_subarray_sum(arr): max_ending_here = max_so_far = arr[0] for num in arr[1:]: max_ending_here = max(num, max_ending_here + num) max_so_far = max(max_so_far, max_ending_here) return max_so_far arr = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] print(f"最大子数组和: {max_subarray_sum(arr)}") ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值