用于查找循环排序数组中正数总和的分治算法[关闭]

Posted

技术标签:

【中文标题】用于查找循环排序数组中正数总和的分治算法[关闭]【英文标题】:Divide & Conquer algorithm for finding sum of positive numbers in circularly sorted array [closed] 【发布时间】:2020-02-02 17:13:42 【问题描述】:

我正在尝试为下一个问题找到 O(N) 的分治法解决方案:

给定一个循环排序数组,我需要正数之和。即:

If the array is: -2, 0, 3, 4, 11, 13, -23, -15, -8

Then the algorithm should return 31.

我想我用下面的代码很接近它,但它奇怪地返回 -17 并且我找不到问题调试:

public class Main 

private static final int[] TEST_VECTOR = new int[]-2, 0, 3, 4, 11, 13, -23, -15, -8;

public static int sumPositives2(int[] vector) 
    return maximumSum(vector,0, vector.length - 1);


// Function to find Maximum subarray sum using
// divide and conquer
public static int maximumSum(int[] A, int left, int right)

    // If array contains only one element
    if (right == left) 
        return A[left];
    

    // Find middle element of the array
    int mid = (left + right) / 2;

    // Find maximum subarray sum for the left subarray
    // including the middle element
    int leftMax = Integer.MIN_VALUE;
    int sum = 0;
    for (int i = mid; i >= left; i--)
    
        if(A[i] > 0) 
            sum += A[i];
        
    

    // Find maximum subarray sum for the right subarray
    // excluding the middle element
    int rightMax = Integer.MIN_VALUE;
    sum = 0;    // reset sum to 0
    for (int i = mid + 1; i <= right; i++)
    
        if(A[i] > 0) 
            sum += A[i];
        
    

    // Recursively find the maximum subarray sum for left
    // subarray and right subarray and tale maximum
    int maxLeftRight = maximumSum(A, left, mid) +
            maximumSum(A, mid + 1, right);

    // return maximum of the three
    return maxLeftRight + leftMax + rightMax;


public static void main(String[] args)

    System.out.println("The Maximum sum of the subarray is " +
            maximumSum(TEST_VECTOR, 0, TEST_VECTOR.length - 1));//Should be 31



编辑:这个解决方案会是 O(N) 吗?

感谢您的宝贵时间。

【问题讨论】:

由于您需要遍历所有数字才能获得总和(如果所有数字都是正数),则至少需要 O(N) 时间。 我不确定这段代码在做什么,而且似乎有很多缺陷。例如,整个第一个循环没有影响,因为计算了 sum,但在使用之前它被设置回 0。leftMaxrightMax 都不会更改。停止子句在返回元素之前不检查元素是正数还是负数。 但是考虑到它是循环排序的,我猜应该有一种方法可以通过攻击“中间”值来获得 O(log N) 我的建议是从头开始。一步一步做。从一个没有分而治之的可行解决方案开始,然后尝试看看如何使用它(不确定是否有这样做的理由,但如果你坚持练习 - 没关系)。 @dev 攻击?你能告诉我2, 3, _, 1 的总和吗?它也是循环排序的。很明显,如果您不知道空白的值,我要求您做的事情是不可能的。期望计算机能够在 log n 操作中执行此操作是相同的原则。 【参考方案1】:

如果您遍历数组一次并仅对正数求和,您可以轻松获得O(n) 解决方案。增强的for 循环似乎很合适:

public static void main(String[] args) 
    int[] circularlySortedArray = -2, 0, 3, 4, 11, 13, -23, -15, -8;

    // define a variable for the sum
    int sumOfPositives = 0;

    // go through all numbers in the array (n numbers)
    for (int number : circularlySortedArray) 
        // check if the number is positive
        if (number >= 0) 
            // and add it to the sum variable
            sumOfPositives += number;
        
    

    // then print the result
    System.out.println("The sum of positive numbers is " + sumOfPositives);

这种情况下的输出是

The sum of positive numbers is 31

排序对算法没有任何影响。

如果它有效,您的解决方案将是 O(n),但您不必在这里分而治之,因为无论如何它不能在少于 O(n) 的时间内完成,它不是二分搜索。

编辑 由于上面的文字和代码似乎不够令人满意,我重新考虑了我关于分而治之的说法。该任务可能在少于n 的步骤中完成,但O(n) 仍然是正确的。排序确实提供了不遍历数组的所有元素的可能性,但这并不是在每种情况下都能实现的。

