【问题标题】:How to return maximum sub array in Kadane's algorithm?如何在 Kadane 算法中返回最大子数组?
【发布时间】:2011-12-08 08:32:36
【问题描述】:
public class Kadane {
  double maxSubarray(double[] a) {
    double max_so_far = 0;
    double max_ending_here = 0;

    for(int i = 0; i < a.length; i++) {
      max_ending_here = Math.max(0, max_ending_here + a[i]);
      max_so_far = Math.max(max_so_far, max_ending_here);
    }
    return max_so_far;
  }
}

以上代码返回最大子数组的总和。

我将如何返回具有最大总和的子数组?

【问题讨论】:

  • 你的意思是从索引 0 开始的最大子数组吗?
  • 最大子数组不必从索引0开始,它取决于数组值

标签: java algorithm kadanes-algorithm


【解决方案1】:

类似这样的:

public class Kadane {
  double[] maxSubarray(double[] a) {
    double max_so_far = 0;
    double max_ending_here = 0;
    int max_start_index = 0;
    int startIndex = 0;
    int max_end_index = -1;

    for(int i = 0; i < a.length; i++) {
      if(0 > max_ending_here +a[i]) {
        startIndex = i+1;
        max_ending_here = 0;
      }
      else {
        max_ending_here += a[i];
      }

      if(max_ending_here > max_so_far) {
        max_so_far = max_ending_here;
        max_start_index = startIndex;
        max_end_index = i;
      }
    }

    if(max_start_index <= max_end_index) {
      return Arrays.copyOfRange(a, max_start_index, max_end_index+1);
    }

    return null;
  }
}

【讨论】:

  • 条件应该是 "if(0 >=max_ending_here+a[i]) 而不仅仅是 > 使其适用于数组,例如:{5,2,-4,3,2,-8 ,7,6,-1,-2,3,-50}
  • +1 好的测试用例。我看了一下这个,我想你的意思是 if(max_ending_here >= max_so_far)。然而,并不清楚较长的最大子数组是否比较短的子数组更正确。
  • 你能解释一下为什么我们需要 max-start-index 和 start-index 吗?我们可以摆脱 max-start-index 吗?
  • @user911 - 我看了看,无法找到一种干净的方法来摆脱一个或另一个而不用另一个变量替换它。这并不意味着虽然不可能。最终我们确实想要“最大子数组的起始索引”,即 max_start_index。你有什么想法?我考虑过的一些测试用例是 {5,2,-8, 1, 7} 和 {5, -4, 2, 1}
  • 这不适用于将所有负数的数组示例 {-6,-9,-3,-5-2}
【解决方案2】:

上面的代码有错误。应该是:

max_ending_here = Math.max(a[i], max_ending_here + a[i]);

不是:

max_ending_here = Math.max(0, max_ending_here + a[i]);

如果不是,则对于以下序列将失败:2、4、22、19、-48、-5、20、40,并返回 55 而不是正确答案 60。

http://en.wikipedia.org/wiki/Maximum_subarray_problem查看 Kadane 算法

【讨论】:

  • 正确。 max(a[i], max_ending_here + a[i]) 仅适用于所有负值的数组。
  • @AndrejKaurin 你可能想把你的代码改成if(A.length &gt;= 1){ for(var i = 1;i&lt;A.length;i++) { var x = A[i]; max_ending_here = Math.max(0, max_ending_here + x); max_so_far = Math.max(max_so_far, max_ending_here); } }
【解决方案3】:

我在一个列表中维护 max_so_far:

for(int i = 0;i<N;i++){
    max_end_here = Math.max(seq[i], max_end_here + seq[i]);
    sum_list.add(max_end_here);
    // System.out.println(max_end_here);
    max_so_far = Math.max(max_so_far, max_end_here);
}

然后在列表中查找最大和,其索引为子序列结束。 以索引为结束,向后搜索,找到最后一个值为正的索引。子序列开始就是这个索引。

for(int i=sum_list.size()-1; i>=0; i--){
    if(sum_list.get(i) == max_so_far){
        end = i;
        while(sum_list.get(i) > 0 && i>=0){
            i--;
        }
        start = (i==-1)?0:i+1;
        break;
    }
}

