題目描述:從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個數的問題。只不太重複循環的時間複雜度就比較高。