1. 前言:
这两种排序经常使用,且在算法题中经常遇见。
这里我们简单分析讨论一下。
1. 快速排序
平均时间复杂度:
O(nlogn)
最坏时间复杂度:O(n^2)
1.1. 左右向中遍历:
- 取最右侧4为基础元素,取出后,4的位置为null。
- 从有空的另一侧开始遍历比较,如果左侧3的元素小于基准元素4,则不变化,反之则把左侧元素交换到右侧,左侧为null。
- 然后从右侧开始遍历。
- 反复,直到low,high指针相遇。
这种方式易于理解却不便于书写,需要反复判断是向左遍历还是向右遍历。
public class QuickSortVariant {public static void main(String[] args) {int[] arr = {1, 3, 6, 2, 4};quickSort(arr, 0, arr.length - 1);for (int i : arr) {System.out.print(i + " ");}}public static void quickSort(int[] arr, int low, int high) {if (low < high) {int pivotIndex = partition(arr, low, high);quickSort(arr, low, pivotIndex - 1);quickSort(arr, pivotIndex + 1, high);}}public static int partition(int[] arr, int low, int high) {int pivot = arr[high]; // 选择最右侧元素作为基准int left = low;int right = high - 1;while (left < right) {// 从左侧寻找大于基准的元素while (arr[left] <= pivot && left < right) {left++;}// 从右侧寻找小于基准的元素while (arr[right] >= pivot && left < right) {right--;}// 交换左右找到的元素swap(arr, left, right);}// 如果最后左指针指向的元素大于基准,与基准交换if (arr[left] >= pivot) {swap(arr, left, high);} else {left++;}return left; // 返回基准元素的最终位置}public static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}
1.1. 单侧遍历:
这种遍历理解麻烦点,书写却简单点。
- i 指针指向小于base元素的结尾。这里我指向了数组前的一个元素,这不是错误,而是一个巧妙的设计。
- 如果 j 指针指向的元素小于base,则 i 指针扩充一个元素,即i++,然后swap(i,j)一般情况下二者是重合的。
- 如果遇见大于base的指针,则j++,i 不变,等效与记录了这个大元素指针的位置,再遇到小的元素可以通过swap(i,j)交换,这时,则真正起作用。
public class Solution {public static void main(String[] args) {int []arr = new int[]{1,3,6,2,4};sort(arr,0,4);System.out.println(Arrays.toString(arr));}public static void sort(int[]arr,int low, int high){int baseIndex = quickSort(arr,low,high);quickSort(arr,low,baseIndex-1);quickSort(arr,baseIndex + 1,high);}public static int quickSort(int[]arr,int low, int high){int base = arr[high];int i = low - 1;for(int j = low; j < arr.length - 1; j++){if(arr[j] < base){i++;swap(arr,i,j);}}swap(arr,high,i + 1);return i + 1;}public static void swap(int[]arr,int index0, int index1){int tem = arr[index0];arr[index0] = arr[index1];arr[index1] = tem;}}