准备面试过程中看到网上流传的一道亚马逊的面试题。认为很有意思,自己写了一遍code,如今和大家分享一下,题目例如以下:
数组有N-2个数字,数字的范围为1 ... N,没有反复的元素,要求打印缺少的2个数字,不能够用额外的空间。
方法1. 经过分析题目能够看出,数组中缺了两个数字,直观上能够通过把数字排序,然后从头到尾进行扫描。找到缺失的两个数字。由于有排序,而排序的时间复杂度最快为O(NLogN)。有没有更快的算法呢?
方法2:数组中的数字事实上很有规律,有没有可能通过数学公式计算而非扫描来得到结果呢?假如我们把数组中全部的数字加起来得到一个总数 M0, 而1+2+3+4...+N = N(N+1)/2, 用后一个数字减去前一个数字的结果应该刚好是缺失的两个数字的加和。假定缺失的两个数字分别为x和y,则x+y= N(N+1)/2 - M0.
我们得到了一个含有两个变量的方程式。有没有可能再得到一个关于这两个变量的另外一个方程式。从而通过联合两个方程式解出x和y?
如果我们尝试把数组中每个数组计算平方以后再加和在一起得到一个数字M1, 而1^2 + 2^2 + 3^2 + .... N^2 = N(N+1)(2N+1)/6, 把后一个数减去前一个便能够得到 x^2 + y^2 = N(N+1)(2N+1)/6 - M1
这样我们便得到了两个关于x和y方程,通过联合这两个方程便能够解出x和y的值。
方法3:假设N非常大,导致N^2 轻易就超出整数的表达范围,有没有可能得到其它其它关于x和y的一阶的方程? 在方法2中事实上我们非常easy便得到了x+y的值,假如我们 计算(x+y) / 2便能够得到一个介于x和y的中间的值,也就是说x一定小于等(x+y)/2, 而y一定大于 (x+y)/2
假如我们在扫描数组进行加和过程中把加和的算法改为例如以下:
假设 num[i] <= (x+y)/2
total0 = total0 - num[i]
否则
total0 = total0 + num[i]
total0 的值是: N + N-1 + - 2 - .. - 1, 当中不包括x和y
相同的原理。在计算1+2+3+4... + N的过程中把算法改成:
假设i<= (x+y)/2
total1 = total1 - i
否则
total1 = total1 + itotal1 的值是: N + N-1 + y +.. - x - .. - 1
而total1 - total0 = y - x
这样我们便得到另外一个x和y的一阶方程式,联合方程式便可解出x和y。能够看出算法的时间复杂度为O(N), 而在方法2中,由于有计算数的平方即乘法。所以方法2的效率肯定低于方法3
下面为代码
void prtMissingNum(int * num, int len)
{
int i = 0;
int totalPlus = 0;
int totalMinus = 0;
int index = 0;
for(i = 0; i < len; i++)
{
totalPlus = totalPlus + (i+1) - num[i];
}
totalPlus = totalPlus + len + 1 + len + 2;
index = totalPlus / 2;
for(i = 0; i < len; i++)
{
if(i+1 <= index)
{
totalMinus = totalMinus - i -1;
}
else
{
totalMinus = totalMinus + i +1;
}
if(num[i] <= index)
{
totalMinus = totalMinus + num[i] ;
}
else
{
totalMinus = totalMinus - num[i] ;
}
}
if(len + 1 <= index)
{
totalMinus = totalMinus - len - 1;
}
else
{
totalMinus = totalMinus + len + 1;
}
totalMinus = totalMinus + len + 2;
// x + y = totalPlus
// x - y = totalMinus
int x = (totalPlus + totalMinus)/ 2;
int y = (totalPlus - totalMinus)/ 2;
printf("num1:%d, num2:%d
", x, y);
}