快速尋找滿足條件的兩個數

時間: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;  
}  



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章