快速排序及其优化

泛型苔原
• 阅读 6509

简单实现

快速排序是比较经典、常用的算法,下面简要介绍其思路。对于一个数组,选取某个元素作为切分元素(比如第一个元素),然后把比这个元素小的都放到它前面,比这个元素大的都放到它后面,这样切分元素的最终位置就确定了,并且数组被划分为两个子数组。然后再用同样的方法分别对子数组进行排序,最终整个数组将变成有序的。
这里的关键是实现数组划分的方法,这里举例说明。假如要对数组a={5,3,6,5,8,4,7}排序,选取a[0] 作为切分元素,排序过程如下:

5,3,6,5,8,4,7
i            j

5,3,6,5,8,4,7
    i     j   
    
5,3,4,5,8,6,7
      ij

如上所示,刚开始i指向数组第一个元素,而j指向数组末尾元素后一个位置,i从前往后扫描,直到找到一个大于等于切分元素时停下(这里是6),j从后往前扫描,直到找到一个小于等于切分元素的时候停下(这里是4),然后交换这两个元素。然后继续寻找直到两个指针相遇,则本轮扫描结束。此时j<=ij右边的元素均大于切分元素,交换a[j]与切分元素即可。接着对两个子数组{5,3,4}以及{8,6,7}继续用刚才的方法排序,最终数组将全部有序。代码实现如下:

void sort(int[] a, int lo, int hi) {
    if (hi <= lo)
        return;
    int j = partition(a, lo, hi);
    sort(a, lo, j - 1);
    sort(a, j + 1, hi);
}

int partition(int[] a, int lo, int hi) {
    int i = lo, j = hi + 1;
    int v = a[lo];
    while (true) {
        while (a[++i] < v)
            if (i == hi)
                break;
        while (a[--j] > v)
            if (j == lo)
                break;
        if (i >= j)
            break;
        swap(a, i, j);    //交换元素
    }
    swap(a, lo, j);
    return j;
}

对于快速排序有几点说明:

  • 快速排序是原地排序(只需要一个辅助栈)
  • 一般情况下快速排序的时间复杂度是O(NlgN),因为平均而言划分的子数组是对半分的。但如果切分不均匀,那么时间可能会变成平方级别。例如对于一个已经有序的数组,快速排序的性能反而很差。
  • 在每次扫描时,左侧扫描遇到大于等于切分元素时停下,右侧扫描则是遇到小于等于切分元素时停下。这样会将一些等值元素交换,但可以避免在数组中含有大量重复值的时候运行时间变成平方级别。

优化

对于快速排序有3点优化思路:

  1. 对于小数组,可以采用插入排序,避免递归调用。例如,当if(hi <= lo + M)时,就可以转到插入排序。
  2. 采用子数组的一部分元素的中位数来切分数组。这样做得到的切分更好,但代价是需要计算中位数。
  3. 如果数组中含有大量的重复元素,可以采用三向切分。将数组切分为三部分,分别对应于小于、等于和大于切分元素的数组元素。代码实现如下:

    private static void sort1(int[] a, int lo, int hi) {
    if (hi <= lo)
        return;
    int lt = lo, i = lo + 1, gt = hi;
    int v = a[lo];
    while (i <= gt) {
        if (a[i] < v) {
            swap(a, lt++, i++);
        } else if (a[i] > v) {
            swap(a, i, gt--);
        } else {
            i++;
        }
        sort(a, lo, lt - 1);
        sort(a, gt + 1, hi);
    }

    }

另一种实现:单向扫描

快速排序的数组切分还有另一种单向扫描的版本,具体步骤是选择数组中最后一个元素作为切分元素,同样设置两个指针,指针i指向数组中第一个元素前面一个位置,j则指向数组中第一个元素。j从前左右往右扫描,遇到小于等于切分元素时就把i加一,然后交换ij指向的元素。最后把i+1位置的元素和切分元素交换即可完成一次数组划分。代码实现如下:

