从整数列表中找到最小缺失整数的最快方法

时间:2018-11-25 11:03:28

标签: c++ arrays algorithm vector time-complexity

我有一个100个随机整数的列表。每个随机整数的值都在0到99之间。允许重复,因此列表可能类似于

56, 1, 1, 1, 1, 0, 2, 6, 99...

我需要找到列表中包含的 not 的最小整数(> = 0)。

我最初的解决方法是:

vector<int> integerList(100); //list of random integers
...
vector<bool> listedIntegers(101, false);
for (int theInt : integerList)
{
    listedIntegers[theInt] = true;
}
int smallestInt;
for (int j = 0; j < 101; j++)
{
    if (!listedIntegers[j])
    {
        smallestInt = j;
        break;
    }
}

但是,这需要用于记账的辅助数组和第二个(可能是完整的)列表迭代。我需要执行此任务数百万次(实际应用是在贪婪的图形着色算法中,在这里我需要找到具有顶点邻接表的最小未使用颜色值),所以我想知道是否有一种聪明的方法来获取同样的结果却没有太多的开销?

6 个答案:

答案 0 :(得分:1)

我相信没有更快的方法。在这种情况下,您可以做的是重用cat file1 | tr ' ' '\n' | tr '[:upper:]' '[:lower:]' | grep -vwif file2 ,每个线程只需要一个这样的向量。

尽管更好的方法可能是重新考虑整个算法以完全消除此步骤。也许您可以在算法的每个步骤中更新最少未使用的颜色?

答案 1 :(得分:1)

已经一年了,但是...

想到的一个想法是在迭代列表时跟踪未使用值的间隔。为了实现高效查找,您可以将间隔作为元组保留在二叉搜索树中。例如,

因此,使用您的示例数据:

56, 1, 1, 1, 1, 0, 2, 6, 99...

最初,您将有未使用的间隔[0..99],然后,在处理每个输入值时:

56: [0..55][57..99]
1: [0..0][2..55][57..99]
1: no change
1: no change
1: no change
0: [2..55][57..99]
2: [3..55][57..99]
6: [3..5][7..55][57..99]
99: [3..5][7..55][57..98]

结果(在最小剩余间隔中的最小值):3

答案 2 :(得分:0)

由于无论如何您都必须扫描整个列表,因此您拥有的算法已经相当不错了。我可以建议的唯一不需衡量(肯定会加快速度)的改进就是摆脱RateManual(x$nper, x$pmt, x$pv) ,并用4个32位整数或2个64位整数的堆栈分配数组替换它。

然后,您将不必每次都在堆上分配数组的费用,并且可以更快地获得第一个未使用的数字(第一个0位的位置)。要查找包含前0个位的单词,您只需查找不是最大值的第一个,并且可以使用位乱七八糟的技巧快速获取该单词的前0个位。

答案 3 :(得分:0)

您的编程已经非常高效,以O(n)表示。只能找到边际收益。 一种可能性是将可能值的数量划分为大小为block的块,并进行注册 不是在布尔数组中,而是在int数组中,在这种情况下,存储的是模block的取值。
实际上,我们将大小为N的循环替换为大小为N/block的循环加上大小为block的循环。
从理论上讲,我们可以选择block = sqrt(N) = 12以使数量N/block + block最小化。
在下面的程序中,假设将整数除以8并计算模8的值应该很快,则选择大小为8的块。
但是,很明显,只有在相当大的最小值下才能获得增益!

constexpr int N = 100;
int find_min1 (const std::vector<int> &IntegerList) {
    constexpr int Size = 13;    //N / block
    constexpr int block = 8;
    constexpr int Vmax = 255;   // 2^block - 1

    int listedBlocks[Size] = {0};
    for (int theInt : IntegerList) {
        listedBlocks[theInt / block] |= 1 << (theInt % block);
    }
    for (int j = 0; j < Size; j++) {
        if (listedBlocks[j] == Vmax) continue;
        int &k = listedBlocks[j];
        for (int b = 0; b < block; b++) {
            if ((k%2) == 0) return block * j + b;
            k /= 2;
        }
    }
    return -1;
}

答案 4 :(得分:0)

可能您可以通过一些位操作将最后一步减少到O(1),在您的情况下__int128,在循环1中设置相应的位并调用类似__builtin_clz的方法或使用适当的方法bit hack

答案 5 :(得分:-1)

从集合中找到最小整数的最佳解决方案是https://codereview.stackexchange.com/a/179042/31480

这里是c ++版本。

int solution(std::vector<int>& A)
{
    for (std::vector<int>::size_type i = 0; i != A.size(); i++) 
    {
        while (0 < A[i] && A[i] - 1 < A.size()
            && A[i] != i + 1
            && A[i] != A[A[i] - 1]) 
        {
            int j = A[i] - 1;

            auto tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
    }

    for (std::vector<int>::size_type i = 0; i != A.size(); i++)
    {
        if (A[i] != i+1)
        {
            return i + 1;
        }
    }
    return A.size() + 1;
}