K:求取数组中最大连续子序列和的四个算法

相关介绍:

 求取数组中最大连续子序列和问题,是一个较为“古老”的一个问题。该问题的描述为,给定一个整型数组(当然浮点型也是可以的啦),求取其下标连续的子序列,且其和为该数组的所有子序列和中值为最大的。例如数组A={1, 3, -2, 4, -5},则最大连续子序列和为6,即1+3+(-2)+ 4 = 6。解决该问题的算法有四种,根据其时间复杂度的高低,下面分别为这四种算法做介绍。

第一种:时间复杂度为O(N^3)

 该算法也是最容易想到的,很直观的算法,其算法的思路为,穷举数组中以某个元素为起始元素,以另一个元素为终止元素的元素子序列的和,并记录找出的子序列和最大的那个值

示例代码如下:

/**
 * 用于演示O(N^3)的算法
 * @param array 用于寻找最大连续子序列和的数组
 * @return 最大连续子序列和的值
 */
public int maxSubN3(int[] array)
{
    int result=0;
    int length=array.length;
    //用于控制子序列的起始下标
    for(int i=0;i<length;i++)
    {
        //用于控制子序列的终止下标
        for(int j=i;j<length;j++)
        {
            //用于计算起始下标到终止下标之间的元素的和
            int sum=0;
            for(int k=i;k<=j;k++)
            {
                sum+=array[k];
            }
            result=Math.max(sum, result);
        }
    }
    return result;
}

第二种:时间复杂度为O(N^2)

 该算法通过对上面算法的实现方式进行改进,从而降低了时间复杂度。该算法的思路同上一个的大致相同,但是注意到,每次将终止元素的下标往后移动一位的时候,其子序列和又重新计算了一遍。为此,可以通过保留其上一次从起始元素到终止元素的之间的子序列和的计算结果,来减低时间复杂度。

示例代码如下:

/**
 * 用于演示O(N^2)的算法
 * @param array 用于寻找最大连续子序列和的数组
 * @return 最大连续子序列和的值
 */
public int maxSubN2(int[] array)
{
    int length=array.length;
    int result=0;
    for(int i=0;i<length;i++)
    {
        int sum=0;
        for(int j=i;j<length;j++)
        {
            sum+=array[j];
            result=Math.max(result, sum);
        }
    }
    return result;
}

第三种:时间复杂度为O(NlogN)

 采用分治的思想,将数组划分为两半,最大连续子序列和出现的地方只可能有三种,要么在数组的左半边,要么在数组的右半边,要么横跨了数组的左右半边,同时其必定包含数组左半边的右边界以及数组右半边的左边界

/**
 * 用于演示O(NlogN)的算法
 * @param array 用于寻找最长子序列的数组
 * @return
 */
public int maxSubNlogN(int[] array)
{
    return maxSubNlogN(array,0,array.length-1);
}
/**
 * 用于递归的那个方法,low和high为数组的左右边界
 */
private int maxSubNlogN(int[] array,int low,int high)
{
    //当只剩下一个数组元素的时候,将其直接进行返回
    if(low==high)
        return Math.max(array[low], 0);
    //找到数组中间的分界点
    int mid=low+(high-low)/2;
    //用于寻找到横跨数组左右半边的那个最长子序列
    int lMax=0,rMax=0;
    int lSum=0,rSum=0;
    //找到包含右边界的左半边的最长子序列
    for(int i=mid;i>=low;i--)
    {
        lSum+=array[i];
        lMax=Math.max(lSum, lMax);
    }       
    //找到包含左边界的右半边的最长子序列
    for(int i=mid+1;i<high;i++)
    {
        rSum+=array[i];
        rMax=Math.max(rSum, rMax);
    }
    //返回三种情况中子序列和最大的那个值
    return Math.max(maxSubNlogN(array,mid+1,high),Math.max(rMax+lMax, maxSubNlogN(array,low,mid)));
}

第三种:时间复杂度为O(N)

 由分析可知,数组的最大连续子序列中的包含最大连续子序列的开头元素或者末尾元素的任意一个子序列里的元素值之和均为正数。为此,可以在仅需遍历一遍数组的过程中,计算出其最大连续子序列和

示例代码如下:

/**
 * 用于演示O(N)的算法
 * @param array 用于寻找最长子序列的数组
 * @return 最长子序列的值
 */
public int maxSubN(int[] array)
{
    int sum=0;
    int result=0;
    for(int i=0;i<array.length;i++)
    {
        sum+=array[i];
        if(sum>result)
            result=sum;
        if(sum<0)
            sum=0; 
    }
    return result;
}

代码汇总如下:

package other;
/**
 * 该类用于演示寻找最长子序列的几种算法
 * @author 学徒
 *
 */
