首页 > 编程语言 > 详细

排序-快速排序

时间:2020-09-21 16:39:20      阅读:48      评论:0      收藏:0      [点我收藏+]

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);
    }
}

对于快排的分区操作有两种操作方式

  1. 双指针的快慢指针方式(对应方法partition),都是从低位到高位的遍历顺序,使用快指针指向的元素和pivot元素对比,对于满足排序条件的元素,将块慢指针指向的元素进行交换操作,并同时向后移动快慢指针;反之对于不满足排序条件的元素,只移动块指针;最后,慢指针元素和pivot元素进行交换,并返回慢指针索引为中间值索引;通过慢指针保证了[0,慢指针索引]区间中的元素都是大于等于或小于等于pivot元素;
  2. 双指针的低位和高位指针方式(对应方法partition2),低位指针从低到高进行遍历,高位指针从高到低进行遍历,使用低位指针指向的元素和pivot元素进行对比,当符合排序条件时,只向后移动低位指针;当不满足排序条件时,将低位指针指向的元素写入到高位指针索引位置,将高位指针前移一位,将当前高位指针指向的元素写入到低位指针索引位置,低位指针不移动,此时高位指针实际指向的就是一个空闲插槽; 当高位指针和低位指针相遇时,表示分区结束,将中间元素写入到高位指针指向位置;此时高位指针就是中间元素指针

"未完待续-----剩余使用python+Matplotlib 绘制可视化动画"

https://zhuanlan.zhihu.com/p/38157591

  

排序-快速排序

原文:https://www.cnblogs.com/xingguoblog/p/13704701.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!