1. 程式人生 > >演算法練習:兩數之和

演算法練習:兩數之和

題目:給定一個整型陣列,是否能找出兩個數使其和為指定的某個值?注:整型陣列中不存在相同的數。

一、解題方法

1、暴力破解法(時間複雜度O(n^2) )

這是最容易想到的一種方法,即使用兩層迴圈,從數組裡取出一個數,然後在此數之後部分找出另外一個數,計算兩數之和,判斷是否等於指定值。如下:

//直觀的辦法,使用兩個迴圈
bool IsExistSumOfTwoNum( int nArray[], int nCount, int nSum )
{
	bool bRet = false;
	for ( int i = 0; i < nCount - 1; ++i )
	{
		for ( int j = i + 1; j < nCount; ++j )
		{
			if ( ( nArray[i] + nArray[j] ) == nSum )
			{
				bRet = true;
				break;
			}
		}
	}
	
	return bRet;	
}

此種方法的的時間複雜度為O(n^2),那麼能否降低此複雜度呢?答案是可以的,請看第二種方法。

2、排序加首尾指標(時間複雜度O(nlogn) 

先對陣列進行從小到大的排序,然後設定首尾指標,從首尾兩端開始移動,一次移動一端的指標,直至兩指標相遇或者兩指標指向的數的和為指定的值。

假設兩指標為ij,其中i < j,如果a[i]a[j]之和大於指定值,那麼要找的兩個數一定在j的左側,如果a[i]a[j]之和小於指定值,那麼要找的兩個數一定在i的右側。可用反證法證明此結論的正確性,證明略。

bool IsExistSumOfTwoNum( int nArray[], int nCount, int nSum )
{
	//從小到大排序
	std::sort( nArray, nArray+nCount, std::less<int>() );

	//首尾指示
	int i = 0; 
	int j = nCount - 1;

	bool bRet = false;
	while( i < j )
	{
		if ( ( nArray[i] + nArray[j] ) == nSum )
		{
			bRet = true;
			break;
		}
		else if ( ( nArray[i] + nArray[j] ) > nSum  )
		{
			--j;
		}
		else
		{
			++i;
		}
	}

	return bRet;	
}

此種演算法中,一開始有一個排序,最好的排序的時間複雜度可為O(nlogn),比如堆排序、歸併排序、快速排序。While迴圈至多掃描一遍陣列,所以其時間表複雜度為O(n)。由此可得,最終的時間複雜度為O(nlogn)。那麼能否再降低時間複雜度呢?答案還是可以的,但是這時需要一個額外的儲存空間,請看第三種方法。

3、利用雜湊表(時間複雜度O(n) 

將複雜度為O(nlogn)降低至O(n),首先想到的是雜湊表,因為雜湊表的查詢時間複雜度為O(n)

掃描一遍陣列,將其陣列各個值儲存至雜湊表中,比如鍵值:<陣列值--索引>。然後再次開始從頭掃描陣列,檢查指定值與當前值的差值是否在雜湊表中(特殊情況:如果遇到差值為當前值時,那麼不應返回,因為陣列中的值是不相同的)。

程式程式碼實現說明:程式中使用c++標準庫中的set代替雜湊表,因為標準庫中還未收錄雜湊表相關部分。此處若需要兩數的索引資訊,可以考慮使用map。因為setmap內部使用的是紅黑樹資料結構,查詢效率高,具體的複雜度,我還沒好好研究,呵呵。希望此處用set代替雜湊表,不要引起誤會,姑且將它理解為查詢複雜度為常數時間的東西吧。

//使用額外的儲存空間
bool IsExistSumOfTwoNum( int nArray[], int nCount, int nSum )
{
	//打描一遍陣列,將值存放於雜湊表中
	//(注:此處使用set代替下雜湊表,它內部實現說是紅黑樹,還沒仔細研究過,呵)
	std::set<int> siSetTemp;
	int i = 0;
	bool bRet = false;
	for ( i = 0; i < nCount; ++i )
	{
		siSetTemp.insert( nArray[i] );
	}

	for ( i = 0; i < nCount; ++i )
	{
		std::set<int>::iterator it = siSetTemp.find( nSum - nArray[i] );
		if ( it != siSetTemp.end() && (nSum != 2 * nArray[i] ) )
		{
			bRet = true;
			break;
		}
	}

	return bRet;
 		
}

此種演算法中,雜湊表的查詢是常數時間,所以時間複雜度為On),空間複雜度為

On),即陣列大小On)。

二、擴充套件問題

如果要返回其中所有的可能或者數組裡面存在相同的數,那麼應當怎麼解決呢?相信有了以上的知識,這個解決起來也不難。一個是開闢一個儲存結果的陣列,搜尋完整個陣列空間;另外一個無非就是讓雜湊表中多存放點資料。

系列文章說明:
1.本系列文章[演算法練習],僅僅是本人學習過程的一個記錄以及自我激勵,沒有什麼說教的意思。如果能給讀者帶來些許知識及感悟,那是我的榮幸。
2.本系列文章是本人學習陳東鋒老師《進軍矽谷,程式設計師面試揭祕》一書而寫的一些心得體會,文章大多數觀點均來自此書,特此說明!
3.文章之中,難免有諸多的錯誤與不足,歡迎讀者批評指正,謝謝.

作者:山丘兒
轉載請標明出處,謝謝。原文地址:http://blog.csdn.net/s634772208/article/details/46388789