题目描述:从1-100中删去任意一个数字,而后将剩下的99个数字打乱,获得无序序列。如今须要用一种方法找到这个数字。c++
这是最简单的方法,将全部的数字求和,而后用
减去
就得到答案。函数
复杂度分析: 若是不算上保存99个数的空间,那么该方法只须要
的空间记录求和,以及
的时间遍历序列。ui
另一个容易想到的的方法,因为数字连续而且数据规模小,因此只须要构建100个“桶”。从头遍历给定的99个数字,将其放入数值对应的“桶”中。最后从头开始找这100个“桶”,哪一个桶空就说明缺乏了哪一个数字。spa
复杂度分析: 该方法须要用到
的额外空间,以及
的时间复杂度。code
对于任意整数n,有,根据这个性质,就能够预先将1-100全部的整数异或,获得“异或和”X,而后再将X和序列中的每个元素异或,最后获得的答案就是被抠掉的数字。排序
复杂度分析: 和作加法相似,时间复杂度为
,空间复杂度为
,不过使用异或方法不须要考虑溢出的状况。递归
假设被抠掉的数字是n,那么n必定属于1~50或51~100两个区间。若是n属于前一个区间,那么n也必定属于1~25或26~50两个区间。这个过程能够一直划分,直到找到这个数字。string
如今须要肯定这个数字是多少,那么仍是相似的思想。若是被抠掉的数字属于1~50,那么小于等于50的数字只有49个。如今数一数有多少个落入1~50区间,不妨假设只有49,那么又能够继续划分为计算1~25中有多少个数字的问题。这个过程能够一直划分,直到找到这个数字。it
上面这个过程耗时最大的步骤就是统计区间的元素个数。一遍一遍扫描确定是不够好的,能够参考快速排序的元素划分方式。io
每次搜索的区间都会缩小一半,所以只须要通过步就能够找到缺乏的元素。具体计算以下。用
表示父查询任务的时间复杂度,则有
,当
时,
;
的时间复杂度为
。
复杂度分析: 已经证实时间复杂度为
。空间复杂度
。
题目描述:从1-100中删去任意两个数字,而后将剩下的98个数字打乱,获得无序序列。如今须要用一种方法找到这两个数字。
这个方法哪怕n为其余值也是可使用的,十分简单再也不赘述。
此时简单的差值求解不奏效了,只能求解获得两个数字的和。若是须要解出这两个数字,那就须要用一个等式构成方程组。
可否使用异或的方式获取一个等式构成方程组呢?即解方程组
要判断是否可行,能够列一下真值表:
![]() |
![]() |
+ | ![]() |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
1 | 1 | 0 | 0 |
暂时先不考虑进位对结果的影响,也就是最低位的状况。显然最低位已经没法根据加法值和异或值肯定结果了(第二、3行),因此这个方法是不行的。
而若是使用方程组
虽然理论可行,可是计算在程序上会致使溢出。
若是改用对数方程是否可行?
虽然可能存在精度的问题,可是仍是决定实验一下。代码以下,x和y的值能够任意更换。
#include <bits/stdc++.h>
using namespace std;
int main() {
int x = 1, y = 2;
int sum1 = 5050, sum2 = 0.0;
double logsum1 = 0, logsum2 = 0.0;
for (int i = 1; i <= 100; ++i) {
logsum1 += log(i);
}
for (int i = 1; i <= 100; ++i) {
if (i == x || i == y) continue;
logsum2 += log(i);
sum2 += i;
}
double x_plus_y = sum1 - sum2;
double x_mult_y = exp(logsum1 - logsum2);
cout << "x + y = " << x_plus_y << endl;
cout << "x * y = " << x_mult_y << endl;
return 0;
}
复制代码
通过测验,对于上面的代码,使用double型变量能够提供足够的精度,一方面是double有52bits的有效数据位,足够精细,另外一方面是因为100之内数的对数值并不会相差很大,因此浮点数相加并不会丢失不少精度。一样的代码若是使用float存储浮点结果,就会出现精度丢失的问题。
分治法仍是能够用来解决这个问题。仍是用以前的假设:查询区间为,中值为k,左查询区间为
,右查询区间为
。
不管缺失的两个数字取自哪一个子查询区间,区间对应元素集合就会缺乏数字,那么就将父查询任务转化为有数字缺乏的区间的子查询任务。最差状况下,缺失的两个数字分别属于两个子查询区间。
借鉴“找一个数字”中分治法的符号表示,有,当
时,
;
的时间复杂度为
。
求解递推式:
复杂度分析:
,所以找两个数字的时间复杂度为
;空间复杂度仍是
虽然加法方程和异或方程联立没法解得结果,可是异或结果仍是说明了x和y数位上的特色。
从异或结果中看看从1-8中抠掉2个数字,求抠掉的两个数字。
十进制 | 二进制 |
---|---|
1 | 0001 |
2 | 0010 |
3(抠掉) | 0011 |
4 | 0100 |
5 | 0101 |
6(抠掉) | 0110 |
7 | 0111 |
8 | 1000 |
令x=3,y=6,则异或结果为0101,代表第1位(从低到高)和第3位不同。若是按照第1位是否1将原来的数字分红两组,实际上就是将找两个数的问题转化为找1个数的问题。
复杂度分析 和找1数字的时候同样,时间复杂度为
,空间复杂度为
若是如今问题变成了:从1-100中删去任意n个数字,而后将剩下的100-n个数字打乱,获得无序序列。如今须要用一种方法找到这n个数字。
依然可用。
可使用,可是当n较大的时候,实际上就退化成对数字排序的问题。花在递归函数调用上的时间影响较大。
策略是不断使用比特的特征将找n个数的问题转化为找n-1个数的问题。只不太重复循环的时间复杂度就比较高。