C++ 查找时间为O(n)且空间为O(1)的重复有符号整数

C++ 查找时间为O(n)且空间为O(1)的重复有符号整数,c++,c,algorithm,math,C++,C,Algorithm,Math,(这是对以下内容的概括:) 问题:写出C++或C函数,其中O(n)和O(1)的时间和空间复杂度分别在给定数组中找到重复整数,而不改变它。 示例:给定{1,0,-2,4,4,1,3,1,-2}函数必须打印1,-2和4一次(以任何顺序)。 编辑:以下解决方案要求数组最小值到最大值范围内的每个整数都有一个双位(表示0、1和2)。所需字节数(无论数组大小)永远不会超过(INT\u MAX–INT\u MIN)/4+1 #include <stdio.h> void set_min_max

(这是对以下内容的概括:)

问题:写出C++或C函数,其中O(n)和O(1)的时间和空间复杂度分别在给定数组中找到重复整数,而不改变它。 示例:给定{1,0,-2,4,4,1,3,1,-2}函数必须打印1,-2和4一次(以任何顺序)。


编辑:以下解决方案要求数组最小值到最大值范围内的每个整数都有一个双位(表示0、1和2)。所需字节数(无论数组大小)永远不会超过
(INT\u MAX–INT\u MIN)/4+1

#include <stdio.h>

void set_min_max(int a[], long long unsigned size,\
                 int* min_addr, int* max_addr)
{
    long long unsigned i;

    if(!size) return;
    *min_addr = *max_addr = a[0];
    for(i = 1; i < size; ++i)
    {
        if(a[i] < *min_addr) *min_addr = a[i];
        if(a[i] > *max_addr) *max_addr = a[i];
    }
}

void print_repeats(int a[], long long unsigned size)
{
    long long unsigned i;
    int min, max = min;
    long long diff, q, r;
    char* duos;

    set_min_max(a, size, &min, &max);
    diff = (long long)max - (long long)min;
    duos = calloc(diff / 4 + 1, 1);
    for(i = 0; i < size; ++i)
    {
        diff = (long long)a[i] - (long long)min; /* index of duo-bit
                                                    corresponding to a[i]
                                                    in sequence of duo-bits */
        q = diff / 4; /* index of byte containing duo-bit in "duos" */
        r = diff % 4; /* offset of duo-bit */
        switch( (duos[q] >> (6 - 2*r )) & 3 )
        {
            case 0: duos[q] += (1 << (6 - 2*r));
                    break;
            case 1: duos[q] += (1 << (6 - 2*r));
                    printf("%d ", a[i]);
        }
    }
    putchar('\n');
    free(duos);
}