想象一下以下数组,它们都是循环排序的,请看看它们下面的模式,它们可能是这里分而治之的解决方案和/或具有 的解决方案的关键平均情况(Big Theta)小于n,而最坏情况(Big O)仍将保持O(n)...

示例都有n = 7元素,每个都有p = 4正元素(包括0)和l = 3负元素。遍历所有这些总是O(n),这里是O(6)。在某些情况下,排序提供了有关数组末尾的信息,这使程序员能够在某些情况下将最佳情况(Big Omega)减少到O(p + 1) = O(p) = O(4)

下面的数组需要 n 步,因为必须检查每个元素

-3, -2, -1, 0, 1, 2, 3
  n   n   n  p  p  p  p

下一个示例采用n - 1 步骤,因为在所有正数之后有两个负数,您只需找到第一个即可满足break 条件。那是因为在较低的索引处已经有一个负数。

-1, 0, 1, 2, 3, -2, -3
  n  p  p  p  p   n   n

下一个只需要p + 1(这意味着O(p + 1) = O(p))步骤,因为您可以在找到的第一个负数处break循环。为什么?因为数组从可能的最小正数开始(根据定义),找到的第一个负数表示不需要进一步处理。

0, 1, 2, 3, -1, -2, -3
 p  p  p  p   n   n   n

最后一个示例需要再次执行n 步骤,因为您要查找位于数组开头和结尾的正数。没有机会直接知道它们在哪个索引处。

3, -3, -2, -1, 0, 1, 2
 p   n   n   n  p  p  p

优化平均情况的唯一方法(我知道)是根据可能的模式实现循环的break 条件。所以存储索引并根据它们进行检查,但我认为效果不会那么大。

这是我的第一种方法,可能会在几个方面进行优化,我只尝试过这个编辑的例子:

public static void main(String[] args) 
    int[] circularlySortedArray =  0, 1, 2, 3, -1, -2, -3 ;

    // define a variable for the sum of positive values
    int sumOfPositives = 0;
    // define a variable for the lowest index of a positive number
    int firstPositiveIndex = -1;
    // define a variable for the lowest positive number found
    int smallesPositiveNumber = 0;

    // start iterating the array
    for (int i = 0; i < circularlySortedArray.length; i++) 
        System.out.println("Current index: " + i 
                + ", current value: " + circularlySortedArray[i]);
        // provide a variable for the current number to make this code a little more
        // readable
        int number = circularlySortedArray[i];

        // check if the current number is positive
        if (number >= 0) 
            // add it to the sum
            sumOfPositives += number;
            System.out.println("Added " + number 
                    + " to sumOfPositives (now: " + sumOfPositives + ")");
            // check if it is the first positive number found
            if (firstPositiveIndex < 0) 
                // if yes, set the variable value accordingly
                System.out.println("First and smallest positive number (" 
                                    + number 
                                    + ") found at index " 
                                    + i);
                firstPositiveIndex = i;
                smallesPositiveNumber = number;
            
            System.out.println("————————————————————————————————");
         else 
            // break conditions based on index & value of the smallest positive number found
            if (i > firstPositiveIndex && firstPositiveIndex > 0) 
                System.out.println("Stopped the loop at index " + i);
                break;
             else if (smallesPositiveNumber == 0 && firstPositiveIndex == 0) 
                System.out.println("Stopped the loop at index " + i);
                break;
            
            System.out.println(number + " is not positive, skip it");
            System.out.println("————————————————————————————————");
            continue;
        
    

    System.out.println("The sum of positive numbers is " + sumOfPositives);

【讨论】:

抱歉,我正在寻找分而治之的解决方案。 @dev 好的,没问题...如果它不合适,我不会寻找某种解决方案,但不禁止这样做,所以祝你好运一个;-) 你没有回答实际问题@deHaar @dev 查看我的编辑,我希望我能回答更多,现在......【参考方案2】:

你的要求是不可能的。

输入数组可能都是正值,这意味着您至少必须读取和求和所有元素。这是 O(n)。

即使不是所有元素都是正数,除非定义不超过 O(log n) 个元素是正数,否则相同的结论仍然存在。

【讨论】:

谢谢,你是对的,更新到 O(n)

以上是关于用于查找循环排序数组中正数总和的分治算法[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

查找没有溢出的整数数组的总和

分治算法用于在数组中查找峰值。

用于查找数组中最小值的分治算法

算法——分治和快速排序

排序算法之归并

分治(Divide and Conquer)算法之归并排序