題目:輸入乙個已經按公升序排序過的陣列和乙個數字,
在陣列中查詢兩個數,使得它們的和正好是輸入的那個數字。
要求時間複雜度是o(n)。如果有多對數字的和等於輸入的數字,輸出任意一對即可。
例如輸入陣列1、2、4、7、11、15和數字15。由於4+11=15,因此輸出4和11。
解:如果只是最簡單的遍歷,時間複雜度為o(n)。這樣不符合題意,而且也沒有充分的利用題目的已知條件「排好序的陣列」。這題目老早之前做過一次。印象中使用了兩個指標p、q,乙個指標p指向陣列頭,乙個q指向末尾。指標p只往大的方向走;q只往小的方向走;比較這兩值之和與指定數的大小關係。若大了,則q往小的方向走;若小了,則p往大的方向走;直到找到乙個或是p>q(未找到)。
重新做一遍的時候,我又想到了另外的乙個方法(可能別人已經這樣用,這思想我殘餘的記憶中,逐成),當然沒有這個好。
先說第乙個方法。其實,更主要的我想證明其正確性。雖然以前做過,但沒有細究何以其然,秉著數學嚴謹的精神。花了點時間思考之:
已知:a[1]<=a[2]<=...<=a[n],和數m。求找出兩個數a[i]、a[j],使得a[i]+a[j]=m。
證:(1)不妨先假設存在這麼兩個數的情況。
為了更好證明,將陣列寫成a[min]<=...<=a[max],min表示當前有序集合的最小數的下標,max表示
當前有序集合的最大數的下標。
如果集合存在這兩個數,則以上的演算法是可以收斂到這兩個數。
(2)如果不存在這麼兩個數。這個容易證明類似以上的演算法也是適合的。試想不存在a[i]+a[j]=m (i!=j),這就可能進入到相等的情況。要不max減小,要不min增加。這樣集合總會縮小。直到集合為0個元素(為乙個元素就可說明找不到)
證畢。
1 #include 23using
namespace
std;45
intmain()6;
8int len = sizeof(num)/sizeof(int);9
intn;
10int *p,*q;
1112 cin>>n;
13 p =num;
14 q = &num[len-1
];15
16while(p=n)
1720
while(p2126
else
if (*p + *q >n)
2730
else
3135}36
if (!(p3740
41return0;
42 }
第二方法:先計算出每乙個數還需要多少才能到達m
如題目中的例子:
陣列:1、 2、4、7、11、15
補集:14、13、11、8、4、0
補集說明若原陣列存在這個則可以找到這兩個數。逆補集中數:0說明15+0=15,由於陣列是有序,順序遍歷原陣列1,1>0說明不可能存在0這數;這時,找補集中4,接著上次遍歷原陣列1,1<4。接著遍歷原陣列2,2<4。下乙個4=4找到,即為11和4。整個順序遍歷是不回溯只走一遍,逆向遍歷補集也是只遍歷一次(證明略)。所以整個演算法複雜度為o(n),空間複雜度為o(n)。其實根據這個思想也可以將空間複雜度降到o(1),留個習題(太簡單了o(∩_∩)o,所以**就不給出了)。
找出有序陣列中和等於指定數的兩個數
題目 已知按序排列的整數陣列,輸入任意數number,當陣列中某兩數之和等於number時,列印出兩個數。要求 複雜度為o n 解法 陣列已是有序排列,且兩個加數一定滿足條件 較小加數 number 2 較大加數 那麼只需要找出該陣列的較小加數和較大加數分界index,以該分界為起點分別往左右兩邊逐...
兩個已排序陣列,找出相同的部分
存在的兩個陣列,已經排好順序,求其相同的部分,有以下幾種求法 1 窮舉法 最原始的方法,時間複雜度為o m n 如下 int a new int 示列陣列 int b new int for int i 0 i a.length i 2 binary search 通過乙個陣列的for迴圈,不斷與另...
1 無序陣列中找出兩個數使其和等於給定值
碰到這種類似題目可以根據條件不同寫出不同時間複雜度的 1.最暴力的遍歷,時間複雜度為o n 2 2.一般情況下先排序,再從兩邊向中間搜尋結果,時間複雜度為o nlogn n int i 0,j numbers.size 1 while i j 3.如果陣列所有數都是唯一的,可以用unordered ...