一道C 面試題的誤區

2021-05-25 05:41:13 字數 3974 閱讀 5417

問題:尋找陣列中的最小值和最大值。

一道很簡單的題目,一般有下面

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 遍歷這...