面試 6:拓展性更好的代碼,更容易拿到 Offer


 

今天給大家帶來的是 《劍指 Offer》習題:調整數組順序使奇數位於偶數前面,純 Java 實現希望大家多加思考。

面試題:輸入一個整型數組,實現一個函數來調整該數組中的數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的后半部分,希望時間復雜度盡量小。

看到這道題,想必大多數人都是能一下就想到從頭到尾掃描一遍數組,然后遇到奇數就移動到最前面,遇到偶數就移動到最后面的思路,於是便有了下面的代碼。

注:《劍指 Offer》上面的 「遇到奇數移動到最前面,遇到偶數也移動到最后面」其實只需要做其中一種即可。

public class Test14 { private static int[] reOrderArray(int[] arr) { for (int i = 0; i < arr.length; i++) { // 遇到奇數就放到最前面 if (Math.abs(arr[i]) % 2 == 1) { int temp = arr[i]; // 先把 i 前面的都向后移動一個位置 for (int j = i; j > 0; j--) { arr[j] = arr[j - 1]; } arr[0] = temp; } } return arr; } public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arr = reOrderArray(arr); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); int[] arr1 = {2, 4, 6, 8, 1, 3, 5, 7, 9}; arr1 = reOrderArray(arr1); for (int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + " "); } System.out.println(); int[] arr2 = {2, 4, 6, 8, 10}; arr2 = reOrderArray(arr2); for (int i = 0; i < arr2.length; i++) { System.out.print(arr2[i] + " "); } } } 

上面的代碼固然能達到功能,但時間復雜度上完全不能恭維。每找到一個奇數,我們總是要去移動不少個位置的數。

等等。

我們上面算法最大的問題在於移動,我們能否不做這個移動呢?

當然是可以的。題目要求所有奇數都應該在偶數前面,所以我們應該只需要維護兩個下標值,讓一個下標值從前往后遍歷,另外一個下標值從后往前遍歷,當發現第一個下標值對應到偶數,第二個下標值對應到奇數的時候,我們就直接對調兩個值。直到第一個下標到了第二個下標的后面的時候退出循環。

我們有了這樣的想法,可以先拿一個例子在心中走一遍,如果沒有問題再寫代碼,這樣也可以讓面試官知道,我們並不是那種上來就開始寫代碼不考慮全面的程序員。

  1. 假定輸入的數組是 {1,2,3,4,5};
  2. 設定 odd = 0,代表第一個下標;even = arr.length = 4;
  3. 從前往后移動第一個下標 odd,直到它等於偶數,即當 odd = 1 的時候,我們停止移動;
  4. 再從后往前移動下標 even,直到它等於奇數,即當 even = 4 的時候,我們停止移動;
  5. 滿足 arr[odd] 為偶數,arr[even] 為奇數,我們對調兩個值,得到新數組 {1,5,3,4,2};
  6. 繼續循環,此時 odd = 3,even = 2,不滿足 odd < even 的條件,退出循環,得到的數組符合條件;

心中默走一遍沒問題后,開始手寫代碼:

public class Test14 { private static int[] reOrderArray(int[] arr) { int odd = 0, even = arr.length - 1; // 循環結束條件為 odd >= even while (odd < even) { // 第一個下標為偶數的時候停止 while (odd < even && Math.abs(arr[odd]) % 2 != 0) { odd++; } // 第二個下標為奇數的時候停止 while (odd < even && Math.abs(arr[even]) % 2 == 0) { even--; } // 找到后對調兩個值 int temp = arr[odd]; arr[odd] = arr[even]; arr[even] = temp; } return arr; } public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arr = reOrderArray(arr); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); int[] arr1 = {2, 4, 6, 8, 1, 3, 5, 7, 9}; arr1 = reOrderArray(arr1); for (int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + " "); } System.out.println(); int[] arr2 = {2, 4, 6, 8, 10}; arr2 = reOrderArray(arr2); for (int i = 0; i < arr2.length; i++) { System.out.print(arr2[i] + " "); } System.out.println(); } } 

擴展性更好的代碼,能秒殺 Offer

如果是面試應屆畢業生或者工作時間不長的程序員,面試官可能會滿意前面的代碼,但如果應聘者申請的是資深 的開發崗位,那面試官可能會接着問幾個問題。

  • 面試官:如果把題目改成把數組中的數組按照大小分為兩部分,所有的負數都在非負整數的前面,該怎么做?
  • 應聘者:這很簡單,可以重新定義一個函數,在新的函數里,只要修改第二個和第三個 while 循環里面的判斷條件就好了。
  • 面試官:如果再把題目改改,變成把數組中的數分為兩部分,能被 3 整除的數都在不能被 3 整除的數的前面,怎么辦?
  • 應聘者:我們還是可以定義一個新的函數,在這個函數中......
  • 面試官:(打斷應聘者的話),難道就沒有更好的方法?

這個時候應聘者應該要反應過來,面試官期待我們能提供的不僅僅是解決一個問題的辦法,而是解決一系列同類型問題的通用方法。我們在做解法的時候不能只想着解決當前的問題就好。在《大話設計模式》中,講解了一個非常有意思的事情就是大鳥讓小菜做商場促銷活動的時候,各種改變需求,把小菜繞的雲里霧里。

《大話設計模式》PDF 版本可以在公眾號后台回復「大話設計模式」即可獲取。

是呀,哪有不變的需求,需求不變,我們哪來那么多活干呀?不過要是,我們事先就做了這樣的准備,省下來的時間那不是正好又可以去玩一盤吃雞洛?

回到面試官新提出的兩個問題來,我們其實新的函數都只需要更改第二個和第三個 while 循環里面的判斷條件,而其它都是不需要動的。

public class Test14 { interface ICheck { boolean function(int n); } public static class OrderEven implements ICheck { @Override public boolean function(int n) { return n % 2 == 0; } } private static int[] reOrderArray(int[] arr, ICheck iCheck) { int odd = 0, even = arr.length - 1; // 循環結束條件為 odd >= even while (odd < even) { // 第一個下標為偶數的時候停止 while (odd < even && !iCheck.function(arr[odd])) { odd++; } // 第二個下標為奇數的時候停止 while (odd < even && iCheck.function(arr[even])) { even--; } // 找到后對調兩個值 int temp = arr[odd]; arr[odd] = arr[even]; arr[even] = temp; } return arr; } public static void main(String[] args) { OrderEven even = new OrderEven(); int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arr = reOrderArray(arr,even); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); int[] arr1 = {2, 4, 6, 8, 1, 3, 5, 7, 9}; arr1 = reOrderArray(arr1,even); for (int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + " "); } System.out.println(); int[] arr2 = {2, 4, 6, 8, 10}; arr2 = reOrderArray(arr2,even); for (int i = 0; i < arr2.length; i++) { System.out.print(arr2[i] + " "); } System.out.println(); } } 

寫這玩意兒的時候,我內心是拒絕的,由於 Java 沒有 Python 一樣方便的函數指針,我想了想只想到了用接口方式來處理。要是有其他實現方式的希望大家能在評論區留言~

好了,今天的面試講解,就先到這兒吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM