一:經典快排請參考《算法篇1:排序算法(上篇)》
荷蘭國旗問題:
給定一個數組arr ,和一個數num ,請把小於num的數放到數組的左邊,等於num的數放在數組得到中間,大於num的數放在數組的右邊。(要求額外空間復雜度O(1),時間復雜度O(N))
解題思路:我們用三個指針,第一個 less 指向數組的最左邊減一,表示小於 num 的位置,第二個 more 指向數組最右邊加一,表示大於num的位置,再定義一個指針 cur 在數組中與 num 進行比較,初始位置指向 0,如果 cur指向的值小於 num ,交換 less 的下一個位置與 cur 所指向的數,less 前進一個位置,cur 指向的值等於,則不做變化,cur 指向下一個位置,如果 cur 指向大於 num ,交換 cur 與more 的前一個位置,依次比較到 cur 與more 重合。
代碼實現:
1 public static int[] partition(int[] arr, int L, int R ,int num) { 2 int less = L - 1; 3 int more= R+1; 4 int cur = r; 5 while (cur < more) { 6 if (arr[cur] < num) { 7 swap(arr, ++less, cur++); 8 } else if (arr[cur] > num) { 9 swap(arr, --more, cur); 10 } else { 11 cur++; 12 } 13 } 14 return new int[] { less + 1, more-1 }; 15 } 16 17 public static void swap(int[] arr, int i, int j) { 18 int tmp = arr[i]; 19 arr[i] = arr[j]; 20 arr[j] = tmp; 21 }
我們就可以使用這個思路來優化經典快排的方法,經典快排每次只是找到一個數的正確位置
但是我們需對上述代碼進行改進,因為快排時我們一般選取最后一個數作為參考數,當在荷蘭問題時,我們就直接將最后一個數放在 more 內,不讓其與其他進行比較交換即可。
代碼實現如下:
1 public static void quickSort(int[] arr, int l, int r) { 2 if (l < r) { 3 int[] p = partition(arr, l, r); 4 //遞歸將數組兩側的排序完成 5 quickSort(arr, l, p[0] - 1); 6 quickSort(arr, p[1] + 1, r); 7 } 8 } 9 10 public static int[] partition(int[] arr, int l, int r) { 11 int less = l - 1; 12 int more = r; 13 while (l < more) { 14 if (arr[l] < arr[r]) { 15 swap(arr, ++less, l++); 16 } else if (arr[l] > arr[r]) { 17 swap(arr, --more, l); 18 } else { 19 l++; 20 } 21 } 22 swap(arr, more, r); 23 //返回相等區域的下標 24 return new int[] { less + 1, more }; 25 } 26 27 public static void swap(int[] arr, int i, int j) { 28 int tmp = arr[i]; 29 arr[i] = arr[j]; 30 arr[j] = tmp; 31 }
利用這種方法還有問題嗎?其實快速排序本身就存在着弊端,當數組原本有序時,大於和小於區域划分的大小差別不一致,這是復雜度就會增加,那還有辦法優化嗎?
其實我們可以隨機找一個數,這種算法叫做隨機快排,這種算法的復雜度是O(n log n )這樣就更快了一些。
實現:將此行代碼加入到 if (l < r) {}內,只是相當於隨機選一個位置與最后一個數換下來,這樣不至於在原本有序的情況下復雜度過高。
1 swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
二:比較器
我們在工程中,一般進行比較時,大都是對對象進行比較排序,如果直接將對象丟在數組里,調用數組的Arrays.sort(數組),此時只是對數組中每個對象的內存地址進行排序,那么排出的將毫無意義。我們如果想根據對象的某一個屬性進行排序,我們這時就必須要自定義比較器。
代碼實例:
1 public class Comparator { 2 //模擬定義學生對象 3 public static class Student { 4 public String name; 5 public int id; 6 public int age; 7 8 public Student(String name, int id, int age) { 9 this.name = name; 10 this.id = id; 11 this.age = age; 12 } 13 } 14 public static void main(String[] args) { 15 //實例化對象 16 Student student1 = new Student("A", 1, 23); 17 Student student2 = new Student("B", 2, 21); 18 Student student3 = new Student("C", 3, 22); 19 20 Student[] students = new Student[] { student3, student2, student1 }; 21 printStudents(students); 22 23 //根據年齡進行排序輸出對象 24 Arrays.sort(students, new AgeAscendingComparator()); 25 printStudents(students); 26 27 } 28 //自定義比較器 29 public static class AgeAscendingComparator implements Comparator<Student> { 30 31 @Override 32 public int compare(Student o1, Student o2) { 33 return o1.age - o2.age; 34 } 35 36 }