暴力解法優化o(n
lgn)
o(nlgn)
o(nlgn
)技巧解法o(n
)o(n)
o(n)
輸入乙個遞增排序的陣列和乙個數字s,在陣列中查詢兩個數,使得他們的和正好是s,如果有多對數字的和等於s,輸出兩個數的乘積最小的。
任何一道程式設計題,拿到題目都是從題幹入手,思考題幹的資訊向我們透露出什麼含義。這道題的題幹中最有用的資訊就是就是遞增序列。顯然,如果不把這個遞增用上的話,就無異於是暴力查詢,即便是普通的查詢,需要找到滿足特定條件的兩個數最壞情況複雜度是o(n
2)
o(n^2)
o(n2
)的,在其他時候接觸到程式設計題目需要分析複雜度這一點也是要清楚的,過於暴力的方法(及不改變複雜度的優化)在此不贅述。
題幹中還提到如果有多組,輸出陣列中乘積最小的,可以證明,如果兩個數中較小者在多組數中最小,則這組數的乘積最小,以下有詳細證明,可(不)以(感)理(興)解(趣)的直接跳過。
乘積最小證明
已 知兩
個數之和
為s,不
妨設較小
者為a,
則另乙個
數為s−
a,求證
a越小則
兩個數乘
積f越小
已知兩個數之和為s,不妨設較小者為a,則另乙個數為s-a,求證a越小則兩個數乘積f越小
已知兩個數之
和為s,
不妨設較
小者為a
,則另一
個數為s
−a,求
證a越小
則兩個數
乘積f越小f=
a(s−
a)=−
a2+a
sf=a(s-a)=-a^2+as
f=a(s−
a)=−
a2+as乘積
f是關於
乘積f是關於
乘積f是關於
a的 二次
函式,因
為二次項
係數為−
1小於0
,則二次
函式開口
向下,對
稱軸為s
2的二次函式,因為二次項係數為-1小於0,則二次函式開口向下,對稱軸為\frac
的二次函式,
因為二次
項係數為
−1小於
0,則二
次函式開
口向下,
對稱軸為
2s又∵a
≤s−a
,∴a≤
s2,故
a位於二
次函式的
對稱軸左
邊,所以
f是關於
a的增函
數,即a
越小,則
f越
小又\because a≤s-a,\therefore a≤\frac,故a位於二次函式的對稱軸左邊,所以f是關於a的增函式,即a越小,則f越小
又∵a≤s−
a,∴a
≤2s
,故a位
於二次函
數的對稱
軸左邊,
所以f是
關於a的
增函式,
即a越小
,則f越
小。暴力解法之所以暴力是在於沒有將陣列遞增這個條件用上,以至於在查詢的過程中無法根據查詢的相關性來減少查詢次數,首先知道就是兩個數之和是s,所以兩個數是有相關性的,如果我們查詢到第乙個數a,那麼第二個數我們就知道是s−a
s-as−
a了,所以我們直接去判斷s−a
s-as−
a是否在陣列中即可。
我們要判斷s−a
s-as−
a是否在陣列中,此時需要用到陣列是遞增的特點,相當於有序陣列的查詢,顯然使用二分查詢可以降低複雜度,同時,在編寫**的時候,第乙個數字a我們不確定,只能從前往後(從小往大)遍歷,在二分查詢s−a
s-as−
a時,前面的陣列已經不需要遍歷了,所以查詢的時候,只需要從當前數字的後面到陣列末尾進行二分查詢即可。找到的第一組數字其中較小者肯定是最小的,所以該組數字乘積肯定是最小的。
核心**是乙個迴圈加二分查詢,**過於簡單,就不需要寫了。當然查詢可以比較一下第乙個數找到s/2
s/2s/
2就可以不繼續往下找了,這些都是小技巧。
最好的解法一定是把所有題幹資訊都用到了,而且往往還要用到一定的特殊技巧。至於特殊技巧的部分可以當成定式然後去記憶,遇到類似的題目能夠套用即可。
本題的特殊技巧是雙指標,具體的操作流程是這樣:判斷兩個指標如果不重合則一直迴圈,每次迴圈的過程是乙個指標在陣列中從前往後掃瞄,乙個從後往前,如果兩個指標指向的數字之和小於s,則前面的指標往後(大)移,如果兩數之和大於s,則後面的指標往前(小)移動,如果兩數之和等於s,則返回。
很多人對這個流程的原理可能不理解,下面我給出理由,同樣,理解的可以跳過。
雙指標原理**
最差情況下,兩邊指標重合,相當於把整個陣列都掃瞄了一遍,複雜度為o(n
)o(n)
o(n)
。如果存在兩個數和為s,則掃瞄整個陣列,一定可以找到其中的乙個,不失一般性,假設先找到的是兩個數中較小者,較小者指標為i,較大者指標為j。先找到較小者此時i指標確定,因為較大者的指標是從大向小掃瞄,則還沒找到,此時j指向的數字一定是比較大者大的,然後j不斷的減1直到找到該數字。為什麼j指向的數字一定是比較大者大呢,因為如果j指向的數字比較大者小的話,那麼與我們假設的先找到較小的數字矛盾。如果較大指標指向的數比較大數大,那麼較大指標會持續減小,直到等於較大數。同樣,先找到較大者也是一樣的道理。
再看乘積最小的條件,之前證明過較小的數越小,則乘積越小,因為兩數之和是相等的,同樣說明較大的數越大,則乘積越小,乘積越小的兩個數越是靠兩邊的,所以不管是較小的數還是較大的數總是比其他的一組數發現的更早。
綜上,如果存在正確答案一定會找到,且滿足乘積最小。
c++**實現
class
solution
vector<
int>res;
if(i < j)
return res;}}
;
最後再舉乙個用到這個方法思路的題目,大家可以自行思考。給定乙個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。注意:答案中不可以包含重複的三元組。 和為S的兩個數字
題目描述 輸入乙個遞增排序的陣列和乙個數字s,在陣列中查詢兩個數,是的他們的和正好是s,如果有多對數字的和等於s,輸出兩個數的乘積最小的。輸入 每個測試案例包括兩行 第一行包含乙個整數n和k,n表示陣列中的元素個數,k表示兩數之和。其中1 n 10 6,k為int 第二行包含n個整數,每個陣列均為i...
和為S的兩個數字
輸入乙個遞增排序的陣列和乙個數字s,在陣列中查詢兩個數,是的他們的和正好是s,如果有多對數字的和等於s,輸出兩個數的乘積最小的。輸出描述 對應每個測試案例,輸出兩個數,小的先輸出。class solution public vectorfindnumberswithsum vectorarray,i...
和為s的兩個數字
輸入乙個遞增排序的陣列和乙個數字s,在陣列中查詢兩個數,是的他們的和正好是s,如果有多對數字的和等於s,輸出兩個數的乘積最小的。輸出描述 對應每個測試案例,輸出兩個數,小的先輸出。include using namespace std bool twonumberwithsum int data,i...