【讨论】:

    【解决方案4】:

    与算法密切相关的更简单的方法。

    int main()
    {
       int a[]={-2, 1, -3, 4, -1, 2, 1, -5, 4};
       int size=sizeof(a)/sizeof(a[0]);
       int startIndex=0,endIndex=0,i,j;
       int max_so_far=0,max_sum=-999;
       for(i=0;i<size;i++)
       {
       max_so_far=max_so_far+a[i];//kadane's algorithm step 1
       if(max_so_far>max_sum) //computing max
       {
          max_sum=max_so_far;
          endIndex=i;
       }
    
       if(max_so_far<0)
       {
       max_so_far=0;
       startIndex=i+1;
       }
    }
       cout<<max_so_far<<" "<<startIndex<<" "<<endIndex;
       getchar();
       return 0;
    }
    

    一旦你有了开始和结束索引。

    for(i=startIndex;i<=endIndex;i++)
    {
    cout<<a[i];
    }
    

    【讨论】:

      【解决方案5】:

      我们可以使用以下代码来跟踪最大子数组:

      import java.util.Arrays;
      
      public class KadaneSolution4MaxSubArray{
      
          public static void main(String[]args){
              int [] array = new int[]{13,-3,-25,20 ,-3 ,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
      
              int[] maxSubArray = maxSubArrayUsingKadaneSol(array);
              for(int e : maxSubArray){
                      System.out.print(e+"\t");
                  }
              System.out.println();
          }
      
          public static int[] maxSubArrayUsingKadaneSol(int array[]){
              long maxSoFar =array[0];
              long maxEndingHere =array[0];
              int startIndex = 0;
              int endIndex =0;
              int j=1;
              for(; j< array.length ;j++){
                  int val = array[j];
                  if(val >= val+maxEndingHere){
                          maxEndingHere = val;
                          startIndex = j;
                      }else {
                          maxEndingHere += val;
                          };
                  if(maxSoFar < maxEndingHere){
                          maxSoFar = maxEndingHere;
                          endIndex = j;
                      }
                  }
      
                  return Arrays.copyOfRange(array,startIndex,endIndex+1);
          }   
      }
      

      附注假设给定数组是最大子数组问题的候选,并且所有元素都不是负数

      【讨论】:

        【解决方案6】:

        每次启动新的子数组和时,更新可能的左(起始)索引。一旦 max_sum 更新,更新最终的左右(结束)。还要维护一个触发器,告知是否创建了新的子数组和。

        int last = 0;
            int sum  = Integer.MIN_VALUE;
            boolean fOrReset = true;
            int _L = -1, L = -1, R = -1;
        
            for (int j = 0; j < arr.length; j++) {
                last += arr[j];
                if (fOrReset) {
                    _L = j+1;
                }
                fOrReset = false;
                if (sum < last) {
                    sum = last;
                    L = _L;
                    R = j+1;
                }
                if (last < 0) {
                    last = 0;
                    fOrReset = true;
                }
            }
        

        【讨论】:

          【解决方案7】:
          private static int[] applyKadaneAlgorithmGetSubarrayOptimized(int[] input) {
              int localMax = input[0];
              int globalMax = input[0];
              int start = 0;
              int end = 0;
              for (int i = 1; i < input.length; i++) {
                  localMax = Math.max(localMax + input[i], input[i]);
                  if(localMax == input[i]) { //this is similar as --> input[i] > (localMax + input[i])
                      start = i;
                  }
                  if(localMax > globalMax) {
                      end = i;
                  }
                  globalMax = Math.max(localMax, globalMax);
              }
          
              //Below condition only occur`enter code here`s when all members of the array are negative integers
              //Example: {-9, -10, -6, -7, -8, -1, -2, -4}
              if(start > end) {
                  start = end;
              }
          
              return Arrays.copyOfRange(input, start, end + 1);
          }
          

          【讨论】:

          • 您能否澄清一下您想了解的内容、遇到的问题以及到目前为止您尝试过的内容?
          • 上述解决方案还可以处理所有负整数的数组。
          【解决方案8】:

          我知道这是一个旧线程,但为了清楚起见,我想分享我的解决方案版本。

          • sumMax 是存储 maxSubArray 和的变量
          • subSum 是用于比较总和是否增加的临时变量。
          • 我们知道只有在 subSum 重新开始时才应该移动下限

          public static int maxSubArray(int[] a, out int[] subArr){
                  int sumMax = int.MinValue;
                  int subSum = 0;
                  int iLow = 0, iHigh = 0;
                  for(int i=0; i<a.Length; i++){
                      subSum+=a[i];
                      if(subSum>sumMax){
                          sumMax = subSum;
                          //move high (upper bound) since sumMax increased 
                          iHigh = i;
                          
                          //if the subSum is new, then move lower bound
                          if(subSum == a[i]){
                              iLow = i;
                          }
                      }
                      subSum = Math.Max(subSum, 0);
                  }
                  
                  //build the array - returned as an out parameter
                  int index = 0;
                  subArr = new int[iHigh-iLow+1];
                  while(iLow <= iHigh){
                      subArr[index++] = a[iLow++];
                  } 
                  
                  return sumMax;
              } 
          

          【讨论】:

            【解决方案9】:

            C++ 中的另一种实现,包括 Kadane(实际上只是动态编程方法)和扩展 Kadane 以及索引计算和一些 cmets:

                int maxSubArray(vector<int>& nums) 
                {        
                    int n = nums.size();
                    
                    if(n == 0) return INT_MIN;
                    
                    // max sum that ends at index I
                    int sumMaxI = nums[0];
                    
                    // total max sum
                    int sumMax = nums[0];
                    for(int i = 1; i < n; i++)
                    {  
                        int curr = nums[i];
                        
                        // calc current max sum that ends at I
                        int currSumMaxI = sumMaxI + curr;
                        
                        // calc new max sum that ends at I
                        sumMaxI = max(currSumMaxI, curr);
                        
                        // calc new total max sum
                        sumMax = max(sumMax, sumMaxI);
                    }
                    
                    return sumMax;
                }    
                
                
                int maxSubArray_findBeginEnd(vector<int>& nums) 
                {        
                    int n = nums.size();
                    
                    if(n == 0) return INT_MIN;
                    
                    // max sum that ends at index I
                    int sumMaxI = nums[0];
                    // start index for max sum (end index is I)
                    int sumMaxIStart = 0;
                            
                    // total max sum
                    int sumMax = nums[0];
                    // start and end index for total max sum
                    int sumMaxStart = 0;
                    int sumMaxEnd = 0;
                    for(int i = 1; i < nums.size(); i++)
                    {  
                        int curr = nums[i];
                        
                        // calc current max sum that ends at I
                        int currSumMaxI = sumMaxI + curr;
                        
                        // calc new min sum that ends at I and its starting index
                        // this part is equal to: sumMaxI = max(currSumMaxI, curr);
                        // but additionaly enables to save new start index as well
                        if(curr > currSumMaxI)
                        {
                            sumMaxI = curr;
                            sumMaxIStart = i;
                        }
                        else
                            sumMaxI = currSumMaxI;
                             
                        // calculate total max sum
                        // this part is equal to: sumMax = max(sumMax, sumMaxI);
                        if(sumMaxI > sumMax)
                        {
                            sumMax = sumMaxI;
                            sumMaxStart = sumMaxIStart;
                            sumMaxEnd = i;
                        }
                        // this part is to additionaly capture longest subarray among all that have max sum
                        // also, of all subarrays with max sum and max len, one with smallest index
                        // will be captured
                        else if(sumMaxI == sumMax) 
                        {
                            if(i - sumMaxIStart > sumMaxEnd - sumMaxStart)
                            {
                                sumMaxStart = sumMaxIStart;
                                sumMaxEnd = i;
                            }
                        }
                    }
                    
                    // check validity of start and end indices
                    int checkSum = 0;
                    for(int i = sumMaxStart; i <= sumMaxEnd; i++)
                        checkSum += nums[i];
                    assert(checkSum == sumMax); 
                    
                    // output indices
                    cout << "Max subarray indices: [" << sumMaxStart << "," << sumMaxEnd << "]" << endl;
                    
                    return sumMax;
                }    
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-05-14
              • 1970-01-01
              • 2017-12-14
              • 1970-01-01
              • 2019-10-13
              • 2014-07-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多