public class MaxSubLined
{
    public static void main(String[] args)
    {
        int[] array={-2,11,-4,13,-5,2,-5,-3,12,-9};
        MaxSubLined max=new MaxSubLined();
        System.out.println(max.maxSubN3(array));
        System.out.println(max.maxSubN2(array));
        System.out.println(max.maxSubNlogN(array));
        System.out.println(max.maxSubN(array));
    }
    /**
     * 用于演示O(N^3)的算法
     * 算法的思路:
     * 穷举数组中以某个元素为起始的,数组中以该元素为起始元素的所有子序列的可能
     * 并找出其中子序列和最大的那个值
     * @param array 用于寻找最长子序列的数组
     * @return 最长子序列的值
     */
    public int maxSubN3(int[] array)
    {
        int result=0;
        int length=array.length;
        //用于控制子序列的起始下标
        for(int i=0;i<length;i++)
        {
            //用于控制子序列的终止下标
            for(int j=i;j<length;j++)
            {
                //用于计算起始下标到终止下标之间的元素的和
                int sum=0;
                for(int k=i;k<=j;k++)
                {
                    sum+=array[k];
                }
                result=Math.max(sum, result);
            }
        }
        return result;
    }
    /**
     * 用于演示O(N^2)的算法
     * 算法的思路:
     * 穷举数组中以某个元素为起始的,数组中以该元素为起始元素的所有子序列的可能
     * 并找出其中子序列和最大的那个值
     * @param array 用于寻找最长子序列的数组
     * @return 最长子序列的值
     */
    public int maxSubN2(int[] array)
    {
        int length=array.length;
        int result=0;
        for(int i=0;i<length;i++)
        {
            int sum=0;
            for(int j=i;j<length;j++)
            {
                sum+=array[j];
                result=Math.max(result, sum);
            }
        }
        return result;
    }
    /**
     * 用于演示O(NlogN)的算法
     * 算法的思路:
     * 采用分治的思想,将数组划分为两半,最长子序列出现的可能只有三种
     * 要么在数组的左半边,要么在数组的右半边,要么横跨了数组的左右半边,同时其必定包含
     * 数组左半边的右边界以及数组右半边的左边界
     * @param array 用于寻找最长子序列的数组
     * @return
     */
    public int maxSubNlogN(int[] array)
    {
        return maxSubNlogN(array,0,array.length-1);
    }
    /**
     * 用于递归的那个方法,low和high为数组的左右边界
     */
    private int maxSubNlogN(int[] array,int low,int high)
    {
        //当只剩下一个数组元素的时候,将其直接进行返回
        if(low==high)
            return Math.max(array[low], 0);
        //找到数组中间的分界点
        int mid=low+(high-low)/2;
        //用于寻找到横跨数组左右半边的那个最长子序列
        int lMax=0,rMax=0;
        int lSum=0,rSum=0;
        //找到包含右边界的左半边的最长子序列
        for(int i=mid;i>=low;i--)
        {
            lSum+=array[i];
            lMax=Math.max(lSum, lMax);
        }       
        //找到包含左边界的右半边的最长子序列
        for(int i=mid+1;i<high;i++)
        {
            rSum+=array[i];
            rMax=Math.max(rSum, rMax);
        }
        //返回三种情况中子序列和最大的那个值
        return Math.max(maxSubNlogN(array,mid+1,high),Math.max(rMax+lMax, maxSubNlogN(array,low,mid)));
    }
    /**
     * 用于演示O(N)的算法
     * 算法的思路:
     * 因为最长子序列中的任意一个子序列里的元素值总和均为正数
     * 为此,可以在遍历数组的过程中,计算出其最长子序列
     * @param array 用于寻找最长子序列的数组
     * @return 最长子序列的值
     */
    public int maxSubN(int[] array)
    {
        int sum=0;
        int result=0;
        for(int i=0;i<array.length;i++)
        {
            sum+=array[i];
            if(sum>result)
                result=sum;
            if(sum<0)
                sum=0; 
        }
        return result;
    }
}

回到目录|·(工)·)

转载于:https://www.cnblogs.com/MyStringIsNotNull/p/8242066.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
解一个数组的最大连续子序列和是一个常见的问题,可以使用动态规划的思想来解决。具体算法步骤如下: 1. 定义一个变量 max_sum 表示最大连续子序列和,初始值为数组第一个元素。 2. 定义一个变量 current_sum 表示当前连续子序列和,初始值为数组第一个元素。 3. 从数组第二个元素开始遍历,如果 current_sum 加上当前元素的值大于当前元素的值,则将 current_sum 更新为 current_sum 加上当前元素的值,否则将 current_sum 更新为当前元素的值。 4. 每次更新 current_sum 之后,将 current_sum 和 max_sum 中的较大值赋给 max_sum。 5. 遍历完成后,max_sum 中存储的即为最大连续子序列和。 下面是 C 语言代码实现: ```c int max_subarray_sum(int arr[], int n) { int max_sum = arr[0], current_sum = arr[0]; for (int i = 1; i < n; i++) { current_sum = current_sum + arr[i] > arr[i] ? current_sum + arr[i] : arr[i]; max_sum = current_sum > max_sum ? current_sum : max_sum; } return max_sum; } ``` 在该代码中,max_subarray_sum() 函数接收一个待处理的数组和数组的长度,首先定义 max_sum 和 current_sum 两个变量的初始值为数组的第一个元素。然后从数组的第二个元素开始遍历,如果 current_sum 加上当前元素的值大于当前元素的值,则将 current_sum 更新为 current_sum 加上当前元素的值,否则将 current_sum 更新为当前元素的值。每次更新 current_sum 之后,将 current_sum 和 max_sum 中的较大值赋给 max_sum。最后返回 max_sum 即为最大连续子序列和。 该算法的时间复杂度为 O(n),空间复杂度为 O(1)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值