主要討論:荷蘭國旗問題、隨機快速排序、堆排序、穩定性、比較器、桶排序、相鄰兩數的最大差值問題和簡單介紹工程中的綜合排序算法面試
給定一個數組arr,和一個數num,請把小於等於num的數放在數組的左邊,大於num的數放在數組的右邊。算法
要求額外空間複雜度O(1),時間複雜度O(N)api
參考下面的代碼便可數組
問題二(荷蘭國旗問題)緩存
給定一個數組arr,和一個數num,請把小於num的數放在數組的左邊,等於num的數放在數組的中間,大於num的數放在數組的右邊。less
要求額外空間複雜度O(1),時間複雜度O(N)dom
public static int[] partition(int[] arr, int l, int r, int p) { int less = l - 1; int more = r + 1; while (l < more) { if (arr[l] < p) { swap(arr, ++less, l++); } else if (arr[l] > p) { swap(arr, --more, l); } else { l++; } } return new int[] { less + 1, more - 1 }; } // for test public static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
隨機快速排序的細節和複雜度分析ide
能夠用荷蘭國旗問題來改進快速排序ui
時間複雜度O(N*logN),額外空間複雜度O(logN)this
public static void quickSort(int[] arr) { if (arr == null || arr.length < 2) { return; } quickSort(arr, 0, arr.length - 1); } public static void quickSort(int[] arr, int l, int r) { if (l < r) { swap(arr, l + (int) (Math.random() * (r - l + 1)), r); int[] p = partition(arr, l, r); quickSort(arr, l, p[0] - 1); quickSort(arr, p[1] + 1, r); } } public static int[] partition(int[] arr, int l, int r) { int less = l - 1; int more = r; while (l < more) { if (arr[l] < arr[r]) { swap(arr, ++less, l++); } else if (arr[l] > arr[r]) { swap(arr, --more, l); } else { l++; } } swap(arr, more, r); return new int[] { less + 1, more }; } public static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
堆排序的細節和複雜度分析
時間複雜度O(N*logN),額外空間複雜度O(1)
堆結構很是重要
1,堆結構的heapInsert與heapify
2,堆結構的增大和減小
3,若是隻是創建堆的過程,時間複雜度爲O(N)
4,優先級隊列結構,就是堆結構
public static void heapSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 0; i < arr.length; i++) { heapInsert(arr, i); } int size = arr.length; swap(arr, 0, --size); while (size > 0) { heapify(arr, 0, size); swap(arr, 0, --size); } } public static void heapInsert(int[] arr, int index) { while (arr[index] > arr[(index - 1) / 2]) { swap(arr, index, (index - 1) / 2); index = (index - 1) / 2; } } public static void heapify(int[] arr, int index, int size) { int left = index * 2 + 1; while (left < size) { int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left; largest = arr[largest] > arr[index] ? largest : index; if (largest == index) { break; } swap(arr, largest, index); index = largest; left = index * 2 + 1; } } public static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
排序算法的穩定性及其彙總
冒泡、插入、歸併穩定。
選擇、快速、堆排序不穩定。
值相等的狀況下,相對次序獲得保持。
由現實的例子決定,例如默認序列是按照身高排序的,再按照年齡排序的時候,但願能夠保留原始身高的信息,再進行年齡排序。
有關排序問題的補充:
1,歸併排序的額外空間複雜度能夠變成O(1),可是很是難,不須要掌握,能夠搜「歸併排序 內部緩存法」
2,快速排序能夠作到穩定性問題,可是很是難,不須要掌握,能夠搜「01 stable sort」
3,有一道題目,是奇數放在數組左邊,偶數放在數組右邊,還要求原始的相對次序不變,碰到這個問題,能夠懟面試官。面試官非良人。
認識比較器的使用
1 public class Code_09_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 15 public static class IdAscendingComparator implements Comparator<Student> { 16 17 @Override 18 public int compare(Student o1, Student o2) { 19 return o1.id - o2.id; 20 } 21 22 } 23 24 public static class IdDescendingComparator implements Comparator<Student> { 25 26 @Override 27 public int compare(Student o1, Student o2) { 28 return o2.id - o1.id; 29 } 30 31 } 32 33 public static class AgeAscendingComparator implements Comparator<Student> { 34 35 @Override 36 public int compare(Student o1, Student o2) { 37 return o1.age - o2.age; 38 } 39 40 } 41 42 public static class AgeDescendingComparator implements Comparator<Student> { 43 44 @Override 45 public int compare(Student o1, Student o2) { 46 return o2.age - o1.age; 47 } 48 49 } 50 51 public static void printStudents(Student[] students) { 52 for (Student student : students) { 53 System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age); 54 } 55 System.out.println("==========================="); 56 } 57 58 public static void main(String[] args) { 59 Student student1 = new Student("A", 1, 23); 60 Student student2 = new Student("B", 2, 21); 61 Student student3 = new Student("C", 3, 22); 62 63 Student[] students = new Student[] { student3, student2, student1 }; 64 printStudents(students); 65 66 Arrays.sort(students, new IdAscendingComparator()); 67 printStudents(students); 68 69 Arrays.sort(students, new IdDescendingComparator()); 70 printStudents(students); 71 72 Arrays.sort(students, new AgeAscendingComparator()); 73 printStudents(students); 74 75 Arrays.sort(students, new AgeDescendingComparator()); 76 printStudents(students); 77 78 } 79 80 }
桶排序、計數排序、基數排序的介紹
1,非基於比較的排序,與被排序的樣本的實際數據情況頗有關係,所以實際中並不常用
2,時間複雜度O(N),額外空間複雜度O(N)
3,穩定的排序
public static void bucketSort(int[] arr) { if (arr == null || arr.length < 2) { return; } int max = Integer.MIN_VALUE; for (int i = 0; i < arr.length; i++) { max = Math.max(max, arr[i]); } int[] bucket = new int[max + 1]; for (int i = 0; i < arr.length; i++) { bucket[arr[i]]++; } int i = 0; for (int j = 0; j < bucket.length; j++) { while (bucket[j]-- > 0) { arr[i++] = j; } } }
補充問題
給定一個數組,求若是排序以後,相鄰兩數的最大差值,要求時間複雜度O(N),且要求不能用非基於比較的排序。
解題思路:
一、準備N+1個桶,遍歷數組找到最小最大值
二、Min放在第一個桶,max放在最大的桶,而後把最小到最大的這個範圍等分爲N+1份
三、那麼兩邊放入最大和最小,必然中間會有一個空桶。桶內部的差值確定沒有桶範圍的大。
四、因此流程爲:N個數準備N+1個桶,0~N,當一個數進入桶,每一個桶只記錄出現的最小值和最大值,還有一個bool記錄這個桶有沒有進入過數。
五、遍歷完成後,計算費控桶直接的差值,用min和max計算,若是是空桶就跳過。
六、設置一個空桶的緣由是,用於否認最大差值必定不來自一個桶內部,由於有空桶的存在,那空桶兩側的非空桶差值,確定大於或者等於任意桶內容最大和最小值的差值。
七、上面的式子就是右邊先計算出,每一個桶放多少個數,再把num-min爲分子,能夠看當前數佔用在哪一個位置上。例若有10個數,最大最小範圍爲1~100那就是說10/100=1/10,當前數要在放以10爲分母的中,去計算他該去哪一個桶。
少於60個數的直接用插入排序。(前期用遞歸分一半一半的,變小了再按照其餘排序方法來排序,綜合利用各排序的優勢)
基礎類型用快速排序,由於基礎類型相同分數無差別。
自定義類型用歸併排序,由於關乎多類數據,須要保持原始排序。