時間:2014.07.17
地點:基地
-------------------------------------------------------------------------------------
一、問題描述
給定一個數組,要求快速查找出其中的兩個值,他們的和爲一個給定的值。
比如給定數組:1 4 5 6 8 9,和給定值9,我們能找出4+5=9爲所要的數值
-------------------------------------------------------------------------------------
二、各種解法
2.1窮舉法
對給定集合從中取出任意值求和,看是否等於給定和值,顯然由組合公式知:其複雜度爲N(N-1)/2,即O(N^2)。我們要求儘可能降低算法時間複雜度和算法空間複雜度,這個算法比較原始。
2.2先排序後查找
假定給定數字和爲sum,對數組中的每個數字arr[i],理想上是要有另外一個數字sum-arr[i]存在數組中即爲查找成功,於是問題轉換成了一個查找問題,而我們知道查找算法中最快的莫過於二分查找,時間複雜度爲O(logN),但二分查找的前提是有序序列,於是我們要先對數組進行排序,我們也知道快排、堆排、歸排等都是時間複雜度爲O(NlogN)的排序算法,於是針對數組中的每個數arr[i],都需要花費O(logN)的時間去查找對應的sum-arr[i]是否存在,於是總的時間複雜度爲O(NlogN),在這裏我們排序也需耗時O(NlogN),但好在排序只需一次,於是時間複雜度依舊還是O(NlogN)。
使用二分查找時要注意:
1.將數組劃分爲兩個子部分時,中間分界爲:
middle=first+size/2
2.左邊子部分可能略多,從first開始,大小爲size/2
3.右邊子部分可能略少,從middle+1開始,大小爲(size-1)/2
2.3使用哈希查找
在2.2節,我們已經將問題轉換成了查找問題,事實上,對於查找問題,若不惜代價追求速度的話,哈希查找最快,每次查找時間複雜度爲O(1),於是在這裏我們只需O(N)即可完成任務。此種方法需要增加額外的存儲空間。
2.4雙端掃描
首先若數組無序,排序還是要排,時間複雜度爲O(NlogN),但若數組有序,則可直接O(N)完成任務,在這裏我們假設數組是有序的,現在才用雙指針雙端掃描法對整個數組掃描的策略:
首先,設定兩個指針begin 和 end,分別指向數組首和尾,
然後,逐次判斷arr[*begin]+arr[*end]和給定的sum進行大小比較
i.若大於,說明兩數和過大,執行--end,縮減兩數和
ii.若小於,說明兩數和過小,執行++begin,放大兩數和
iii.若等於,正是我們所要,返回這個數值對
雙端掃描的代碼如下:
pair FindSumPair(int* arr, int n, int sum)
{
//Sort(arr,arr+n); //若數組無序,則先對數組進行排序
int* begin = arr;
int* end = arr + n - 1;
int tem_sum = 0;
while (begin < end)
{
tem_sum = (*begin) + (*end);
if (tem_sum>sum)
--end;
else if (tem_sum < sum)
++begin;
else
return pair(*begin, *end);
}
return pair(-1, -1);
}
-------------------------------------------------------------------------------------
三、擴展
2010年中興面試題-編程求解:輸入兩個整數 n 和 m,從數列1,2,3.......n 中 隨意取幾個數,
使其和等於 m, 要求將其中所有的可能組合列出來。
引用算法之道JULY博客中的代碼如下:
#include<list>
#include<iostream>
using namespace std;
list<int>list1;
void find_factor(int sum, int n)
{
// 遞歸出口
if(n <= 0 || sum <= 0)
return;
// 輸出找到的結果
if(sum == n)
{
// 反轉list
list1.reverse();
for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)
cout << *iter << " + ";
cout << n << endl;
list1.reverse();
}
list1.push_front(n); //典型的01揹包問題
find_factor(sum-n, n-1); //放n,n-1個數填滿sum-n
list1.pop_front();
find_factor(sum, n-1); //不放n,n-1個數填滿sum
}
int main()
{
int sum, n;
cout << "請輸入你要等於多少的數值sum:" << endl;
cin >> sum;
cout << "請輸入你要從1.....n數列中取值的n:" << endl;
cin >> n;
cout << "所有可能的序列,如下:" << endl;
find_factor(sum,n);
return 0;
}