面試常考演算法題(二)--荷蘭國旗問題
阿新 • • 發佈:2018-12-11
面試常考演算法題(二)–荷蘭國旗問題
荷蘭國旗問題是面試中常考的一個題目,涉及到的思想並不是很複雜.
荷蘭國旗問題
題目
給定一個數組arr,和一個數num,請把小於num的數放在陣列的左邊,等於num的數放在陣列的中間,大於num的數放在陣列的右邊。
要求額外空間複雜度O(1),時間複雜度O(N)
問題解析
最差解法
這個問題如果不考慮時間複雜度和空間複雜度的話,最簡單的解法自然是使用輔助陣列,對當前陣列資料進行分類以後複製回原陣列,程式碼如下:
public static int[] terriblePartition(int[] arr, int num) {
if (arr == null || arr.length < 2){
return arr;
}
int[] temp = new int[arr.length];
int m = -1;
int n = arr.length ;
//設定兩個指標,分別指向temp陣列的最前面和最後面,將小於num的資料放在前部分,大於num的資料放在後部分
for (int i = 0; i < arr.length; i++) {
if (arr[i] < num) {
temp[++m] = arr[i];
} else if (arr[i] > num){
temp[--n] = arr[i];
}
}
//等於num的陣列資料放在中間
for (int i = m + 1; i < n; i++) {
temp[i] = num;
}
//複製temp陣列資料到arr陣列
copyArr(arr, temp) ;
//返回等於num資料的位置
return new int[]{m + 1, n - 1};
}
private static void copyArr(int[] arr, int[] temp) {
for (int i = 0; i < temp.length; i++) {
arr[i] = temp[i];
}
}
這個答案除非題目不要求並且時間不足夠思考更優秀的演算法,不然不建議使用此方法,這種解法一般僅用作比較器
.
優秀一點的解法
那麼如何在上述方法上進行優化呢,其實本質所使用的思想和上述程式碼思想一致.知道讀者未必對上面的程式碼感興趣,這裡我來通過一個實際陣列[4, 5, 2, 8, 1, 9, 6, 11]進行圖解一下:
程式碼
public static int[] partition(int[] arr, int num) {
if(arr == null || arr.length < 2){
return arr;
}
int less = -1;
int more = arr.length;
int i = 0;
while (i < more) {
if (arr[i] < num) {
swap(arr, ++less, i++);
} else if (arr[i] > num) {
swap(arr, --more, i);
}else {
i++;
}
}
//返回等於num資料的位置
return new int[] { less + 1, more - 1 };
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
後記
這篇文章到這裡可以說已經結束了,這個問題中的思想在快速排序
中可以得到應用,可以用荷蘭國旗問題來改進快速排序
使其時間複雜度變為O(N*logN),額外空間複雜度O(logN),這個問題將在下一篇文章快速排序中講.必須要注意的一點是,荷蘭國旗問題的解法其實是不穩定的
,會改變原陣列本可以不改變順序的資料的相對順序
.比如[0 9 7 4 4 ]陣列最後得到的結果是[0 4 4 7 9 ],陣列中的兩個4實際上交換了位置.
百度百科給出穩定演算法的定義如下:
假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序後的序列中,r[i]仍在r[j]之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。