問題:尋找陣列中的最小值和最大值。
一道很簡單的題目,一般有下面
4種解法:
1 遍歷兩次,每次分別找出最小值和最大值。
2 只遍歷一次,每次取出的元素先與已找到的最小值比較,再與已找到的最大值比較。
3 每次取兩個元素,將較小者與已找到的最小值比較,將較大者與已找到的最大值比較。
4 分治:將陣列劃分成兩半,分別找出兩邊的最小值、最大值,則最小值、最大值分別是兩邊最小值的較小者、兩邊最大值的較大者。這4
種演算法,哪種效率最高,哪種最低?
後兩種演算法只要進行
1.5*n
次比較,因而網上有不少解答都將它們列為最佳答案。但是,演算法
4用到了遞迴,而遞迴法函式呼叫的開銷是很大的,這就注定了該演算法的效率肯定不高。那麼,演算法3
就是最高效的嗎?還是用**來驗證吧。
後面的**,對每種演算法都實現了兩個函式(假設陣列長度為n):
演算法1:solve_1a
與solve_1b
,後者加入兩個臨時變數,編譯器可以將變數儲存在暫存器中,不用每次迴圈都要寫記憶體。比較次數為
2*n次。演算法2
:solve_2a
與solve_2b
,前者每次迴圈必比較
2次,後者最好情況下
(遞減陣列
)只要比較
1次,但最差情況下(遞增陣列)則要比較
2次,比較次數為:n到
2 * n
次。演算法3:
solve_3a
與solve_3b
,前者每次迴圈取頭尾兩元素(從兩頭往中間取),後者取相鄰兩元素。比較次數為
1.5 * n
次。演算法4:
solve_4a
與solve_4b
,後者返回乙個結構(只有兩個元素),編譯器優化可以通過兩個暫存器返回該結構,減少寫記憶體次數。(檢查
gcc產生的彙編,確認有進行該優化)。比較次數為
1.5 * n
次。下面是測試結果:(陣列長度為
6e7,每種演算法測
4次取平均值)
所用時間(毫秒,gcc 4.5)
所用時間(毫秒,vc 2010)
函式名 遞增
遞減亂序1
亂序2亂序3遞增
遞減 亂序1亂序2
亂序31a
兩次遍歷
175183187
179179
199203
176187
1751b
兩次遍歷(優化)
175179171
171172
183234
175187
1722a
一次遍歷
105105105
129105
105132
105
109
105
2b 一次遍歷(優化)
10590
105
109
105
109109
105
113
105
3a 取頭尾兩元素
85
85
246246
24686
82
261261
2613b
取相鄰兩元素
93
101238
242238
93
101258
257253
4a 分治法
316359867
863867
773777
1554
1558
1558
4b 分治法(優化)
273289824
824828
648656
1347
1340
1339
編譯引數:
tdm-gcc 4.5.2-dw2
:g++ -o3 -s -wextra –wallvc 2010:cl /ox /ehsc /nologo /w3
很明顯,「分治」法的效率遠低於其它3
種演算法。對前
3種演算法,將陣列長度增加到
1e8,並對十組隨機數組進行測試,得到結果:
從上面的表和圖可以看出,演算法
3在陣列有序時,執行效率很高(但與演算法
2相差不大),而在亂序時,甚至比兩次遍歷都慢。亂序時:演算法2
效率最高,演算法
1次之,演算法
3效率最低。演算法
2a和演算法
2b的效率差不多,有時演算法
2a的效率還略高。演算法
2a,每次迴圈都要比較
2次,演算法
2b每次迴圈要比較1到
2次,但由於前者的兩次比較是無關的,後者的比較是相關的(第一次比較的結果決定是否進行第二次比較),在現代
cpu「指令**」等技術前,演算法
2a在某些情況下能比較演算法
2b更高效。演算法3
的效率也不是絕對最差的,上面的隨機數組是通過隨機產生一些數得到,如果把它改為對陣列的元素進行
「隨機洗牌」,就得到下面的結果(所得的新資料與上面的資料和並,下圖中第
一、三列是上面的資料,第
二、四列是改用「隨機洗牌」後得到的新資料):
從圖可以看出,改用「隨機洗牌」法得到亂序陣列後,
vc的結果沒發生改變,
gcc除了函式
3a,結果也沒改變,但是「
3a一次取頭尾兩元素」
卻成為最高效的演算法,但其效率和
「一次遍歷取乙個元素
」 法,相差並不是太大。因而可以說,在單執行緒環境下,「
一次遍歷取乙個元素
」這個最容易想到的方法,反而是本題的最佳解法。
演算法上的最優,並不一定是實際上的最優,快排和堆排序就是乙個典型的例子,雖然快排最壞情況下的複雜度是
o(n^2)
,而堆排序始終是
o(n lgn)
,但實際運用中,乙個好的快排實現一般都比堆排序快很多。何況這
4種演算法的複雜度還都是
o(n)
。為了在最壞情況下節省
0.5 * n
次比較,進行的所謂優化,得到的結果很可能與所期望的恰恰相反。
測試**:
一道c 面試題
float a 1.0f cout int a endl cout int a endl int a 是什麼意思?cout boolalpha int a int a endl float b 0.0f cout int b endl cout int b endl cout boolalpha i...
一道面試題
一道面試題 射擊運動員10發打中90環有多少種可能,請編寫程式計算出來,並列印出結果,0環和10環均有效。打中90環就是沒打中10環,所以打中90環跟打中10環的可能性是一樣的。然後開始遞迴狂打槍,一到10就記錄 if params i 10 在迴圈的控制中已經排除了大於10的可能性 i 10 pa...
一道面試題
前些時候在找工作,就在準備結束此次找工作歷程的時候,去了一家公司面試,去了之後技術經理直接帶到一台電腦旁,給了一張紙條,上面是這樣的題目 用c或c 來實現 1 建立一棵樹,該樹的深度是隨機的,每個節點的位元組點數是隨機的。2 給每個節點分配一段隨機大小的記憶體空間,給每個節點賦乙個隨機數。3 遍歷這...