这是我遇到过几次面试的问题,鉴于缺少四个数字,我真的不确定如何解决这个问题。我熟悉找不到一个或两个数字的算法,但是我看不到将它们中的任何一个概括为四个的方法。
这是我遇到过几次面试的问题,鉴于缺少四个数字,我真的不确定如何解决这个问题。我熟悉找不到一个或两个数字的算法,但是我看不到将它们中的任何一个概括为四个的方法。
Answers:
无论是面试还是实际工作,您的首要任务都必须是对您有意义的可行解决方案。这通常意味着你应该提供你能想到的,很简单,第一个解决方案,并方便你解释。
对我来说,这意味着对数字进行排序并寻找差距。但是,我从事业务系统和Web应用程序的工作。我不摆弄,也不希望我的团队去!
如果您面试的是低层次的,接近金属的工作,“分类”可能会遇到空白的凝视。他们希望您对位之类的东西感到轻松。您的第一个答案应该是:“哦,我要使用位图。” (或位数组或位设置。)
然后,无论哪种方式,即使您给“错误”的解决方案,如果您的面试官(或老板!)坚持不懈,您也可以提出一些改进或替代方案,着眼于经理所关注的特定领域。
O(n*log(n))
。(或O(n)用于整数桶排序!)BitSet
/ BitMap
/ BitArray
)BitArray
标记“找到的数字”。然后扫描0
。BitArray
/的另一遍传递BitSet
(以查找0
)。那是O(n)
,我想!管他呢。
解决您实际遇到的问题。只需先解决问题,并在必要时使用幼稚的解决方案。不要浪费大家的时间来解决尚不存在的问题。
由于它是一个文件,因此我假设您可以进行多次通过。首先创建一个由256个计数器组成的数组,遍历文件,并为每个数字递增将计数器索引为该数字的第一个字节。完成后,大多数计数器应位于2 ^ 24,但是1到4个计数器应具有较低的值。这些索引中的每个索引都代表缺失数字之一的第一个字节(如果少于4,则是因为多个缺失数字共享相同的第一个字节)。
对于每个索引,创建另一个包含256个计数器的数组,然后在文件上进行第二次传递。这次,如果第一个字节是之前的值之一,则根据第二个字节在其数组中增加一个计数器。完成后,再次查找小于2 ^ 16的计数器,您将获得丢失数字的第二个字节,每个字节都与它的第一个字节匹配。
对第三个字节再次执行此操作(注意,即使每个字节最多可以跟随4个不同的字节,每次遍历最多需要4个数组)和第四个字节,您已经找到了所有丢失的数字。
时间复杂度- O(n * log n)
空间复杂度- 恒定!
实际上,我认为n=2^32
可以作为参数,但是缺少数字的数量k=4
也是一个参数。假设k<<n
这意味着空间复杂度为O(k)
。
只是为了好玩(并且因为我目前正在尝试学习Rust),所以我在Rust中实现了它:https : //gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f。我选择使用文本表示形式,因为一个将要用〜2 ^ 32个数字运行...
如果是Java,则可以使用BitSet。好吧,其中两个,因为它们不能完全容纳所有32位数字。骨架代码,可能有错误:
BitSet bitsetForPositives = new Bitset(2^31); // obviously not 2^31 but you get the idea
BitSet bitsetForNegatives = new Bitset(2^31);
for (int value: valuesTheyPassInSomehow) {
if ((value & 0x80000000) == 0)
bitsetForPositives.set(value );
else
bitsetForNegatives.set(value & ~0x80000000);
}
然后使用 BitSet.nextClearBit()
查找缺少的人。
注意在以后添加:
请注意,使用此算法,并行运行耗时部分相当容易。假设原始文件已分为四个大致相等的部分。分配4对BitSet(2GB,仍可管理)。
我希望I / O仍然是速率限制的步骤,但是如果神奇地将所有数字都存储在内存中,则可以真正加快速度。
Integer.MIN_VALUE
正确。您可以屏蔽符号位,而不用否定它。
bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
可以使用一个位数组(真/假)解决此问题。这应该是最有效的结构,它使用数组的索引来保存是否找到该特定数字,从而保存所有数字的答案。
C#
var bArray = new BitArray(Int32.MaxValue);
//Assume the file has 1 number per line
using (StreamReader sr = File.OpenText(fileName))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
var n = int32.Parse(s);
bArray[n] = true;
}
}
然后只需遍历数组,对于仍然为假的那些值,它们将不在文件中。
您可以将文件分成较小的块,但是我可以在运行Windows 7(64位)的16.0 GB笔记本电脑上分配完整的int32最大大小数组(2147483647)。
即使我没有运行64位,也可以分配较小的位数组。我将对该文件进行预处理,以创建一组较小的文件,每个文件的范围为[0-64000] [64001-128000],以此类推。其中一些数字适合于可用的环境资源。浏览大文件,并将每个数字写入相应的设置文件。然后处理每个较小的文件。由于进行了预处理,因此需要花费一些时间,但是如果资源有限,则会绕开资源限制。
由于这是一个面试问题,因此我将向面试官展示一些有关约束的理解。那么,“所有可能的数字”是什么意思?大家都猜真的是0 ... 2 <(32-1)吗?通常的32位体系结构可以使用的不仅仅是32位数字。显然,这只是代表问题。
是要在32位系统上解决它,还是这是数字限制的一部分?例如,典型的32位系统将无法立即将文件加载到RAM。我还要提到的是,由于文件大小的限制,一个32位系统通常将无法包含所有数字的文件。好吧,除非它有一些巧妙的编码,例如“除这四个以外的所有数字”,否则这种问题将被轻松解决。
但是,如果您真的想将问题理解为“给出一个文件,其中所有数字都从0 ... 2 ^(32-1)除少数几个之外,请给我一个缺少的数字”(如果不是,这会很大),然后有很多解决方法。
琐碎但不可行:对于每个可能的数字,请扫描文件并查看文件是否在其中。
拥有512 MB的RAM和单次通过文件:标记从文件读取的每个数字(=该索引处的设置位),然后再对一次RAM进行传递并查看丢失的数字。
一种容易记住且易于在访谈中表达的方法是使用以下事实:如果您查看所有以N位为单位的数字,则每个位将恰好设置为这些值的一半,而不是另一半。
如果您遍历文件中的所有值并在末尾保留32个值,则最终将得到32个值,它们恰好是(2 ^ 32/2)或略小于该值。最大值(2 ^ 32/2)与总数之差为您在缺失值的每个位置设置的总位数。
一旦有了,就可以确定所有可能的4个值的集合,这些集合可以得出这些总数。鉴于此,您可以再次浏览文件中的值,以检查属于那些组合的任何值。当您找到一个时,将消除包含该值的组合。一旦只剩下一种可能的组合,就可以回答。
例如,使用半字节,您将具有以下值:
1010
0110
1111
0111
1101
1001
0100
0101
0001
1011
1100
1110
每个位置设置的总位数为:
7867
从8(4 ^ 2/2)中减去这些值,我们得到:
1021
这意味着有以下4种可能的值集:
1000
0000
0011
0010
1010
0001
0010
0000
(请原谅我,如果我错过了任何事情,我只是看得见的)
然后再次查看原始数字,我们立即找到1010,这意味着第一组是答案。
determine all the possible sets of 4 values that could give those totals
。我真的认为这是解决方案的重要组成部分,您的答案中缺少该部分。它也会影响时间和空间的复杂性。
假设文件按递增的顺序排序:
确保确实包含(2³²-4)个数字。
现在,如果文件完整(或者丢失的4个数字是最后4个),则读取文件中位置N的任何单词将返回匹配值N。
使用二分法搜索位置[0..2³²-4-1)来查找第一个非预期数字X1。
找到第一个丢失的数字后,再次对位置[X1 ..(2³²-4-1)]进行二分查找,以查找第二个丢失的X2:这次,读取位置N处的单词应返回匹配值N-1如果没有更多的遗漏号码(因为您传递了一个遗漏的号码)。
同样,对剩余的两个数字进行迭代。在第三次迭代中,读取位置N处的单词应返回N-2,而在第四次迭代中,应返回N-3。
警告:我尚未对此进行测试。但是我认为它应该起作用。:)
在现实生活中,我同意其他答案:第一个问题将是关于环境的。我们是否有可用的RAM(多少),是直接访问存储设备上的文件,这是单次操作(无需优化)还是关键操作(每个周期计数),是否有可用的外部排序实用程序?等等。
然后找到一个可以接受的妥协方案。这至少表明您在寻找算法之前就开始分析问题。
与所有标准问题一样,解决方案是在面试之前先用谷歌搜索它们。
这个问题和变化形式具有非常明确的“正确”答案,涉及对所有数字进行异或运算。它应该告诉您了解数据库中的索引或其他内容。因此,任何“可能的工作都为零,而不是纸上所说的”为零。
从好的方面来说,这些问题的定义有限,几个小时的修订会使您看起来像个天才。只要记住要假装自己在脑子里锻炼。
编辑。嗯,看来4与XOR有不同的方法
编辑。Downvoters:这是针对OP中所述确切问题的已出版教科书O(n)解决方案。