int partition(int[] a, int lo, int hi) {
    int i = lo - 1, j = lo;
    int v = a[hi];
    while (j < hi) {
        if (a[j] <= v) {
            swap(a, ++i, j);
        }
        j++;
    }
    swap(a, i + 1, hi);
    return i + 1;
}
点赞
收藏
评论区
推荐文章
22 22
4年前
【排序算法动画解】简单选择排序
本文为系列专题的第13篇文章。1.2.3.4.5.6.7.8.9.10.11.12.在文章【】中,我们介绍了暴力排序和冒泡排序这两种排序算法,算是一个引子。同时指出暴力排序和冒泡排序的缺点硬伤。本文介绍的简单选择排序则“医治”了上面提到的暴力排序和冒泡排序的“硬伤”。核心思想:从数组的第一个元素开始,将其作为基准元素,然后找出基准
似梦清欢 似梦清欢
2年前
排序算法(简单选择、堆排序、归并)
简单选择排序:::tip简单选择排序原理:将未排序的数组中从前向后遍历,找到最小的元素和数组中第一个元素交换位置,此时数组中第一个元素位置已经确定,再将未排序的数组中从前向后遍历,找到最小的元素和数组中第二个元素交换位置,依次向下。:::需要两层循环,外层
九路 九路
4年前
7 二分搜索树的原理与Java源码实现
1折半查找法了解二叉查找树之前,先来看看折半查找法,也叫二分查找法在一个有序的整数数组中(假如是从小到大排序的),如果查找某个元素,返回元素的索引。如下:intarrnewint{1,3,4,6,8,9};在arr数组中查找6这个元素,查到返回对应的索引,没有找到就返回1思想很简单:1先找到数组中间元素ta
Stella981 Stella981
3年前
C语言 快速排序 Quick Sort
算法描述:快速排序一般是选择数组的第一个数据为对称轴参考值pivot。按照大小数组分割成左右两个区间。然后对左右两个区间再进行递归排序,知道结束为止。例子演示:数组:43251,长度:5,对称轴参考值选择第一个数据4。比它小的我们放到它的右边,比它大的我们放到左边。设置左右两个工作位置。指向开头和末尾。第一轮:4325
Stella981 Stella981
3年前
JavaScript基础知识
题目:1.找出数字数组中最大的元素(使用Math.max函数)2.转化一个数字数组为function数组(每个function都弹出相应的数字)3.给object数组进行排序(排序条件是每个元素对象的属性个数)4.利用JavaScript打印出Fibonacci数(不使用全局变量)5.实现如下语法的功能:vara(5).plus(
Wesley13 Wesley13
3年前
Java面试总结(排序算法)
1.冒泡排序算法描述:两两比较,大的放后面2.选择排序算法描述:在m元数组中找到最小值的位置,然后将最小值的位置和第n(n0,1,2,....m1)位的值对调,排序k次则m元数组中前k(k<m)位的值已经排序好,m元数组中前k位的值不需要再进行排序,此时需要排序的元素只有mk个3.插入排序算
Stella981 Stella981
3年前
Quick Sort(三向切分的快速排序)(Java)
1//三向切分的快速排序2//这种切分方法对于数组中有大量重复元素的情况有比较大的性能提升34publicstaticvoidmain(Stringargs)5{6ScannerinputnewScanner(System.in);7
Wesley13 Wesley13
3年前
C语言自学《五》
什么是数组数组是一组数目固定、类型相同的数据项数组中的数据称为元素比如longnumbers\10\;方括号中的数字定义了要存放在数组中的元素个数,称为数组维度数组有一个类型,它组合了元素的类型和数组中的元素个数,因此如果两个数组的元素个数、类型相同,这两个数组的类型就相同可以在数组名称后的方括号内使用索引值,索引值是从0开始
Wesley13 Wesley13
3年前
PHP快速排序(原地切分)
        快速排序是一种分治的排序算法,采用递归的思想,将数组元素分为两部分,选择切分元素,左右扫描数组,将大于切分元素的数据放在右边,小于切分元素的数据放在左边,直到扫描指针相遇,切分结束,同时递归调用,直到数组有序。      代码如下:<?phpfunctionquick_sort(array&$array,$l
菜园前端 菜园前端
2年前
什么是插入排序?
原文链接:什么是插入排序(insertionSort)?在数组中从左到右依次取一个数出来,然后把它放到合适的位置。从思想上可以分为有序区和无序区,有序区在左边代表已经排列好的元素。算法步骤1.默认左边第一个元素已经在有序区了2.在无序区取一个数出来(第二个
菜园前端 菜园前端
2年前
什么是快速排序?
原文链接:什么是快速排序(quickSort)?主要分成两部分实现,分区、递归操作。分区从数组中任意选择一个"基准",所有比基准小的元素放在基准前面,比基准大的元素放在基本后面。递归递归地对基准前后的子数组进行分区。算法步骤1.首先获取数组的第一个值(作为