算法篇4:荷兰国旗问题优化经典快排
一:经典快排请参考《算法篇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 }