快速排序算法过程及其代码优化
快速排序,就是我们选取数组里的一个基数,根据基数,我们把整个数组分为两部分,一边是大于该基数的,一边是小于该基数的。
如上图所示,我们选取6作为基数,经过一系列的排序后,6处于它在排序好的数组中应该存在的位置,两边分别是大于它和小于它的数字,然后我们再分别对两边分别递归快排,之后再对两边分别递归快排,之后全部汇总,得到一个有序的数组。但是我们有个问题,就是这个6不止一个,可能有两三个,那么这个时候就会出现这种情况:
1 2 4 6 6 6 7
这个时候,我们如果还是按照之前的,从lo和hi汇合的地方对两边子部分分别递归快排,这个时候两边的6就显得很多余,因为他们可以不用加入到快排的任务中去,只需要从三个6的两边进行排序即可。所以我们这个时候就要对快排进行改进!
普通快排:
public class QuickSort { public static void sort(int[] arr, int low, int high) { if (low >= high) { return; } int index = arr[low]; int i = low; int j = high; while (i < j) { while (i < j && arr[j] >= index) { j--; } if (i < j) { arr[i++] = arr[j]; arr[j] = index; } while (i < j && arr[i] <= index) { i++; } if (i < j) { arr[j--] = arr[i]; arr[i] = index; } } sort(arr, low, i - 1); sort(arr, i + 1, high); } }
我们定义两个指针less和more,从less负责掌管左边(比基数小的数),more负责掌管右边(比基数大的数)。
1 2 4 6 6 6 7
less L more
这时我们只需要对less和less前面的部分,more后面的部分进行排序,这样我们就不必和之前一样,因为基数重复而需要进行额外的一些判断了。但是还有一个问题就是,如果我们选取的基数,它所处的位置在排序好的数组里处于比较极端的位置(比如处于第一个,或者是最后一个),那这个时候我们的排序就有没有起到分而治之的作用,所以我们可以通过random来做到随机选取数组里的一个数来作为基数,这样我们就很难会出现上面的情况!
改进后的代码:
public class QuickSortImprove { 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]); 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); int[] res = { less, more }; return res; } public static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } }