如何将'Double for loop(O(n ^ 2))'变为'Single for loop(O(n))'?

时间:2017-05-26 09:48:01

标签: java performance

我正在研究算法,我正在尝试以更有效的方式和更清洁的方式。

这是一种算法,可以找到不重复的值并返回其中的第一个值。

以下是我的代码。

// example input of array.
int[] A = [2, 5, 1, 5, 1, 3, 9, 2];

// check pairs. And make them -1
// from 0 index to last index.
for(int i = 0 ; i < A.length ; i++){
    // from the next index to the last index ( the rest indices ).
    for(int j=i+1; j < A.length ; j++){
        // if ith value and jth value are euqal and never checked make them -1 so that you can mark they have been visited.
        if(A[i]==A[j] && A[i]>0){
            A[i]=-1; A[j]=-1; 
        }
    }
}

// find the first number among the left positive values.
for(int i = 0 ; i < A.length ; i++){
    if(A[i]>0) return A[i];
}
// if there is no positive value, return 0;
return 0;

如您所见,这是O(n ^ 2)。我试图让这个更快或更清洁。我想我可以制作这个O(n),这意味着只使用一个for循环(不是循环的双重。)你认为它可能吗?

5 个答案:

答案 0 :(得分:4)

如@ gabi13的回答所述,最简单和最有效的方法可能是使用O(nlogn)排序算法,然后遍历数组,搜索不等于下一个(或前一个)的第一个元素。

但是,我想进一步澄清,因为你似乎在复杂的概念中混淆了你的问题。

将两个循环减少为一个不会将O(n²)变为O(n),除非它们是嵌套的(但是你想要一种方法来丢弃最后一个循环,而不是嵌套循环)。

你的第一个循环是导致O(n²)的循环,因为它有2个嵌套循环遍历数组。即使你删除了最后一个循环,你的代码也将保持为O(n²)。

尽管你的方法不能变成O(n)(参见@ gabe13的替代方法O(nlogn)的答案),你的实现可以进行优化。

首先,如果您只需要关注正值,如果A [i] <= 0,则无需检查对。在嵌套循环外移动该条件将提高效率。

此外,如果你只想要第一个正的非重复元素,只要A [i]> 0,就足以检查第一个嵌套循环,如果你找不到重复(即嵌套)循环结束没有找到一对)。在这种情况下,您已经拥有了解决方案。

答案 1 :(得分:2)

您想要返回第一个不重复的元素吗?因为如果你不必是第一个,你可以在O(nLGn)中完成。使用一些采用对数时间的方法对数组进行排序,例如mergesort,然后通过查找一个位置与下一个/上一个位置不同来遍历它

答案 2 :(得分:2)

您可以使用整数排序的想法。这是三个循环,但它们不是嵌套的。它可以处理负数。时间复杂度为O(n+m),其中n是数组的长度,m是数组中最大值和最小值之间的差异。

这是一个有效的java示例:

public class findDup {
    public static void main(String[] args) {
        int arr[] = { -4, -6, -4, 8, 9, 8, 9, 10, -3 };
        printNonDups(arr);
    }

    public static void printNonDups(int arr[]) {
    // Find max and min.                                                                                                                                                                                       

        int max=arr[0];
        int min=arr[0];

        // Find max and min. 
        // O(arr.length)
        for(int i=0; i<arr.length; i++) {
            if(arr[i]>max)
                max=arr[i];
            if(arr[i]<min)
                min=arr[i];
        }

        int tmp[] = new int[max-min+1];

        // Count the number of occurrences of each number.
        // O(arr.length) 
        for(int i=0; i<arr.length; i++) 
            tmp[arr[i]-min]++;

        // Print all unique values
        // O(max-min)
        for(int i=0; i<tmp.length; i++) 
            if(tmp[i]==1) {
                System.out.println(i+min);
                // break; // Uncomment to stop after first non-duplicate                                                                                                                                               
            }
    }
}

这是三个循环,但它们不是嵌套的。它可以处理负数。时间复杂度为O(n+m),其中n是数组的长度,m是数组中最大值和最小值之间的差异。

答案 3 :(得分:2)

所以根据我的理解,你想要返回第一个既不是0也不重复在下一个位置的值,如果没有返回0?

怎么样:

for( int i=0; i<A.length-1;i++)
{ 
    if( A[i]>0 && A[i]!=A[i+1] ) return A[i];
}
return 0;

当然,如果你还需要修改数组,因为你将继续使用它,你需要进一步的逻辑。

答案 4 :(得分:2)

如果您使用的是Java,则可以使用HashMap

使用HashMap在列表中查找未重复数字的代码:

public class NonDuplicateNumber {
    public static void main(String[] args) {
        int[] arr = {1,3,2,5,7,6,9,8,10,1,3,2,5,7,6,9,8,10,4};
        Map<Integer, Integer> countMap = new HashMap<Integer, Integer>();
        for(int i=0;i<arr.length;i++) {
            if(countMap.containsKey(arr[i])) {
                int count = countMap.get(arr[i]);
                count++;
                countMap.put(arr[i], count);
            } else {
                countMap.put(arr[i], 1);
            }
        }

        Set<Integer> keySet = countMap.keySet();
        for(Integer key : keySet) {
            int count = countMap.get(key);
            if(count == 1) {
                System.out.println("Non duplicate number: "+key);
            }
        }
    }
}