带你吃透分治算法 (一)最大子数组

分治算法

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。就像归并排序的排序思想一样。

回忆一下归并排序的过程,我们在递归地求解一个问题时,在每层递归中应用了以下三个步骤:

  • 分解:将原问题划分为子问题,子问题的形式(性质)和原问题一样,只是规模变小了
  • 解决:当子问题的规模非常小了(如递归排序中排序元素只有一个时),则停止递归,直接进行求解。
  • 合并:将划分的两个子问题的解组合成原问题的解,不过有时我们会遇到需要求解与原问题不完全一样的子问题,我们也将其求解过程看做合并步骤的一部分。

因此,从上面的思路我就便定义了两种情况:

  • 递归情况:当子问题很大,需要递归求解的情况
  • 基本情况:子问题小到了一定的程度时(触底了),不再需要递归的情况

分治思想解最大子数组问题

最大子数组问题:给定一个整数数组,要求找出元素之和最大的子数组。即给你一个数组Arr[a1,a2,a3…,an],求下标j,k,使得sum = a(j)+a(j+1)+a(j+2)+…+a(k)为最大值。

首先我们知道,只有数组中包含负数时,最大子数组问题才有意义,如果所有的数组元素都是非负的,那么最大子数组便是其本身。

对于最大子数组问题,有许多解法,如暴力穷举法,动态规划法等等,我们因为是为了学习分治的思想,因此主要讲用分治思想解最大子数组,其他方法可以参考如下博文:最大子数组及其优化

使用分治策略的求解方法

加入给你如下数组A[13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7],则最大子数组如下图所示:

在这里插入图片描述

下面我们来思考如何使用分治思想求解最大子问题,如过我们将原数组以其中心划分为两部分A[low,…,mid]和A[mid+1,…,high],则我们要寻找的最大子数组一定只有三种可能位置。

  1. 最大子数组完全位于左半边A[low,…,mid]。
  2. 最大子数组完全位于右半边A[mid+1,…,high]。
  3. 最大子数组两边都占据了一些位置。

如下图所示:
在这里插入图片描述
在这里插入图片描述

因此,我们可以递归地对前两种可能性求解,因为这两个子问题仍是最大子数组问题,只是规模相比较原数组更小,之后便是寻找跨越中点的最大子数组,然后在这三种情况中选取最大者。

对于跨越中点的最大子数组的求法,我们可以将这个数组A[i,…,mid,…,j]划分为两部分A[i,…,mid]和A[mid+1,…,j],当这两部分分别为两边的最大子数组时,那么其合并起来的A[i,…,mid,…,j]即为跨越两边的最大子数组。

C语言实现:

//结构类型定义
typedef int ElemType;

typedef struct MaxSubarray{
    int l_index,r_index;
    ElemType Sum;
}MaxSubarray;
//跨越两边的最大子数组求解
MaxSubarray Get_CrossMaxSubArray(ElemType arr[],int left,int mid,int right)
{
    MaxSubarray MSArray;
    ElemType Sum_temp = INT_MIN;
    ElemType Sum_l=0,Sum_r=0;
    for(int i=mid;i>=left;i--)//找左半部最大子数组
    {
        Sum_l += arr[i];
        if(Sum_l > Sum_temp)
        {
            Sum_temp = Sum_l;
            MSArray.l_index = i;
        }
    }
    MSArray.Sum = Sum_temp;
    Sum_temp = INT_MIN;
    for(int i = mid+1;i<=right;i++)//找右半部最大子数组
    {
       Sum_r +=arr[i];
        if(Sum_r > Sum_temp)
        {
            Sum_temp = Sum_r;
            MSArray.r_index = i;
        } 
    }
    MSArray.Sum += Sum_temp;
    return MSArray;
}

解决了这个问题,那么接下来就简单了,按照上面讲的不断划分直到“触底”,然后寻找跨越两边的最大子数组,将三者比较返回最大的子数组。

//分治策略求解最大子数组问题
MaxSubarray GetMaxSubArray_Divide(ElemType arr[],int left,int right)
{
    MaxSubarray MSArray_left,MSArray_mid,MSArray_right;
    if(left == right)
    {
        MaxSubarray TEMP;
        TEMP.l_index = left;
        TEMP.r_index = right;
        TEMP.Sum = arr[left];
        return TEMP;
    }else
    {
        int mid = (left + right)/2;
        MSArray_left = GetMaxSubArray_Divide(arr,left,mid);
        MSArray_right = GetMaxSubArray_Divide(arr,mid+1,right);
        MSArray_mid = Get_CrossMaxSubArray(arr,left,mid,right);

        if(MSArray_mid.Sum >= MSArray_left.Sum && MSArray_mid.Sum >= MSArray_right.Sum)//寻找最大值
            return MSArray_mid;
        else if(MSArray_left.Sum >= MSArray_right.Sum && MSArray_left.Sum >= MSArray_mid.Sum)
            return MSArray_left;
        else
            return MSArray_right;
    }
}

分治算法解最大子数组的时间复杂度为O(n*log n),但是实际上还存在一个线性时间的算法,可参考最大子数组问题及其优化

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
操作系统是计算机系统中的核心组成部分,负责管理和协调计算机硬件和软件资源,提供程序运行环境。在CSDN上有很多关于操作系统的专题文章,以下将从操作系统的基本概念、功能和常见类型等方面简要介绍一下。 首先是操作系统的基本概念。操作系统是一种系统软件,它是计算机硬件和应用软件之间的桥梁,提供给应用程序一系列的服务和资源,同时负责调度和管理系统资源。它为用户屏蔽了底层的硬件差异,提供了一个统一的、易于使用的界面。 操作系统主要有四个基本功能。首先是处理器管理,负责将处理器分配给系统中的各个进程,并进行进程切换,实现多道程序并发执行。其次是内存管理,管理计算机的内存资源,包括分配、回收和保护等操作。再次是文件管理,负责管理文件的存储、命名和保护等操作,提供了文件操作的接口。最后是设备管理,负责管理计算机的各种设备,包括输入输出设备和存储设备等。 常见的操作系统有多种类型。最主流的是Windows、Linux和Mac OS等桌面操作系统。此外还有服务器操作系统,如Windows Server和Linux等,用于管理和部署服务器。还有嵌入式操作系统,如Android和iOS等,用于移动设备和物联网设备。操作系统也有实时操作系统,用于需要实时控制和响应的系统,如工控系统和航空航天系统等。 总之,操作系统是计算机系统中不可或缺的重要组成部分,通过CSDN上的相关文章,我们可以更深入了解操作系统的基本概念、功能和不同类型。这些知识对于理解计算机系统的工作原理和提升编程能力都有着重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值