两个数之和等于第三个数 --改進版 。

来源:互联网 发布:蓝天豚硅藻泥 知乎 编辑:程序博客网 时间:2024/06/10 14:48

 

原題如下:给出一个有序数组,另外给出第三个数,问是否能在数组中找到两个数,这两个数之和等于第三个数

 

原題的解法在這裡就不重複了,算法還是很好理解的,但這種解法有個局限性,那就是對於亂序的數組就無用了,而且我還想打出所有的兩個數該怎麼辦呢?

比如:有一個數組

int arr[7] = {5,2,1,4,7,3,6};

我給定一個數為7,那麼應該輸出的是

5,2

1,6

4,3

注:2,5 = 5,2, 1,6 = 6,1 , 3,4 = 4,3

首先想到的就是用一個set,set裡面存放一個結構體,該結構體包含2個數據。代碼如下:

struct MyNum {int m_lhs;int m_rhs;MyNum(int v1, int v2) {m_lhs = v1;m_rhs = v2;}};


OK,接下來很順手的就會寫出以下的代碼。

set<MyNum> numset;

對與否,先來看看set的STL中的定義吧,以visual studio 2008為例。

// TEMPLATE CLASS settemplate<class _Kty, class _Pr = less<_Kty>, class _Alloc = allocator<_Kty> > class set  : public _Tree<_Tset_traits<_Kty, _Pr, _Alloc, false> > { // ordered red-black tree of key values, unique keyspublic: typedef set<_Kty, _Pr, _Alloc> _Myt; typedef _Tree<_Tset_traits<_Kty, _Pr, _Alloc, false> > _Mybase; typedef _Kty key_type; typedef _Pr key_compare; typedef typename _Mybase::value_compare value_compare; typedef typename _Mybase::allocator_type allocator_type; typedef typename _Mybase::size_type size_type; typedef typename _Mybase::difference_type difference_type; typedef typename _Mybase::pointer pointer; typedef typename _Mybase::const_pointer const_pointer; typedef typename _Mybase::reference reference; typedef typename _Mybase::const_reference const_reference; typedef typename _Mybase::iterator iterator; typedef typename _Mybase::const_iterator const_iterator; typedef typename _Mybase::reverse_iterator reverse_iterator; typedef typename _Mybase::const_reverse_iterator  const_reverse_iterator; typedef typename _Mybase::value_type value_type;


特別注意這一行,class _Pr = less<_Kty>, set中是通過less的函數來做比較的,用於判定函數是否存在,其原型為key_comp。key_comp接受2個物體,這2個物體必須為set中的對像,之後通過weak ordering和reflexive來判定物體是否已經存在。f(x,y ) && f(x,y) 同時為false的時候,則物體不存在,便插入。

struct MyNumComp {bool operator()(const MyNum &lhs, const MyNum &rhs) {return (lhs.m_lhs < rhs.m_lhs);}};

我實現了一個key_comp的方函數,但只是比較第一個數而不是兩個全部比較,想想為甚麼呢?

拿這個數組為例:int arr[7] = {5,2,1,4,7,3,6};

我給定一個數為7,那麼應該輸出的是

5,2

1,6

4,3

則,(5,2)-(2,5)必定成對出現,我在後面的算法中,插入的順序也是以小的數在前(2,5),我要的只是一對(2,5),比較的時候會自反比較:(2,5)-(5,2)。

那麼如果在key_comp中,兩個數都做less比較的話,則邏輯方面說不通。舉個例子:set中已存在(2,5)。

當我插入(3,5)的時候,就不行了。

 

下面說說主算法的思路,原來的算法雖可以找出所有的數對,但只是對有序數組有效,對於無序的數組,用hash_map來做吧。

想想看,如果兩個數的和等於一個數,那麼我們可以用K - 其中一個數,得到是另一個數,反之亦然。第二,把所有的數存入hash_map,在把那個K-數組中的數存入hash_map,如果有數對,那麼他們的value必定是2。對於4+4 =8, 8+8=16這種,value為4。 {1,2,4,4,8,6}

先生成hash_map,如果差小於零就沒有必要放了。:

for (int i = 0; i < ARRNUM; i++) {int val = sum - arr[i];if (val > 0)hmap[sum - arr[i]]++;hmap[arr[i]]++;}


 

然後遍歷hash_map,找出數對放入 set中。注意:set插入可是以key_comp為順序的喔,你也可以定義greater。

hash_map<int,int>::iterator itr;for (itr = hmap.begin(); itr != hmap.end(); itr++) {// such as 4+4 = 8, 6+6 = 12if ( itr->first != 0 &&  itr->first  == sum / 2 && itr->second == 4) {numset.insert(MyNum(itr->first,itr->first));}if (itr->second == 2 && itr->first != sum / 2) {int val1 = itr->first;int val2 = sum - itr->first;if (val1 > val2)numset.insert(MyNum(val2,val1));elsenumset.insert(MyNum(val1,val2));}}

第一個判斷處理4+4,8+8這種情況,第二個處理只有一個4,8但和為8,16這種情況。Whatever,set的插入總是以小的數在前。

下面看看完整的代碼:

#define ARRNUM 1000struct MyNum {int m_lhs;int m_rhs;MyNum(int v1, int v2) {m_lhs = v1;m_rhs = v2;}};struct MyNumComp {bool operator()(const MyNum &lhs, const MyNum &rhs) {return (lhs.m_lhs < rhs.m_lhs);}};int main() {int arr[ARRNUM]; for (int i = 0; i < 500; i++)     arr[i] = i+1; for (int i = 500; i < ARRNUM; i++)            arr[i] = ARRNUM - i + 500;  typedef set<MyNum,MyNumComp> Mysetnum;Mysetnum numset;hash_map<int,int> hmap;int sum = 516;DWORD dwStart = GetTickCount();for (int i = 0; i < ARRNUM; i++) {int val = sum - arr[i];if (val > 0)hmap[sum - arr[i]]++;hmap[arr[i]]++;}hash_map<int,int>::iterator itr;for (itr = hmap.begin(); itr != hmap.end(); itr++) {// such as 4+4 = 8, 6+6 = 12if ( itr->first != 0 &&  itr->first  == sum / 2 && itr->second == 4) {numset.insert(MyNum(itr->first,itr->first));}if (itr->second == 2 && itr->first != sum / 2) {int val1 = itr->first;int val2 = sum - itr->first;if (val1 > val2)numset.insert(MyNum(val2,val1));elsenumset.insert(MyNum(val1,val2));}}DWORD dwEnd = GetTickCount() - dwStart;cout << "The time is: " << dwEnd / 1000.0f << endl;cin.get();// printcout << "The sum is 516, so..." << endl;Mysetnum::iterator itrSet = numset.begin();while (itrSet !=  numset.end()) {cout << " (" << itrSet->m_lhs << ", " << itrSet->m_rhs <<") \t";itrSet++;}



時間複雜度為O(2n),空間複雜度O(n),輸出結果如下圖:

 

 


 

如有其他好的算法,可以補充,但本人不喜歡先把無序數組排序,排序也要時間的對吧。這道題一是熟悉基本的算法,二是熟悉STL中的set,怎麼加入自訂義類型,怎麼寫自己的key_comp仿函數。

 

 

原创粉丝点击