前兩天面試的過程中問道的一個算法題,題目不算難,但是一步步分析優化的過程我覺得挺受啟發,所以拿出來分享一下。
題目要求很簡單,就是找出給定數組中第二大的數,略微思考之后我給出了下面的答案,即使用執行兩次迭代,使用冒泡排序將兩個最大值移動到數組末尾,數組中倒數第二個值即為要求的第二大的值。
public static int getSecondBiggestNum(int []arr) { int temp; for (int j = 0; j < 2; j++) { for (int i = 0; i < arr.Length - 1; i++) { if (arr[i] > arr[i + 1]) { temp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = temp; } } } return arr[arr.Length - 2]; }
時間復雜度為2n,效率還可以,使用數組{8,6,2,7,3}來測試返回結果也沒有問題,但是忽略了一個細節,題目中沒有說明數組中的值是不重復的,所以對於{8,8,6,2,7,3}來說,返回結果就是錯誤的。
於是我考慮可以在兩次迭代之后判斷最后兩個值是否相等,如果相等的話再繼續第三次冒泡排序,這樣以此類推,總能找到第二大的,但是很快這種想法被否定了,因為在極端情況下(如{8,8,8,8,8,3})算法的復雜度是n2,效率太低。
那么接着分析,首先肯定是要有一輪迭代找出最大值的,那么既然已經找出最大值了,是不是可以在第二輪迭代中直接將數組中其他小於最大值的數移到一個臨時數組中呢,然后對臨時數組再做一次冒泡就可以求出最大值,時間復雜度大概是3n,貌似可以接受。
public static int getSecondBiggestNum(int []arr) { int temp; int []tempArr = new int[arr.Length-1]; for (int i = 0; i < arr.Length - 1; i++) { if (arr[i] > arr[i + 1]) { temp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = temp; } } for (int i = 0; i < arr.Length - 1; i++) { if (arr[i] != arr[arr.Length-1]) { tempArr[i] = arr[i]; } } for (int i = 0; i < tempArr.Length - 1; i++) { if (tempArr[i] > tempArr[i + 1]) { temp = tempArr[i]; tempArr[i] = tempArr[i + 1]; tempArr[i + 1] = temp; } } return tempArr[tempArr.Length - 1]; }
但是新的問題又來了,因為引入了新的臨時數組變量,增加了空間復雜度。那么不用臨時數組是不是可以呢?既然最大值已經求出來了,那么完全可以在第二輪迭代的時候通過與最大值的比較得出第二大的值。
public static int getSecondBiggestNum(int []arr) { int temp =0; //第一輪迭代求最大值 for (int i = 0; i < arr.Length - 1; i++) { if (arr[i] > arr[i + 1]) { temp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = temp; } } //第二輪迭代求第二大值 for (int i = 0; i < arr.Length - 1; i++) { if (temp <arr[i] && arr[i] != arr[arr.Length-1] )
{
temp = arr[i];
}
}
return temp;
}
貌似可以了,但是仔細看第一次迭代,只是求最大值,沒有必要進行排序,排序也是要耗費內存降低效率的,所以再定義一個臨時變量記錄最大值。
public static int getSecondBiggestNum(int []arr) { int temp =0; int biggestNum = arr[0]; //第一輪迭代求最大值 for (int i = 0; i < arr.Length ; i++) { if (biggestNum < arr[i]) biggestNum = arr[i]; } //第二輪迭代求第二大值 for (int i = 0; i < arr.Length; i++) { if (temp < arr[i] && arr[i] != biggestNum) { temp = arr[i]; } } return temp; }
這次終於可以了,但是使用{1,1,1,1,1,1}這種數組驗證時,返回了0,明顯是錯誤的值,這時候應該給出提示,按照具體需求返回一個值,具體的代碼這里就不再寫了。
這道題確實不難實現,很容易找到思路,但是實現功能是一方面,分析還有沒有可優化的地方是另一方面,而這個分析優化的過程也是自己不斷進步的過程。
(委托系列文章稍后會繼續)