一 題目描述:
有乙個整數陣列,請求出兩兩之差絕對值最小的值,只要求出最小值即可,不要求求出是哪兩個數。
二 常規思路:
求解此題的尋常思路是什麼?觀察題目我注意到後面強調不要求求出兩個數,那麼最最簡單的o(n^2)的
演算法顯然做了很多無用功。嗯,好,既然這個辦法不行想想其他的。對於陣列也就是序列之類的題,有一種很
常用的思路那就是預處理。這道題目貌似是可以的。
首先,對陣列進行排序,這個可以在o(n*logn)時間之類解決,然後,有了這個預處理,就會想到,絕對值
之差最小值肯定只能發生在預處理的陣列之後的相鄰的元素上,這個是很顯然的事實。那麼我們便可以迴圈一
遍陣列,記下兩兩之間絕對值的最小值,那麼所求得到值便是解答,總的時間複雜度是o(n*logn)。仔細想想
這種方法,很明顯,排序減小了我們所需要搜尋的解空間,從而達到了減小時間複雜度的目的。不過這個解
法仍然不能讓人滿意,因為我們還是浪費時間求出了最終的兩個元素,而題目不要求,所以,這肯定不是
最優解。
三 轉化的思想
再仔細觀察題目,我們可以猜到,最優解應該是只求出最小值而不求出具體的元素的,那麼該怎麼做
呢?我們可能能想到用輔助陣列,但是卻很難想到怎麼做這個輔助。其實這道題我一直在思考如何通過常
規的思維去想到這個最優解,不過我當時沒有想出來,而這才是我寫這篇部落格的原因,即促使我了解
並對這種思路印象深刻,不過這可能只適用於解這題或者類似能讓我聯想到這種方法的題,這背後更
一般的思維(可以叫做轉化,但是還可以更具體些)我還沒有想到,希望想到的同學聯絡我!。
好了,本題要做的輔助陣列是這樣乙個陣列,設它為bn.原來題目中給定的陣列是an,則bn等於:
b1 = a1 - a2;
b2 = a2 - a3;
b3 = a3 - a4;
......
bn-1 = an-1 - an.
注意,bn的長度是n-1,正好比an要小乙個。聰明的同學看到這個輔助陣列,立馬就能猜到原因了,
因為這樣做的話,我們能夠把這道看似無從下手求出最優解的問題轉化為求bn的絕對值最小的最長連
續子串行和,因為bn的連續子串行和便是an任意兩數之差(注意,由於題目要求的是絕對值最小,
所以求出a1-a2等效於得出a2-a1),例如:
a2 - a5 = b2 + b3 + b4 = a2 - a3 + a3 - a4 + a4 - a5 = a2 - a5
實際上,任何ai - aj(ik=j-1)(k)
這樣的話,我們就成功把問題轉化為了連續子串行問題,不過和我們以前做的最大或最小連
續子串行還不完全相同,此處是絕對值最小。那麼怎麼樣的值可能是絕對值最小呢?正數最小或
者負數最大,也就是說在數軸上離0更近的數其絕對值更小,基於此我們可以得到如下的方法。
和原來求最大連續子串行和一樣,要用數學歸納法思考,我們直接看歸納基礎,
歸納基礎: 假設已知b1..bk的絕對值最小連續的連續子串行和是min(bk)
我們利用這個求解b(k+1),加入b(k+1)後有可能比min(bk)小的只可能是以b(k+1)結尾的絕對
值最小的連續子串行和,如果把這個和min(bk)比較就可以知道是否需要更新min(bk)。所以,我
們加強這個歸納基礎。
更強的歸納基礎: 假設已知b1..bk的絕對值最小連續的連續子串行和min(bk),以及以
bk結尾的絕對值最小連續子串行和suffix(bk)
有了這個歸納,我們可以去想如何維護這個suffix(bk),目標是使的suffix(b(k+1))仍然是以
b(k+1)結尾的最小連續子串行和。如果按照求最小和的思路,那便是只要suffix(bk)是正數
便置它為0,因為如果它是正數,那麼在後續求suffix(b(k+1))時就肯定比用0要更大,因為
正數會使得整個值變大,而0不會。同樣的道理,我們只要使得求suffix的時候比直接置0
更小即可,否則我們可以直接把suffix(b(k+1))置0以獲得更小值。由於我們求的是絕對
值最小,直接按最小值的思路是不行的,因為可能某個suffix是暫時求得乙個很小的負數,
下次加上某個正數會使得它成為很小的正數,所以不能以正數負數作定論而要以與0的
距離。所以我們應該採取比較符號的方法,如果當前suffix和下乙個數的符號相反,那麼
可以繼續相加以求得下乙個suffix,因為我們可以獲得絕對值更小的suffix;如果是同號,
無論正負一定會比把當前suffix置0更糟糕,因為這將使得下次的suffix在數軸上離0更遠。
所以我們維護suffix的公式如下:
suffix(b(k+1)) = suffix(b(k)) + b(k+1), if (suffix(b(k))*b(k+1)) < 0
suffix(b(k+1)) = 0, if (suffix(b(k))*b(k+1)) ) > 0
這樣我們一直歸納下去,便可以求得最終的min(bn),即可求得解。整個的時間複雜
度是o(n),空間複雜度是o(n)。
四 程式
程式如下:
[cpp]view plain
copy
#include"stdafx.h"
#include
#include
usingnamespacestd;
intgetminabsolutesubsequence(intb,intnlen)
} returnabs(nglobal);
} int
getminabsolutediff(inta,
intnlen)
returngetminabsolutesubsequence(b,nlen-1);
} int_tmain(intargc, _tchar*argv)
; int
nlen=5;
coutreturn0;
}
五 總結
整個思路過程便是這樣,總的來說,這類題目還是很有思考價值的,至少讓我們體會
到了各種美,也能深刻領會轉化的意義。
求陣列兩兩之差絕對值最小的值
一 題目描述 有乙個整數陣列,請求出兩兩之差絕對值最小的值,只要求出最小值即可,不要求求出是哪兩個數。二 常規思路 求解此題的尋常思路是什麼?觀察題目我注意到後面強調不要求求出兩個數,那麼最最簡單的o n 2 的演算法顯然做了很多無用功。嗯,好,既然這個辦法不行想想其他的。對於陣列也就是序列之類的題...
整數陣列中兩兩之差絕對值最小的值
題目1 有乙個整數陣列,請求出兩兩之差絕對值最小的值,記住,只要得出最小值即可,不需要求出是哪兩個數。題目2 請求出最小連續子串行絕對值和,也就是求連續子串行之和的絕對值最小值 針對問題1 方法 1 暴力的方式。遍歷所有的兩個數的差,記錄最小值。演算法的複雜度o n2 方法 2 兩個數要想差的絕對值...
有乙個整數陣列,請求出兩兩之差絕對值最小的值
1.可以快排,然後遍歷一遍排完序後的陣列。時間複雜度為o nlogn n 2.先遍歷一遍陣列a 找出最大值max和最小值min。然後以建立乙個 max min 1 大小的陣列b 再次遍歷原始陣列,b a i min 試圖建立一種對映關係,降低時間複雜度。如下 include include usin...