Easy interview question got harder: given numbers 1..100, find the missing number(s) given exactly k are missing
我有一段很有趣的面试经历。问题开始很容易:
Q1: We have a bag containing numbers
1 ,2 ,3 , …,100 . Each number appears exactly once, so there are 100 numbers. Now one number is randomly picked out of the bag. Find the missing number.
当然,我以前听过这个面试问题,所以我很快就回答了:
A1: Well, the sum of the numbers
1 + 2 + 3 + … + N is(N+1)(N/2) (see Wikipedia: sum of arithmetic series). ForN = 100 , the sum is5050 .Thus, if all numbers are present in the bag, the sum will be exactly
5050 . Since one number is missing, the sum will be less than this, and the difference is that number. So we can find that missing number inO(N) time andO(1) space.
在这一点上,我认为我做得很好,但突然间问题发生了意想不到的转变:
Q2: That is correct, but now how would you do this if TWO numbers are missing?
我以前从未见过/听到/考虑过这种变化,所以我惊慌失措,无法回答这个问题。面试官坚持要了解我的思维过程,所以我提到,也许我们可以通过与预期的产品进行比较来获得更多的信息,或者在从第一关收集了一些信息之后再进行第二关,等等,但我实际上只是在黑暗中射击,而不是有一条通向解决方案的清晰道路。n.名词
面试官确实鼓励我说,有第二个方程式确实是解决问题的一种方法。在这一点上,我有点心烦意乱(因为在手前不知道答案),我问这是一种通用的(阅读:"有用的")编程技术,还是仅仅是一个诡计/成功的答案。
面试官的回答让我很惊讶:你可以概括一下找出3个缺失数字的方法。事实上,你可以把它推广到寻找缺少K的数字。
Qk: If exactly k numbers are missing from the bag, how would you find it efficiently?
这是几个月前的事了,我还是搞不清这是什么技术。显然,由于我们必须至少扫描一次所有数字,所以存在一个
所以这里的问题很简单:
- 如何解决问题2?
- 你将如何解决问题3?
- 你将如何解决问题?
澄清
- 通常有n个从1到n的数字,而不仅仅是1到100。
- 我不想寻找明显的基于集合的解决方案,例如使用一个位集,用指定位的值对每个数字的存在/不存在进行编码,因此在额外的空间中使用
O(N) 位。我们负担不起任何与n成比例的额外空间。 - 我也不想寻找明显的排序优先方法。这种方法和基于集合的方法在面试中值得一提(它们很容易实现,并且依赖于n,可能非常实用)。我在寻找圣杯解决方案(可能实际实现,也可能不实际实现,但仍然具有所需的渐进特性)。
因此,当然,您必须扫描
下面是Dimitris Andreou链接的摘要。
记住第i次幂的和,其中i=1,2,…,k。这就减少了求解方程组的问题。
A1A2+AK=B1
A12+A22+…+AK2=B2
…
A1K+A2K+…+AKK=BK
使用牛顿的恒等式,知道biallows to compute
C1=A1+A2+…A <> K</Sub >
C2=A1A2+A1A3+…+AK-1AK
…
CK=A1A2A <> K</Sub >
如果展开多项式(x-a1)…(x-ak)系数将精确为c1,…,ck—请参见vi_te公式。由于每个多项式因子都是唯一的(多项式环是欧几里得域),这意味着ai是唯一确定的,直到排列为止。
这就证明了记忆的力量足以恢复数字。对于常数k,这是一个很好的方法。
但是,当k发生变化时,计算c1的直接方法非常昂贵,因为例如ckk是所有缺失数字的乘积,量级n!/(N-K)!为了克服这个问题,在zqfield中进行计算,其中q是一个素数,这样n<=q<2n-它由伯特兰的假设存在。证明不需要改变,因为公式仍然成立,多项式的因式分解仍然是唯一的。您还需要一个有限域上的因子分解算法,例如Berlekamp或Cantor Zassenhaus的算法。
常数k的高级伪代码:
- 计算给定数字的第i次幂
- 减法得到未知数的第i次幂和。调用sums bi。
- 使用牛顿恒等式计算bi的系数;称之为ci。基本上,C1=B1;C2=(C1B1-B2)/2;有关精确公式,请参阅维基百科。
- 系数多项式xk-c1xk-1+…+CK
- 多项式的根是所需的数字a1、…、ak。
对于变化的k,使用Miller-Rabin等方法求出质数n<=q<2n,并在所有数字都减少模q的情况下执行步骤。
编辑:此答案的前一版本说明,可以使用特征2(q=2^(log n))的有限字段,而不是zq,其中q是prime。但事实并非如此,因为牛顿公式需要除以不超过k的数字。
你可以通过阅读muthukrishnan的几页来找到它——数据流算法:难题1:寻找缺失的数字。它精确地显示了您正在寻找的推广。也许这就是你的面试官读到的,也是他提出这些问题的原因。
现在,如果人们开始删除被穆楚克里希南治疗所包含或取代的答案,并使这篇文章更容易找到的话。:)
另请参见SDCVVC的直接相关答案,其中还包括伪代码(hurray!不用看那些复杂的数学公式了:(谢谢,干得好!).
我们可以通过把数字本身和数字的平方相加来解q2。
然后我们可以把问题减少到
1 2 | k1 + k2 = x k1^2 + k2^2 = y |
其中,
替换给了我们:
1 | (x-k2)^2 + k2^2 = y |
然后我们就可以解决这个问题来确定缺失的数字。
正如@j_random_hacker所指出的,这与在o(n)时间和o(1)空间中查找重复项非常相似,我在这里的答案也适用。
假设"包"由一个基于1的数组
首先,我们用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | for i := n - k + 1 to n A[i] := A[1] end for for i := 1 to n - k while A[A[i]] != A[i] swap(A[i], A[A[i]]) end while end for for i := 1 to n if A[i] != i then print i end if end for |
第一个循环将
第二个循环排列扩展数组,以便如果元素
请注意,虽然它有一个嵌套循环,但它仍然在
第三个循环打印数组
我请了一个4岁的孩子来解决这个问题。他把数字分类,然后数数。这有一个空间要求的O(厨房地板),它的工作一样容易,但许多球丢失。
不确定,如果这是最有效的解决方案,但是我将循环遍历所有条目,并使用一个位集来记住,设置了哪些数字,然后测试0位。
我喜欢简单的解决方案——我甚至相信,它可能比计算和或平方和等更快。
我没有查过数学,但我怀疑在计算
基于数字和的解的问题是,它们没有考虑存储和处理具有大指数的数字的成本…在实践中,为了使它适用于非常大的n,将使用一个大数字库。我们可以分析这些算法的空间利用率。
我们可以分析SDCVVC和DimitrisAndreou算法的时空复杂性。
存储:
1 2 3 4 5 6 7 | l_j = ceil (log_2 (sum_{i=1}^n i^j)) l_j > log_2 n^j (assuming n >= 0, k >= 0) l_j > j log_2 n \in \Omega(j log n) l_j < log_2 ((sum_{i=1}^n i)^j) + 1 l_j < j log_2 (n) + j log_2 (n + 1) - j log_2 (2) + 1 l_j < j log_2 n + j + c \in O(j log n)` |
所以
使用的总存储量:
使用空间:假设计算
1 2 3 4 5 | t = k ceil(\sum_i=1^n log_2 (i)) = k ceil(log_2 (\prod_i=1^n (i))) t > k log_2 (n^n + O(n^(n-1))) t > k log_2 (n^n) = kn log_2 (n) \in \Omega(kn log n) t < k log_2 (\prod_i=1^n i^i) + 1 t < kn log_2 (n) + 1 \in O(kn log n) |
使用总时间:
如果时间和空间令人满意,可以使用简单的递归算法。让B!我是包里的第i个条目,之前的数字以及k删除的次数。在haskell语法中…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | let -- O(1) isInRange low high v = (v >= low) && (v <= high) -- O(n - k) countInRange low high = sum $ map (fromEnum . isInRange low high . (!)b) [1..(n-k)] findMissing l low high krange -- O(1) if there is nothing to find. | krange=0 = l -- O(1) if there is only one possibility. | low=high = low:l -- Otherwise total of O(knlog(n)) time | otherwise = let mid = (low + high) `div` 2 klow = countInRange low mid khigh = krange - klow in findMissing (findMissing low mid klow) (mid + 1) high khigh in findMising 1 (n - k) k |
使用的存储:
等一下。正如问题所述,袋子里有100个数字。不管k有多大,这个问题都可以在恒定时间内解决,因为在一个循环的最多100-k次迭代中,可以使用一个集合并从集合中删除数字。100是常数。剩下的一组数字就是你的答案。
如果我们把解推广到从1到n的数值,除了n以外没有什么变化不是常数,所以我们是在o(n-k)=o(n)时间内。例如,如果我们使用一个位集,我们在O(n)时间中将位设置为1,迭代这些数字,在我们进行(o(n-k)=O(n))时将位设置为0,然后我们就得到了答案。
在我看来,面试官是在问你如何用O(k)时间而不是O(n)时间打印出最后一集的内容。显然,使用位集,您必须遍历所有n位,以确定是否应该打印数字。但是,如果更改集合的实现方式,可以在k次迭代中打印出数字。这是通过将数字放入一个对象来实现的,该对象将存储在哈希集和双重链接列表中。从哈希集中删除对象时,也会将其从列表中删除。答案将留在现在长度为k的列表中。
这是一个使用k位额外存储的解决方案,没有任何巧妙的技巧,而且非常简单。执行时间o(n),额外空间o(k)。只是为了证明这可以在不首先阅读解决方案或成为天才的情况下解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | void puzzle (int* data, int n, bool* extra, int k) { // data contains n distinct numbers from 1 to n + k, extra provides // space for k extra bits. // Rearrange the array so there are (even) even numbers at the start // and (odd) odd numbers at the end. int even = 0, odd = 0; while (even + odd < n) { if (data [even] % 2 == 0) ++even; else if (data [n - 1 - odd] % 2 == 1) ++odd; else { int tmp = data [even]; data [even] = data [n - 1 - odd]; data [n - 1 - odd] = tmp; ++even; ++odd; } } // Erase the lowest bits of all numbers and set the extra bits to 0. for (int i = even; i < n; ++i) data [i] -= 1; for (int i = 0; i < k; ++i) extra [i] = false; // Set a bit for every number that is present for (int i = 0; i < n; ++i) { int tmp = data [i]; tmp -= (tmp % 2); if (i >= even) ++tmp; if (tmp <= n) data [tmp - 1] += 1; else extra [tmp - n - 1] = true; } // Print out the missing ones for (int i = 1; i <= n; ++i) if (data [i - 1] % 2 == 0) printf ("Number %d is missing ", i); for (int i = n + 1; i <= n + k; ++i) if (! extra [i - n - 1]) printf ("Number %d is missing ", i); // Restore the lowest bits again. for (int i = 0; i < n; ++i) { if (i < even) { if (data [i] % 2 != 0) data [i] -= 1; } else { if (data [i] % 2 == 0) data [i] += 1; } } } |
为了解决2(和3)缺少数字的问题,您可以修改
将集合相对于随机轴
通过比较透视值和每个分区的大小(
a)如果每个分区缺少一个数字,则使用求和差法查找每个缺少的数字。
b)如果一个分区同时缺少两个数字,且该分区为空,则缺少的数字可以是
如果一个分区缺少2个数字,但不是空的,则递归到该分区。
由于只有2个丢失的数字,该算法总是丢弃至少一个分区,因此保留了QuickSelect的
这里有一个不使用就地分区的实现,因此这个示例不满足空间需求,但它确实说明了算法的步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <?php $list = range(1,100); unset($list[3]); unset($list[31]); findMissing($list,1,100); function findMissing($list, $min, $max) { if(empty($list)) { print_r(range($min, $max)); return; } $l = $r = []; $pivot = array_pop($list); foreach($list as $number) { if($number < $pivot) { $l[] = $number; } else { $r[] = $number; } } if(count($l) == $pivot - $min - 1) { // only 1 missing number use difference of sums print array_sum(range($min, $pivot-1)) - array_sum($l) ." "; } else if(count($l) < $pivot - $min) { // more than 1 missing number, recurse findMissing($l, $min, $pivot-1); } if(count($r) == $max - $pivot - 1) { // only 1 missing number use difference of sums print array_sum(range($pivot + 1, $max)) - array_sum($r) ." "; } else if(count($r) < $max - $pivot) { // mroe than 1 missing number recurse findMissing($r, $pivot+1, $max); } } |
演示
你能查查每个号码是否都存在吗?如果是,您可以尝试:
S = sum of all numbers in the bag (S < 5050) Z = sum of the missing numbers 5050 - S
如果缺少的数字是
x = Z - y and
max(x) = Z - 1
所以你检查一下从
可能该算法适用于问题1:
甚至更好:
1 2 3 4 5 6 7 8 9 10 11 | def GetValue(A) val=0 for i=1 to 100 do val=val^i done for value in A: do val=val^value done return val |
这个算法实际上可以扩展为两个缺失的数字。第一步保持不变。当我们用两个缺失的数字调用getValue时,结果将是一个
现在,为了从val中筛选出a1和a2,我们取val中的任何一个设置位。假设
如果两个列表的总和和两个列表的积,则可以求解q2。
(l1为原件,l2为修改单)
1 2 | d = sum(l1) - sum(l2) m = mul(l1) / mul(l2) |
我们可以对此进行优化,因为算术级数的和是第一个和最后一个项的平均值的N倍:
1 2 | n = len(l1) d = (n/2)*(n+1) - sum(l2) |
现在我们知道了(如果A和B是删除的数字):
1 2 | a + b = d a * b = m |
所以我们可以重新安排:
1 2 | a = s - b b * (s - b) = m |
然后乘以:
1 | -b^2 + s*b = m |
重新排列,使右边为零:
1 | -b^2 + s*b - m = 0 |
然后用二次公式求解:
1 2 | b = (-s + sqrt(s^2 - (4*-1*-m)))/-2 a = s - b |
python 3代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from functools import reduce import operator import math x = list(range(1,21)) sx = (len(x)/2)*(len(x)+1) x.remove(15) x.remove(5) mul = lambda l: reduce(operator.mul,l) s = sx - sum(x) m = mul(range(1,21)) / mul(x) b = (-s + math.sqrt(s**2 - (-4*(-m))))/-2 a = s - b print(a,b) #15,5 |
我不知道sqrt、reduce和sum函数的复杂性,所以我无法计算出这个解决方案的复杂性(如果有人知道,请在下面评论)。
对于第二季度,这是一个比其他解决方案效率低一点的解决方案,但仍然有O(N)运行时,占用O(K)空间。
其思想是运行原始算法两次。在第一个例子中,你会得到一个丢失的总数,它给你一个丢失数字的上界。我们把这个号码叫做
因此,您再次循环所有数字,丢弃第一个间隔中未包含的所有数字。也就是说,你要跟踪他们的总数。最后,您将知道丢失的两个数字中的一个,并将其扩展到第二个。
我有一种感觉,这种方法可以被推广,在输入的一次传递过程中,可能会有多个搜索以"并行"的方式运行,但我还没有弄清楚是如何进行的。
有一种通用的方法来概括这样的流算法。我们的想法是使用一些随机化的方法,希望能将
- 制作一个数组,
a ,大小为u = k^2 。 - 选择任何通用哈希函数,
h : {1,...,n} -> {1,...,u} 。(像乘法移位) - 每增加一个
i ,增加a[h(i)] += i 。 - 对于输入流中的每个数字
x ,减少a[h(x)] -= x 。
如果所有丢失的数字都被散列到不同的存储桶中,那么数组的非零元素现在将包含丢失的数字。
根据通用散列函数的定义,特定对发送到同一个bucket的概率小于
请注意,该算法使用
实际上,通过使用注释中描述的技巧,我们甚至可以比幂和法更有效。
我认为这可以在没有任何复杂的数学方程和理论的情况下完成。以下是一个就地O(2N)时间复杂性解决方案的建议:
输入表单假设:
#袋中数量=n
#缺失数字的数量=k
袋子中的数字由长度n的数组表示。
algo的输入数组长度=n
数组中缺少的条目(从包中取出的数字)将替换为数组中第一个元素的值。
最初的袋子看起来像[2,9,3,7,8,6,4,5,1,10]。如果取出4,值4将变为2(数组的第一个元素)。因此,取出4个后,袋子看起来就像是[2,9,3,7,8,6,2,5,1,10]
此解决方案的关键是在遍历数组时,通过对该索引处的值求反来标记已访问数字的索引。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | IEnumerable<int> GetMissingNumbers(int[] arrayOfNumbers) { List<int> missingNumbers = new List<int>(); int arrayLength = arrayOfNumbers.Length; //First Pass for (int i = 0; i < arrayLength; i++) { int index = Math.Abs(arrayOfNumbers[i]) - 1; if (index > -1) { arrayOfNumbers[index] = Math.Abs(arrayOfNumbers[index]) * -1; //Marking the visited indexes } } //Second Pass to get missing numbers for (int i = 0; i < arrayLength; i++) { //If this index is unvisited, means this is a missing number if (arrayOfNumbers[i] > 0) { missingNumbers.Add(i + 1); } } return missingNumbers; } |
你可能需要澄清O(K)的意思。
对于任意k,这里有一个简单的解决方案:对于你的数字集中的每一个v,求2^v的和。最后,循环i从1到n。如果与2^i相加的和为零,那么我就不见了。(或者用数字表示,如果和的底除以2^i为偶数。或
容易,对吧?o(n)时间,o(1)存储,支持任意k。
除了你在计算大量的数字,在一台真正的计算机上,每一个都需要O(N)空间。实际上,这个解和位向量是相同的。
所以你可以聪明地计算平方和和和立方体的和…达到v^k的总和,然后进行奇特的数学运算以提取结果。但这些数字也很大,这就引出了一个问题:我们在讨论什么抽象的操作模型?在O(1)空间中适合的大小是多少,以及需要多长时间才能求出所需大小的数字?
你可以从对称性的角度(群体,数学语言)来思考这个问题,以此来激励解决方案。不管数字的顺序如何,答案都应该是一样的。如果要使用
您可以通过注意
我们如何判断数组中缺少哪些项?想想多项式
所以我们可以建立多项式,并试着将其因子化,以确定哪些数字不在集合中,正如其他人提到的那样。
最后,如果我们关心的是大数溢出内存(n次对称多项式的阶数为
很好的问题。我会用一个设定差来计算QK。许多编程语言甚至支持它,比如Ruby:
1 | missing = (1..100).to_a - bag |
这可能不是最有效的解决方案,但如果我在这种情况下面临这样的任务(已知的界限,低的界限),我会在现实生活中使用它。如果数字集非常大,那么我会考虑一种更有效的算法,当然,但是在那之前,简单的解对我来说已经足够了。
另一种方法是使用残差图过滤。
假设我们有1到4的数字,3不见了。二进制表示如下:
1=001B,2=010B,3=011B,4=100B
我可以创建一个流程图,如下所示。
1 2 3 4 5 6 7 8 9 10 11 | 1 1 -------------> 1 | | 2 | 1 | 0 ---------> 1 ----------> 0 | | | | | 1 1 | | 0 ---------> 0 ----------> 0 | | | 1 | 1 | 1 ---------> 0 -------------> 1 |
请注意,流程图包含X个节点,而X是位数。最大边数为(2*x)-2。
所以对于32位整数,它需要O(32)空间或O(1)空间。
现在,如果从1,2,4开始移除每个数字的容量,那么剩下的就是一个残差图。
1 | 0 ----------> 1 ---------> 1 |
最后,我将运行如下循环:
1 2 3 4 | result = [] for x in range(1,n): exists_path_in_residual_graph(x) result.append(x) |
现在的结果是在
我将最后一次查看给定的列表,以标记结果是否丢失。
所以时间复杂度是O(n)。
最后,可以通过取节点
我会用不同的方法来回答这个问题,并询问面试官关于他试图解决的更大问题的更多细节。根据问题及其周围的需求,显而易见的基于集合的解决方案可能是正确的,而生成一个列表和一个接一个的IT事后方法可能不是。
例如,面试官可能会发送
这当然不是批处理预先生成的数字包的最快方法,因为整个过程运行
我相信我有一个
你有一个
1 2 3 4 5 6 | for (i = 0 ; i < k ; i++) { j = floor(log2(s)); missing[i] = j; s -= j; } |
现在,对于
我认为可以这样概括:
表示s,m为算术级数和乘法的初始值。
1 2 | S = 1 + 2 + 3 + 4 + ... n=(n+1)*n/2 M = 1 * 2 * 3 * 4 * .... * n |
我应该考虑一个计算这个的公式,但这不是重点。不管怎样,如果缺少一个数字,您已经提供了解决方案。但是,如果缺少两个数字,那么让我们用s1和m1来表示新的和和和总倍数,如下所示:
1 2 3 4 5 | S1 = S - (a + b)....................(1) Where a and b are the missing numbers. M1 = M - (a * b)....................(2) |
因为你知道s1,m1,m和s,上面的方程是可以解的,可以找到A和B,缺失的数字。
现在,对于丢失的三个数字:
1 2 3 4 5 | S2 = S - ( a + b + c)....................(1) Where a and b are the missing numbers. M2 = M - (a * b * c)....................(2) |
现在你的未知值是3,而你只有两个方程可以解。
这听起来可能很愚蠢,但是,在第一个问题中,你必须看到包里剩下的所有数字,才能用这个方程把它们相加,找到丢失的数字。
所以,既然你能看到所有的数字,只需寻找丢失的数字。当两个数字丢失时,情况也一样。我觉得很简单。当你看到袋子里剩下的数字时,用一个方程是没有意义的。
你可以试着用布卢姆过滤器。将包中的每个数字插入bloom,然后迭代整个1-k集,直到报告未找到的每个数字。这可能在所有的场景中都找不到答案,但可能是一个足够好的解决方案。
我们可以在O(logn)中进行q1和q2。
假设我们的
假设我们的处理器是一个
现在,如果我们在试管中间点燃激光,得到光度的输出。
- 等于一个预先计算的值(在没有缺少数字的情况下计算),则缺少的数字大于
n/2 。 - 如果我们的输出较小,那么至少有一个丢失的数字小于
n/2 。我们还可以检查亮度是否被1 或2 降低。如果被1 减少,那么一个丢失的数字小于n/2 ,另一个大于n/2 。如果被2 减少,那么两个数字都小于n/2 。
我们可以一次又一次地重复上述过程,缩小我们的问题领域。在每一步中,我们将域缩小一半。最后我们可以得到我们的结果。
值得一提的并行算法(因为它们很有趣)。
- 通过一些并行算法进行排序,例如,并行合并可以在
O(log^3 n) 时间内完成。然后通过O(log n) 时间的二进制搜索找到丢失的数字。 - 理论上,如果我们有
n 处理器,那么每个进程都可以检查其中一个输入,并设置一些标识数字的标志(在数组中很方便)。在下一步中,每个进程都可以检查每个标记,最后输出未标记的数字。整个过程需要O(1) 时间。它还需要额外的O(n) 空间/内存。
注意,上面提供的两个并行算法可能需要注释中提到的额外空间。
我不知道这是否有效,但我想建议这个解决方案。
现在运行一个循环,得到可能的对(p,q),这两个对都位于[1,100]中,求和为d。
当得到一对时,检查(3的结果)xor p=q如果是的话,我们就完了。
如果我错了,请纠正我;如果我错了,请评论时间复杂性
尝试查找1到50之间的数字的乘积:
让积,p1=1 x 2 x 3 x…………五十
当你把数字一个一个地拿出来时,把它们相乘,得到产品p2。但是这里缺少两个数字,因此p2 两个错误项的乘积,a x b=p1-p2。 你已经知道总数了,a+b=s1。 从上述两个方程出发,用二次方程求解A和B。A和B是你丢失的号码。
可能的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class MissingNumber { public static void main(String[] args) { // 0-20 int [] a = {1,4,3,6,7,9,8,11,10,12,15,18,14}; printMissingNumbers(a,20); } public static void printMissingNumbers(int [] a, int upperLimit){ int b [] = new int[upperLimit]; for(int i = 0; i < a.length; i++){ b[a[i]] = 1; } for(int k = 0; k < upperLimit; k++){ if(b[k] == 0) System.out.println(k); } } } |
一个非常简单的方法,大致在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import random K = 2 missingNums = range(0, 101) incompleteList = range(0, 101) #Remove K numbers for i in range(K): valueToRemove = random.choice(incompleteList) incompleteList.remove(valueToRemove) dummyVariable = [missingNums.remove(num) for num in p if num in missingNums] print missingNums |
您可以使用二进制搜索来查找丢失(或连续)数字的间隔。运行时间应该在(num interval)*log(avg interval length)*n左右。如果间隔不多,则很有用。
一种方法是计算质数101的模。
计算并存储整数1到100的乘积,将这个数减去模101。小外显子:结果是1。
计算并存储所有数字的和1到100,将结果模块101减少。小exo:结果是0。
现在假设这个包去掉了数字x和y。
计算袋模101中所有东西的积和。所以我会知道
a= x+y和B= x*y
模101。
现在很容易找到x和y模101(用101个元素在有限域上求解二次多边形)。
现在你知道x和y模101了。但既然你也知道x和y小于101,你就知道它们的真实值。
如果一个数字只出现一次,那么很容易用以下方式来判断:
创建一个给定数字大小的布尔数组
循环输入数字,并根据数字值将元素设置为true。例如,如果找到45,则设置
这将是一个O(N)操作。
然后循环通过
这是一个O(N)操作。空间复杂性为O(1)。
所以这个解决方案可以从给定的连续数集中找到任何缺失的数。
我们可以使用以下简单的代码来查找重复和缺少的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | int size = 8; int arr[] = {1, 2, 3, 5, 1, 3}; int result[] = new int[size]; for(int i =0; i < arr.length; i++) { if(result[arr[i]-1] == 1) { System.out.println("repeating:" + (arr[i])); } result[arr[i]-1]++; } for(int i =0; i < result.length; i++) { if(result[i] == 0) { System.out.println("missing:" + (i+1)); } } |
假设它是一个从1到n的数组,其元素是a1,a2,…,an:
1 2 | 1+N=N+1; 2+N-1=N+1; |
…所以这里的总和是独一无二的。我们可以从开始和结束扫描数组来添加这两个元素。如果总和是n+1,那么好的,否则它们会丢失。
1 2 3 4 5 | for (I <= N/2) { temp = a[I] + a[n-I]; if (temp != N+1) then Find the missing number or numbers } |
重复这个循环,你就能很容易地得到答案。
关键是使用索引来标记数字是否在范围内。这里我们知道我们有1到n。时间复杂度o(n)空间复杂性O(1)
后续问题:这可能会被修改,以查找差异d的ap中是否缺少元素。其他变化可能包括从包含-ve数的任意随机数组中查找第一个缺少的+ve数。然后,首先在0quick sort左右分区,然后在分区的右侧执行此过程part of thearray,do necessary modification.
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static void missing(int [] arr){ for(int i=0; i< arr.length; i++){ if(arr[i]!=-1 && arr[i]<=arr.length){ int idx=i; while(idx>=0 && idx<arr.length&& arr[idx]!=-1 ){ int temp =arr[idx]; // temp-1 because array index starts from 0, i.e a[0]=-1 is indicates that 1 is present in the array arr[temp-1]=-1; idx=temp-1; } } } } |
在此之后,我们需要迭代数组,并检查是否有一个[i]!=-1,则I+1是丢失的数字。我们必须小心当一个[I]>N.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // Size of numbers def n=100; // A list of numbers that is missing k numbers. def list; // A map def map = [:]; // Populate the map so that it contains all numbers. for(int index=0; index<n; index++) { map[index+1] = index+1; } // Get size of list that is missing k numbers. def size = list.size(); // Remove all numbers, that exists in list, from the map. for(int index=0; index<size; index++) { map.remove(list.get(index)); } // Content of map is missing numbers println("Missing numbers:" + map); |
免责声明:这个问题我已经读了好几天了,但我无法理解数学。
我试图用集合来解决它:
1 2 3 4 5 6 7 8 9 10 | arr=[1,2,4,5,7,8,10] # missing 3,6,9 NMissing=3 arr_origin = list(range(1,arr[-1]+1)) for i in range(NMissing): arr.append(arr[-1]) ##### assuming you do not delete the last one arr=set(arr) arr_origin=set(arr_origin) missing=arr_origin-arr # 3 6 9 |
这个问题很简单
1 2 3 4 5 6 7 8 9 | void findMissing(){ bool record[N] = {0}; for(int i = 0; i < N; i++){ record[bag[i]-1] = 1; } for(int i = 0; i < N; i++){ if(!record[i]) cout << i+1 << endl; } } |
o(n)时空复杂性
1 2 3 4 5 6 7 8 9 | //sort int missingNum[2];//missing 2 numbers- can be applied to more than 2 int j = 0; for(int i = 0; i < length - 1; i++){ if(arr[i+1] - arr[i] > 1 ) { missingNum[j] = arr[i] + 1; j++; } } |
我已经用Java 8和Java 8编写了代码。它使用一个公式:(n*(n+1))/2计算所有数字的和。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * * * @author pradeep * * Answer : SumOfAllNumbers-SumOfPresentNumbers=Missing Number; * * To GET SumOfAllNumbers : Get the highest number (N) by checking the * length. and use the formula (N*(N+1))/2 * * To GET SumOfPresentNumbers: iterate and add it * * */ public class FindMissingNumber { /** * Before Java 8 * * @param numbers * @return */ public static int missingNumber(List<Integer> numbers) { int sumOfPresentNumbers = 0; for (Integer integer : numbers) { sumOfPresentNumbers = sumOfPresentNumbers + integer; } int n = numbers.size(); int sumOfAllNumbers = (n * (n + 1)) / 2; return sumOfAllNumbers - sumOfPresentNumbers; } /** * Using Java 8 . mapToInt & sum using streams. * * @param numbers * @return */ public static int missingNumberJava8(List<Integer> numbers) { int sumOfPresentNumbers = numbers.stream().mapToInt(i -> i).sum(); int n = numbers.size(); int sumOfAllNumbers = (n * (n + 1)) / 2; return sumOfAllNumbers - sumOfPresentNumbers; } public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list = Arrays.asList(0, 1, 2, 4); System.out.println("Missing number is : " + missingNumber(list)); System.out.println("Missing number using Java 8 is :" + missingNumberJava8(list)); } }* |
对于不同的k值,方法会有所不同,因此就k而言,没有一般的答案。例如,对于k=1,可以利用自然数之和,但对于k=n/2,必须使用某种位集。同样,对于k=n-1,我们可以简单地将袋子中的唯一数字与其余数字进行比较。