与经典问题find an integer not among four billion given ones有关,但不完全相同。
通过 integers 进行澄清,我真正的意思只是其数学定义的一部分。也就是说,假设只有有限数量的整数。用C ++表示,它们是int
范围内的[INT_MIN, INT_MAX]
。
现在给定std::vector<int>
(无重复项)或std::unordered_set<int>
,其大小可以为40、400、4000左右,但又不过大,如何有效地生成一个保证为不是给定的吗?
如果不必担心溢出,那么我可以将所有非零值相乘并将其乘积加1。但是确实存在。对手的测试用例可以仔细地包含INT_MAX
。
我更喜欢简单,非随机的方法。有吗?
谢谢!
更新:为消除歧义,我们假设未排序的std::vector<int>
可以保证没有重复。所以我问是否有比O(n log(n))更好的东西。另外请注意,测试用例可能同时包含INT_MIN
和INT_MAX
。
答案 0 :(得分:31)
您可以只返回输入中未包含的N+1
个候选整数中的第一个。最简单的候选者是数字0
至N
。这需要O(N)
的空间和时间。
int find_not_contained(container<int> const&data)
{
const int N=data.size();
std::vector<char> known(N+1, 0); // one more candidates than data
for(int i=0; i< N; ++i)
if(data[i]>=0 && data[i]<=N)
known[data[i]]=1;
for(int i=0; i<=N; ++i)
if(!known[i])
return i;
assert(false); // should never be reached.
}
随机方法可以提高空间利用率,但在最坏的情况下可能需要对数据进行更多的传递。
答案 1 :(得分:9)
随机方法在这里确实非常有效。
如果我们要使用确定性方法,并假设大小 n 不太大,例如4000,那么我们可以创建大小为{的向量 x {1}}(或更大一点,例如4096,以便于计算),以0初始化。
对于范围中的每个m = n + 1
,我们只需设置x [array [i]模m] =1。
然后在 x 中进行简单的O(n)搜索将提供一个不在 array
中的值注意:模运算不完全是“%”运算
编辑:我提到通过选择4096的大小使计算更容易。更具体地讲,这意味着模运算是通过简单的i
操作执行的。
答案 2 :(得分:6)
如果允许使用以下算法对输入向量进行重新排序,则可以使用O(1)辅助空间找到O(N)时间中最小的未使用整数。 [注1](如果向量包含重复数据,该算法也将起作用。)
size_t smallest_unused(std::vector<unsigned>& data) {
size_t N = data.size(), scan = 0;
while (scan < N) {
auto other = data[scan];
if (other < scan && data[other] != other) {
data[scan] = data[other];
data[other] = other;
}
else
++scan;
}
for (scan = 0; scan < N && data[scan] == scan; ++scan) { }
return scan;
}
第一遍保证如果在位置k
之后找到了[0, N)
范围内的某个k
,那么它现在位于位置k
。通过交换完成此重新安排,以避免丢失数据。扫描完成后,数组中任何地方都不会引用值与其索引不同的第一个条目。
该断言可能不是100%显而易见的,因为可以从更早的索引中引用一个条目。但是,在那种情况下,该条目不能是与其索引不相等的第一个条目,因为较早的条目将满足该条件。
要查看此算法为O(N),应注意,仅当目标条目不等于其索引时才发生第6行和第7行的交换,并且在交换之后目标条目相等到它的索引。因此,最多可以执行N
交换,并且第5行的if
条件最多是true
次N
。另一方面,如果if
条件为假,则scan
将递增,这也只能发生N
次。因此,if
语句最多被评估2N
次(即O(N))。
[INT_MIN, 0)
上的有符号整数映射到无符号整数[INT_MAX, INT_MAX - INT_MIN)
上(减法运算是数学运算,不是根据C语义进行的,这不允许结果为在2的补码中,是相同的位模式。当然,这会改变数字的顺序,从而影响“最小的未使用整数”的语义;保留订单的映射也可以使用。答案 3 :(得分:4)
使x等于(INT_MIN..INT_MAX),然后针对所有像素进行测试。在失败时测试x ++(对于40/400/4000,很少见)。
答案 4 :(得分:2)
步骤1: 对向量进行排序。
那可以在O(n log(n))中完成,您可以在线找到一些不同的算法,最喜欢使用一种。
步骤2: 查找不在向量中的第一个int 。
轻松地从INT_MIN迭代到INT_MIN + 40/400/4000,检查向量是否具有当前int:
伪代码:
SIZE = 40|400|4000 // The one you are using
for (int i = 0; i < SIZE; i++) {
if (array[i] != INT_MIN + i)
return INT_MIN + i;
解决方案为 O(n log(n)+ n)含义: O(n log(n))
编辑:抱歉,请阅读您的编辑要求的内容比 O(n log(n))更好。
答案 5 :(得分:0)
对于在std::unordered_set<int>
中提供整数(而不是std::vector<int>
)的情况,您可以简单地遍历整数值的范围,直到遇到一个整数,即unordered_set<int>
中不存在。在std::unordered_set<int>
中搜索整数的存在非常简单,因为std::unodered_set
确实提供了通过其find()
成员函数进行搜索的功能。
这种方法的空间复杂度为 O(1)。
如果以int
(即std::numeric_limits<int>::min()
)的最低可能的值开始遍历,则将获得最低 {{ 1}}未包含在int
中:
std::unordered_set<int>
类似地,如果您开始使用int find_lowest_not_contained(const std::unordered_set<int>& set) {
for (auto i = std::numeric_limits<int>::min(); ; ++i) {
auto it = set.find(i); // search in set
if (it == set.end()) // integer not in set?
return *it;
}
}
(即int
)的最大可能值遍历,则会获得最低 std::numeric_limits<int>::max()
中未包含int
:
std::unordered_set<int>
假设哈希函数将int find_greatest_not_contained(const std::unordered_set<int>& set) {
for (auto i = std::numeric_limits<int>::max(); ; --i) {
auto it = set.find(i); // search in set
if (it == set.end()) // integer not in set?
return *it;
}
}
均匀地映射到int
的存储桶中,则可以在unordered_set<int>
上实现搜索操作在恒定的时间内。这样,运行时复杂度将为 O(M),其中 M 是您要查找的非整数值的整数范围的大小。 M 以unordered_set<int>
的大小为上限(即您的情况 M <= 4000 )。
实际上,使用这种方法,选择任何大小大于unordered_set<int>
的整数范围,都可以保证与unordered_set
中不存在的整数值相抵。 / p>