我在接受采访时被问到这个问题。考虑穿孔卡的情况,其中每个穿孔卡具有64位模式。我被建议每张卡片为int
,因为每个int都是一个位集合。
另外,我认为我有一个已经包含1000张这样的牌的阵列。我必须每次生成一个新元素,这与之前的1000张卡片不同。数组中的整数(也就是卡片)不一定要排序。
更多的是,对于C ++来说,问题怎么可能呢?64 bit int
来自何处?如何从数组中生成这个新卡,其中要生成的元素与所有元素不同已经存在于阵列中?
答案 0 :(得分:5)
有2个 64 64位整数,这个数字非常多 大于1000,最简单的解决方案就是生成一个 随机64位数,然后验证它不在表中 已生成数字。 (它的概率是 无穷小,但你可以肯定。)
由于大多数随机数生成器不生成64位值,因此
留下你自己写的,或(更简单),结合
值,例如通过生成8个随机字节,并memcpy
将它们放入a
uint64_t
。
至于验证号码是否已经存在,std::find
是
一两个新数字就好了;如果你必须做很多事情
查找,排序表和使用二进制搜索将是
值得的。或者某种哈希表。
答案 1 :(得分:4)
我可能会遗漏一些东西,但大多数其他答案在我看来都过于复杂。 只需对原始数组进行排序,然后从零开始计数:如果当前计数在数组中跳过它,否则你有下一个数字。该算法是O(n),其中n是新生成的数字的数量:对数组进行排序和跳过现有数字都是常量。这是一个例子:
#include <algorithm>
#include <iostream>
unsigned array[] = { 98, 1, 24, 66, 20, 70, 6, 33, 5, 41 };
unsigned count = 0;
unsigned index = 0;
int main() {
std::sort(array, array + 10);
while ( count < 100 ) {
if ( count > array[index] )
++index;
else {
if ( count < array[index] )
std::cout << count << std::endl;
++count;
}
}
}
答案 2 :(得分:2)
这是 O(n)算法:
int64 generateNewValue(list_of_cards)
{
return find_max(list_of_cards)+1;
}
注意:正如@amit在下面指出的那样,如果INT64_MAX
已经在列表中,则会失败。
据我所知,这是你获得 O(n)的唯一方法。如果你想处理那个(相当重要的)边缘情况,那么你将不得不进行某种正确的排序或搜索,这将带你到 O(n log n)。
答案 3 :(得分:2)
@arne几乎就在那里。您需要的是self-balancing interval tree,可以在O( n lg n )时间内构建。
然后选择顶部节点,它将存储一些间隔[ i , j ]。根据区间树的属性, i -1和 j +1都是新密钥的有效候选者,除非 i = {{ 1}}或 j = UINT64_MIN
。如果两者都为真,那么您已经存储了2 ^ 64个元素,并且您无法生成新元素。存储新元素,该元素需要O(lg n )最坏情况时间。
I.e。:init需要O( n lg n ),生成需要O(lg n )。两者都是最糟糕的数字。这种方法的最大优点是顶级节点将保持“增长”(存储更大的间隔)并与其后继者或前任节点合并,因此树实际上将在内存使用方面缩小并最终每次操作的时间衰减到O(1)。你也不会浪费任何数字,所以你可以继续生成,直到你有2 ^ 64个。
答案 4 :(得分:2)
此算法具有O(N lg N)
初始化,O(1)
查询和O(N)
内存使用情况。我假设您有一些整数类型,我将其称为int64
,它可以表示整数[0, int64_max]
。
[u, v]
[1, first number - 1]
[prev number + 1, current number - 1]
[last number + 1, int64_max]
您现在有一个列表,表示未使用的数字。您可以简单地迭代它们以生成新数字。
答案 5 :(得分:0)
我认为要走的路是使用某种散列。因此,您可以根据MOD操作说明将卡存储在某些存储桶中。在您创建某种索引之前,您将无法循环遍历整个数组。
如果你看一下java中的HashSet实现,你可能会得到一个线索。
编辑:我假设您希望它们是随机数,如果您不介意下面的序列MAX + 1是很好的解决方案:)
答案 6 :(得分:0)
您可以构建已存在元素的二叉树并遍历它,直到找到深度不是64且节点少于两个的节点。然后,您可以构造一个“缺少”的子节点并拥有一个新元素。如果我没有弄错的话,应该相当快,按大约O(n)的顺序。
答案 7 :(得分:0)
bool seen[1001] = { false };
for each element of the original array
if the element is in the range 0..1000
seen[element] = true
find the index for the first false value in seen
答案 8 :(得分:0)
初始化: 不要对列表进行排序。 创建一个包含0..999的新数组1000。 迭代列表,如果任何数字在0..999范围内,则通过将新数组中的值替换为列表中第一项的值,使新数组中的值无效。
插入: 使用递增索引到新数组。如果此索引处新数组中的值不是列表中第一个元素的值,请将其添加到列表中,否则请检查新数组中下一个位置的值。 当新阵列用完时,使用1000..1999重新填充它并使上述现有值无效。是的,这是循环遍历列表,但不必为每次插入完成。
在O(1)附近,直到列表变得如此之大,偶尔迭代它以使'new'新数组无效变得很重要。也许你可以通过使用一个增长的新数组来缓解这种情况,也许总是与列表一样大小?
RGDS, 马丁
答案 9 :(得分:0)
将它们全部放入大小为&gt;的哈希表中1000,找到空单元格(这是停车问题)。为此生成密钥。这当然会更好地适应更大的桌面尺寸。该表只需要1位条目。
编辑:这是鸽子原则。 对于散列函数,这需要“modulo tablesize”(或其他一些“半可逆”函数)。unsigned hashtab[1001] = {0,};
unsigned long long long long numbers[1000] = { ... };
void init (void)
{
unsigned idx;
for (idx=0; idx < 1000; idx++) {
hashtab [ numbers[idx] % 1001 ] += 1; }
}
unsigned long long long long generate(void)
{
unsigned idx;
for (idx = 0; idx < 1001; idx++) {
if ( !hashtab [ idx] ) break; }
return idx + rand() * 1001;
}
答案 10 :(得分:0)
根据此处的解决方案:question on array and number
由于有1000个数字,如果我们将其余数视为1001,则至少会有一个余数丢失。我们可以选择那个作为我们缺失的号码。
所以我们保持一个计数数组:C [1001],它将在C [r]中保持余数r(除以1001)的整数个数。
我们还维护一组C [j]为0的数字(比如使用链表)。
当我们移动窗口时,我们减少第一个元素的计数(比如余数i),即递减C [i]。如果C [i]变为零,我们将i添加到数字集合中。我们用我们添加的新数字更新C数组。
如果我们需要一个数字,我们只需从j的集合中选择一个C [j]为0的随机元素。
新数字为O(1),最初为O(n)。
这与其他解决方案类似但不完全相同。
答案 11 :(得分:0)
如此简单的事情:
1)将数组划分为等于1000以下的数字
2)如果所有数字都在下面的分区内,那么选择1001(或任何大于1000的数字),我们就完成了。
3)否则我们知道下层分区中不存在1到1000之间的数字。
4)创建1000个元素的bool数组,或1000个元素的长位域,或者诸如此类的数据并将数组初始化为全0的
5)对于下层分区中的每个整数,使用其值作为数组/位域的索引,并将相应的bool设置为true(即:进行基数排序)
6)遍历数组/位域并选择任何未设置值的索引作为解决方案
这可以在O(n)时间内工作,或者因为我们已经将所有内容限制在1000,技术上它是O(1),但一般来说是O(n)时间和空间。数据有三次传递,这不一定是最优雅的方法,但复杂性仍为O(n)。
答案 12 :(得分:-1)
你可以使用不在原始数组中的数字创建一个新数组,然后从这个新数组中选择一个。
¿O(1)?