O(n)算法找到从1到n(非奇数)的连续整数数组中的奇数输出

时间:2013-10-14 22:21:00

标签: java arrays algorithm time-complexity big-o

我在尝试解决这个问题时遇到了很多麻烦,而麻烦的根源在于创建O(n)复杂度的算法。这是我正在努力解决的问题:

  

长度为A的数组n包含[0, .., n - 1]范围内的整数。但是,它只包含n - 1个不同的数字。因此,其中一个数字丢失,另一个数字重复。编写一个Java方法,将A作为输入参数并返回缺少的数字;该方法应该在O(n)中运行。

     

例如,当A = [0, 2, 1, 2, 4]时,oddOneOut()应该返回3;当A = [3, 0, 0, 4, 2, 1]时,oddOneOut()应该返回5

显然,这是一个使用O(n2)算法解决的简单问题,(很可能是O(n),我只是没有看到它!)。我试图用各种方法解决它,但无济于事。我试图用Java解决它,但是如果你更习惯解决Python,那也没关系。

提前谢谢你......

4 个答案:

答案 0 :(得分:34)

假设缺少的数字为x,副本为y。如果添加所有数字,总和将为:

(n - 1) * n / 2 - x + y

从上面可以找到(x - y) .....(1)

同样,对数字的平方求和。总和将是:

  

(n - 1) * n * (2 * n - 1) / 6 - x2 + y2

从上面你得到(x2 - y2) ....(2)

(2) / (1) gives (x + y).....(3)

(1)+(3)给出2 * x,您可以找到xy

请注意,在此解决方案中, O(1)额外存储空间并且 O(n)时间复杂度。上面的其他解决方案是不必要的O(n)额外存储。

混合C / C ++中的代码更清晰:

#include <stdio.h>

int findDup(int *arr, int n, int& dup, int& missing)
{
    int sum = 0;
    int squares = 0;

    for (int i = 0; i < n; i++) {
        sum += arr[i];
        squares += arr[i] * arr[i];
    }

    sum = (n - 1) * n / 2 - sum; // x - y

    squares = (n - 1) * n * (2 * (n - 1) + 1) / 6 - squares; // x^2 - y^2

    if (sum == 0) {
        // no duplicates
        missing = dup = 0;
        return -1;
    }
    missing = (squares / sum + sum) / 2; // ((x^2 - y^2) / (x - y) + (x - y)) / 2 = ((x + y) + (x - y)) / 2 = x

    dup = missing - sum; // x - (x - y) = y

    return 0;
}


int main(int argc, char *argv[])
{
    int dup = 0;
    int missing = 0;

    int a[] = {0, 2, 1, 2, 4};

    findDup(a, sizeof(a) / sizeof(int), dup, missing);
    printf("dup = [%d], missing = [%d]\n", dup, missing);

    int b[] = {3, 0, 0, 4, 2, 1};
    findDup(b, sizeof(b) / sizeof(int), dup, missing);
    printf("dup = [%d], missing = [%d]\n", dup, missing);

    return 0;
}

输出:

dup = [2], missing = [3]
dup = [0], missing = [5]

一些python代码:

def finddup(lst):
    sum = 0
    sumsq = 0
    missing = 0
    dup = 0
    for item in lst:
        sum = sum + item
        sumsq = sumsq + item * item
    n = len(a)
    sum = (n - 1) * n / 2 - sum
    sumsq = (n - 1) * n * (2 * (n - 1) + 1) / 6 - sumsq
    if sum == 0:
        return [-1, missing, dup]
    missing = ((sumsq / sum) + sum) / 2
    dup = missing - sum
    return [0, missing, dup]

found, missing, dup = finddup([0, 2, 1, 2, 4])
if found != -1:
    print "dup = " + str(dup) + " missing = " + str(missing)

print finddup([3, 0, 0, 4, 2, 1])

输出:

dup = 2 missing = 3
[-1, 0, 0]

答案 1 :(得分:16)

迭代数组两次:那仍然是O(n)。创建一个临时的布尔数组(或一个Java BitSet)来保存你得到的数字。第二次进行循环时,检查布尔数组中是否有孔。

答案 2 :(得分:4)

使用哈希集并单次传递以检测哪个数字是重复的。在同一次迭代中,跟踪所有数字的累积总和。

现在计算所有数字不同的预期总数:n * (n - 1) / 2。减去你找到的总数。您将留下“缺失”数字减去副本。添加副本以获得答案。

由于哈希表访问是常量时间,并且我们使用单次传递,因此这是O(n)。 (请注意,单次传递不是绝对必要的:Martijn指出固定数量的传递仍然是线性复杂度是正确的。)

答案 3 :(得分:1)

这可能是有意义的,虽然我不确定在什么条件下(如果有的话)它表现最好。我们的想法是,我们将每个元素移动到数组中的正确位置(0以索引0等),直到它变得清楚什么是缺失的和什么是额外的。

def findmissing(data):
    upto = 0
    gap = -1
    while upto < len(data):
        #print data, gap
        if data[upto] == upto:
            upto += 1
            continue
        idx = data[upto]
        if idx is None:
            upto += 1
            continue
        data[upto], data[idx] = data[idx], data[upto]
        if data[upto] == data[idx]:
            print 'found dupe, it is', data[upto]
            data[upto] = None
            gap = upto
            upto += 1
        elif data[upto] is None:
            gap = upto
    return gap

if __name__ == '__main__':
    data = range(1000)
    import random
    missing = random.choice(data)
    print missing
    data[missing] = data[0]
    data[0] = random.choice(data[1:])
    random.shuffle(data)
    print 'gap is', findmissing(data)

这是O(n),因为每个步骤 递增upto 将值移动到数组中的“正确”位置,并且每个步骤事情只能发生n次。