排序-快速排序
https://en.wikipedia.org/wiki/Quicksort
快速排序
- 原理:通过中位值(pivot)进行比较来进行不断分区,直到所有的区间达到最小(容量为1)时,此时结果就是有序的; 在执行对比操作时是从低位到高位的对比操作,通过不断摘除中间值的方式来实现排序
- 快速排序所有的移动操作都是在原数组中进行操作的,并不需要额外的数组空间,因此属于原地排序算法
- 稳定性:由于对于相同元素也是需要移动的,因此有可能出现相同元素排序前后相对位置不一致;所以快速排序是不稳定的排序
快排体现了分治的思想,不过重点在于分区,分区的目的是为了找到中心点的位置,排除中心点元素,对左右区间继续分区步骤;
快排通过每次确认每个分区点中心位置,并对于分区排除已确定位置的中心元素来实现排序的目的
public interface Sort<T extends Comparable<T>> { /** * 对数组进行排序 * * @param array * @param flag true 代表的是正序;false代表的是逆序 */ void sort(T[] array, boolean flag); } public class QuickSort<T extends Comparable<T>> implements Sort<T> { /** * 快排的主要思想在于分区,通过不断的分区达到排序的效果 * 5 4 3 2 1 * pivot(中心点) * low->开始遍历当前区间high * i,j ; 当存在 array[j] < pivot 时, 交换 array[i],array[j] = array[j],array[i] (golang语法),此时i + 1,后移一位,j继续往后移动直到high,交换当前i和high,array[i],array[high] = array[high],array[i] , 此时i就作为当前分区的中心点 * 第一次分区结束后的结果为 ,当前pivot 的索引为 0(i) ;分成的区间为 [][1][4,3,2,5];由中心点继续分区 * * @param array * @param flag true 代表的是正序;false代表的是逆序 */ @Override public void sort(T[] array, boolean flag) { sort(array, 0, array.length - 1, flag); } /** * 对于快速排序实际就是一个不断递归分区的过程 * pivot元素初始时是当前分区的最高位元素,high为pivot元素前一个位置,low为最低位 * 通过low high双指针实现对比交换 * * @param array * @param low * @param high * @param flag true代表升序,false代表降序 */ public void sort(T[] array, int low, int high, boolean flag){ // 如果低位索引值大于等于高位索引值,则表示没有必要在进行继续分区 if (low >= high) { return; } // 返回分区中间值索引 // int pivotIndex = partition(array,low,high,flag); // 双指针移动, low和high int pivotIndex = partition2(array,low,high,flag); // 对于左右分区都不会包含中间元素 // 递归 左侧分区 sort(array, low, pivotIndex - 1, flag); // 递归 右侧分区 sort(array, pivotIndex + 1, high, flag); } // 双指针,通过块慢指针均从低位移动, 对于[0,i]区间可以保证的是其中的元素肯定都大于等于或小于等于中间元素 public int partition(T[] array, int low, int high, boolean flag) { // 最高位作为中心点 T pivot = array[high]; // 待交换位置 int i = low; // 遍历游标 int j = low; for (; j < high; j++) { // 如果为升序,当存在array[j] =< pivot,将array[j]放到前面和i进行交换位置,并将i后移一位 if ((flag && array[j].compareTo(pivot) < 1) || (!flag && array[j].compareTo(pivot) > 0)) { // 交换i和j的元素 T temp = array[i]; array[i] = array[j]; array[j] = temp; // 将i后移一位 i++; } } // 不管i是否变化,都要将中心点元素和当前i所在位置元素进行交换 // 交换当前high 和 i 的元素,将中间点的元素移动到中间索引位置 array[high] = array[i]; array[i] = pivot; // 返回中间索引 return i; } // 采用双指针移动, 低位指针从低往高位移动,高位指针从高位往低位移动 public int partition2(T[] array, int low, int high, boolean flag) { // 将最高位设置为pivot元素 T pivot = array[high]; while (low < high) { if ((flag && array[low].compareTo(pivot) == -1) || (!flag && array[low].compareTo(pivot) == 1)) { low++; } else { // 交换高位和低位元素 array[high--] = array[low]; // 此时high已经前移一位 array[low] = array[high]; // 此时high的位置已经出现空闲插槽 } } // 将中间元素放入空闲插槽中 array[high] = pivot; return high; } public static void main(String[] args) { Integer[] values = new Integer[]{3,7,8,5,2,1,5,4}; Sort<Integer> integerInsertSort = new QuickSort<>(); integerInsertSort.sort(values, true); Stream.of(values).forEach(System.out::print); System.out.println(); integerInsertSort.sort(values, false); Stream.of(values).forEach(System.out::print); } }
对于快排的分区操作有两种操作方式
- 双指针的快慢指针方式(对应方法partition),都是从低位到高位的遍历顺序,使用快指针指向的元素和pivot元素对比,对于满足排序条件的元素,将块慢指针指向的元素进行交换操作,并同时向后移动快慢指针;反之对于不满足排序条件的元素,只移动块指针;最后,慢指针元素和pivot元素进行交换,并返回慢指针索引为中间值索引;通过慢指针保证了[0,慢指针索引]区间中的元素都是大于等于或小于等于pivot元素;
- 双指针的低位和高位指针方式(对应方法partition2),低位指针从低到高进行遍历,高位指针从高到低进行遍历,使用低位指针指向的元素和pivot元素进行对比,当符合排序条件时,只向后移动低位指针;当不满足排序条件时,将低位指针指向的元素写入到高位指针索引位置,将高位指针前移一位,将当前高位指针指向的元素写入到低位指针索引位置,低位指针不移动,此时高位指针实际指向的就是一个空闲插槽; 当高位指针和低位指针相遇时,表示分区结束,将中间元素写入到高位指针指向位置;此时高位指针就是中间元素指针
JDK中的代码实现
java.util.DualPivotQuicksort
待分析?????
"未完待续-----剩余使用python+Matplotlib 绘制可视化动画"
https://zhuanlan.zhihu.com/p/38157591

浙公网安备 33010602011771号