輸入乙個陣列和乙個數字,在陣列中查詢兩個數,使得它們的和正好是輸入的那個數字。
要求時間複雜度是o(n)。如果有多對數字的和等於輸入的數字,輸出任意一對即可。
例如輸入陣列1、2、4、7、11、15和數字15。由於4+11=15,因此輸出4和11。
咱們試著一步一步解決這個問題(注意闡述中數列有序無序的區別):
直接窮舉,從陣列中任意選取兩個數,判定它們的和是否為輸入的那個數字。此舉複雜度為o(n^2)。很顯然,我們要尋找效率更高的解法
題目相當於,對每個a[i],查詢sum-a[i]是否也在原始序列中,每一次要查詢的時間都要花費為o(n),這樣下來,最終找到兩個數還是需要o(n^2)的複雜度。那如何提高查詢判斷的速度呢?
答案是二分查詢,可以將o(n)的查詢時間提高到o(log n),這樣對於n個a[i],都要花logn的時間去查詢相對應的sum-a[i]是否在原始序列中,總的時間複雜度已降為o(n log n),且空間複雜度為o(1)。 (如果有序,直接二分o(n log n),如果無序,先排序後二分,複雜度同樣為o(n log n + n log n)= o(n log n),空間複雜度總為o(1))。
可以繼續優化做到時間o(n)麼?
解法一根據前面的分析,a[i]在序列中,如果a[i]+a[k]=sum的話,那麼sum-a[i](a[k])也必然在序列中。 舉個例子,如下: 原始序列:
用輸入數字15減一下各個數,得到對應的序列為:
第乙個陣列以一指標i 從陣列最左端開始向右掃瞄,第二個陣列以一指標j 從陣列最右端開始向左掃瞄,如果第乙個陣列出現了和第二個陣列一樣的數,即a[i]=a[j],就找出這倆個數來了。 如上,i,j最終在第乙個,和第二個序列中找到了相同的數4和11,所以符合條件的兩個數,即為4+11=15。 怎麼樣,兩端同時查詢,時間複雜度瞬間縮短到了o(n),但卻同時需要o(n)的空間儲存第二個陣列。要注意的是,首先陣列要排序,其次如果a[i]>a[j] i++,如果a[i]j] j--
/** 根據前面的分析,a[i]在序列中,如果a[i]+a[k]=sum的話,那麼sum-a[i](a[k])也必然在序列中。 舉個例子,如下:
* 原始序列:1、 2、 4、 7、11、15
* 用輸入數字15減一下各個數,得到對應的序列為:
* 14、13、11、8、4、 0
* 第乙個陣列以一指標i 從陣列最左端開始向右掃瞄,第二個陣列以一指標j 從陣列最右端開始向左掃瞄,
* 如果第乙個陣列出現了和第二個陣列一樣的數,即a[i]=a[j],就找出這倆個數來了。
* 如上,i,j最終在第乙個,和第二個序列中找到了相同的數4和11,所以符合條件的兩個數,即為4+11=15。
* 兩端同時查詢,時間複雜度瞬間縮短到了o(n),但卻同時需要o(n)的空間儲存第二個陣列。
*/public
static
void solution1(int arr,int
n)
int start = 0;
int end = arr.length-1;
while(end>0&&startelse
if(arr[start]>temp[end])
else
}}
解法二當題目對時間複雜度要求比較嚴格時,我們可以考慮下用空間換時間,上述解法一即是此思想,此外,構造hash表也是典型的用空間換時間的處理辦法。
即給定乙個數字,根據hash對映查詢另乙個數字是否也在陣列中,只需用o(1)的時間,前提是經過o(n)時間的預處理,和用o(n)的空間構造hash表。
但能否做到在時間複雜度為o(n)的情況下,空間複雜度能進一步降低達到o(1)呢?
/** 構建hash表,儲存另乙個數字是否存在陣列內
*/public
static
void solution2(int arr,int
n)
for(int
i:arr)
}}
解法三如果陣列是無序的,先排序(n log n),然後用兩個指標i,j,各自指向陣列的首尾兩端,令i=0,j=n-1,然後i++,j--,逐次判斷a[i]+a[j]?=sum,
所以,陣列無序的時候,時間複雜度最終為o(n log n + n)=o(n log n)。
如果原陣列是有序的,則不需要事先的排序,直接用兩指標分別從頭和尾向中間掃瞄,o(n)搞定,且空間複雜度還是o(1)。
/** 如果陣列是無序的,先排序(n log n),然後用兩個指標i,j,各自指向陣列的首尾兩端,令i=0,j=n-1,然後i++,j--,逐次判斷a[i]+a[j]?=sum,
* 如果某一刻a[i]+a[j] > sum,則要想辦法讓sum的值減小,所以此刻i不動,j--;
* 如果某一刻a[i]+a[j] < sum,則要想辦法讓sum的值增大,所以此刻i++,j不動。
* 所以,陣列無序的時候,時間複雜度最終為o(n log n + n)=o(n log n)。
* 如果原陣列是有序的,則不需要事先的排序,直接用兩指標分別從頭和尾向中間掃瞄,o(n)搞定,且空間複雜度還是o(1)。
*/public
static
void solution3(int arr,int
n)
else
if(arr[start]+arr[end]>n)
else
}}
程式設計之法 面試和演算法心得(尋找和為定值的多個數)
輸入兩個整數n和sum,從數列1,2,3.n 中隨意取幾個數,使其和等於sum,要求將其中所有的可能組合列出來。注意到取n,和不取n個區別即可,考慮是否取第n個數的策略,可以轉化為乙個只和前n 1個數相關的問題。尋找和為定值的多個數 public class searchsomesurevalue ...
程式設計之法 面試和演算法心得 筆記
一 字串翻轉 三步反轉法,先將兩部分分別反轉,然後再整體反轉。abcdef defabc 1 cbadef 旋轉前一部分 2 cbafed 旋轉後一部分 3 defabc 整個旋轉 那麼將 i am a student.studnet.a am i 也類似。二 字串的包含 a abcxyzlmnop...
程式設計之法 面試和演算法心得(奇偶調序)
輸入乙個整數陣列,調整陣列中數字的順序,使得所有奇數字於陣列的前半部分,所有偶數字於陣列的後半部分。要求時間複雜度為o n 最容易想到的辦法是從頭掃瞄這個陣列,每碰到乙個偶數,拿出這個數字,並把位於這個數字後面的所有數字往前挪動一位。挪完之後在陣列的末尾有乙個空位,然後把該偶數放入這個空位。由於每碰...