void main()
{
    int a[] = {1, 0, -2, 4, 4, 1, 3, 1, -2};
    print_repeats(a, sizeof(a)/sizeof(int));
}
#包括
void set_min_max(int a[],长无符号大小\
整数*最小地址,整数*最大地址)
{
长未签名的我;
如果(!size)返回;
*最小地址=*最大地址=a[0];
对于(i=1;i*max\u addr)*max\u addr=a[i];
}
}
重复无效打印(整数a[],长无符号大小)
{
长未签名的我;
int-min,max=min;
长-长差异,q,r;
char*duos;
设置最小值最大值(a、大小、最小值和最大值);
差异=(长-长)最大-(长-长)最小;
duos=calloc(差/4+1,1);
对于(i=0;i>(6-2*r))&3)
{

案例0:duos[q]+=(1因为你有一个整数数组,你可以使用简单的解决方案对数组进行排序(你没有说它不能修改)并打印副本。整数数组可以使用O(n)和O(1)的时间和空间复杂度进行排序。不过,通常可能需要O(n)空间,可以使用O(1)空间轻松实现就地二进制MSD基数排序(查看更多详细信息)。

O(1)空间约束很难解决

根据定义,打印阵列本身需要O(N)存储

现在,感觉很大方,我会告诉你,你可以在程序中有一个缓冲区O(1)存储,并且考虑程序之外的空间对你没有任何影响,因此输出不是问题…

尽管如此,O(1)空间约束还是让人感觉难以处理,因为输入数组上存在不变性约束。可能不是,但感觉是这样的


您的解决方案溢出,因为您试图在有限的数据类型中存储O(N)信息。

这里的定义有一个棘手的问题。O(N)是什么意思

Konstantin的回答声称基数排序的时间复杂度是O(n)。事实上,它是O(n logm),其中对数的底是所选的基数,M是数组元素可以具有的值的范围。因此,例如,32位整数的二进制基数排序将具有logm=32

在某种意义上,这仍然是O(n),因为logm是一个独立于n的常数。但是如果我们允许这一点,那么有一个更简单的解决方案:对于范围内的每个整数(所有4294967296个),遍历数组以查看它是否出现多次。在某种意义上,这也是O(n),因为4294967296也是一个独立于n的常数


我认为我的简单解算不上答案。但如果不是,那么我们也不应该允许基数排序。

我真的不明白为什么只有O(1)空格,而不是修改初始数组。我猜您需要一个额外的数据结构。例如,整数的范围是多少?如果它是0..N,就像您链接的另一个问题一样,您可以有一个大小为N的附加计数数组。然后是O(N)遍历原始数组并在当前元素的位置递增计数器。然后遍历另一个数组并打印计数>=2的数字。类似于:

int* counts = new int[N];
for(int i = 0; i < N; i++) {
    counts[input[i]]++;
}

for(int i = 0; i < N; i++) {
    if(counts[i] >= 2) cout << i << " ";
}

delete [] counts;
int*计数=新的int[N];
对于(int i=0;i如果(计数[i]>=2)不能我怀疑这是可能的。假设有一个解决方案,让我们看看它是如何工作的。我将尽可能地笼统地说明它不能工作……那么,它是如何工作的呢

在不失去一般性的情况下,我们可以说我们处理数组k次,其中k是固定的。当存在m个重复项时,解决方案也应该有效,m>>k。因此,在至少一个过程中,我们应该能够输出x个重复项,其中x随着m的增长而增长。为此,在前一个过程中计算并存储了一些有用的信息在O(1)存储中使用d。(阵列本身无法使用,这将提供O(n)存储。)

问题是:我们有O(1)个信息,当我们遍历数组时,我们必须识别x个数字(输出它们)。我们需要一个O(1)存储,如果元素在其中,它可以在O(1)时间内告诉我们。或者用另一种方式说,我们需要一个数据结构来存储n个布尔值(其中x是
true
),它使用O(1)空间,并占用O(1)该提问了


这个数据结构存在吗?如果不存在,那么我们就无法在一个具有O(n)时间和O(1)空间的数组中找到所有的重复项(或者有一些奇特的算法以完全不同的方式工作??).

假设您可以使用这样一个事实,即您没有使用所有的空间。您只需要为每个可能的值增加一位,并且在32位
int
值中有大量未使用的位

这有严重的限制,但在这种情况下有效。数字必须介于-n/2和n/2之间,如果重复m次,则将打印m/2次

void print_repeats(long a[], unsigned size) {
    long i, val, pos, topbit = 1 << 31, mask = ~topbit;
    for (i = 0; i < size; i++)
        a[i] &= mask;

    for (i = 0; i < size; i++) {
        val = a[i] & mask;
        if (val <= mask/2) {
           pos = val;
        } else {
            val += topbit;
            pos = size + val;
        }
        if (a[pos] < 0) {
            printf("%d\n", val);
            a[pos] &= mask;
        } else {
            a[pos] |= topbit;
        }
    }
}

void main() {
    long a[] = {1, 0, -2, 4, 4, 1, 3, 1, -2};
    print_repeats(a, sizeof (a) / sizeof (long));
}

大O表示法的定义是,它的参数是一个函数(f(x)),当函数(x)中的变量趋于无穷大时,存在一个常数K,使得目标成本函数为s
4
1
-2