广告
返回顶部
首页 > 资讯 > 后端开发 > Python >JAVA十大排序算法之快速排序详解
  • 640
分享到

JAVA十大排序算法之快速排序详解

2024-04-02 19:04:59 640人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录快速排序问题思路荷兰国旗问题代码实现时间复杂度算法稳定性总结快速排序 快速排序是对冒泡排序的一种改进,也是采用分治法的一个典型的应用。jdk中Arrays的sort()方法,具体

快速排序

快速排序是对冒泡排序的一种改进,也是采用分治法的一个典型的应用。jdk中Arrays的sort()方法,具体的排序细节就是使用快速排序实现的。

数组中任意选取一个数据(比如数组的第一个数或最后一个数)作为关键数据,我们称为基准数(pivot,或中轴数),然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序,也称为分区(partition)操作。

问题

若给定一个无序数组 [8, 5, 6, 4, 3, 1, 7, 2],并指定一个数为基准,拆分数组使得左侧的数都小于等于它 ,右侧的数都大于它。

基准的选取最优的情况是基准值刚好取在无序区数值的中位数,这样能够最大效率地让两边排序,同时最大地减少递归划分的次数,但是一般很难做到最优。基准的选取一般有三种方式:

  • 选取数组的第一个元素
  • 选取数组的最后一个元素
  • 以及选取第一个、最后一个以及中间的元素的中位数(如4 5 6 7, 第一个4, 最后一个7, 中间的为5, 这三个数的中位数为5, 所以选择5作为基准)。

思路

  • 随机选择数组的一个元素,比如 6 为基准,拆分数组同时引入一个初始指针,也叫分区指示器,初始指针指向 -1
  • 将数组中的元素和基准数遍历比较
  • 若当前元素大于基准数,不做任何变化
  • 若当前元素小于等于基准数时,分割指示器右移一位,同时
    • 当前元素下标小于等于分区指示器时,当前元素保持不动
    • 当前元素下标大于分区指示器时,当前元素和分区指示器所指元素交换

快速排序

荷兰国旗问题

荷兰的国旗是由红白蓝三种颜色构成,如图:

若现在给一个随机的图形,如下:

image-20210803151023780

把这些条纹按照颜色排好,红色的在上半部分,白色的在中间部分,蓝色的在下半部分,这类问题称作荷兰国旗问题。

对应LeetCode:颜色分类

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

分析:

假如给定一个数组[8, 3, 6, 2, 5, 1, 7, 5],做如下操作:

1.随机选择数组的一个元素,比如 5 为基准,拆分数组同时引入一个左分区指示器,指向 -1,右分区指示器指向基准数(注:此时的基准数为尾元素)

2.若当前元素大于基准数,右分区指示器左移一位,当前元素和右分区指示器所指元素交换,

索引保持不变

3.若当前元素小于等于基准数时,左分区指示器右移一位,索引右移

  • 当前元素大于等于左分区指示器所指元素,当前元素保持不动
  • 当前元素小于左分区指示器所指元素,交换

简单来说就是,左分区指示器向右移动的过程中,如果遇到大于或等于基准数时,则停止移动,右分区指示器向左移动的过程中,如果遇到小于或等于主元的元素则停止移动。这种操作也叫双向快速排序。

345345

代码实现


public class QuickSort {
    public static final int[] ARRAY = {8, 5, 6, 4, 3, 1, 7, 2};
    public static final int[] ARRAY2 = {8, 3, 6, 2, 5, 1, 7, 5};
    private static int[] sort(int[] array, int left, int right) {
        if (array.length < 1 || left > right) return null;
        //拆分
        int partitionIndex = partition(array, left, right);
        //递归
        if (partitionIndex > left) {
            sort(array, left, partitionIndex - 1);
        }
        if (partitionIndex < right) {
            sort(array, partitionIndex + 1, right);
        }
        return array;
    }
    
    public static int partition(int[] array, int left, int right) {
        //基准数下标---随机方式取值,也就是数组的长度随机1-8之间
        int pivot = (int) (left + Math.random() * (right - left + 1));
        //分区指示器索引
        int partitionIndex = left - 1;
        //基准数和尾部元素交换
        swap(array, pivot, right);
        //按照规定,如果当前元素大于基准数不做任何操作;
        //小于基准数,分区指示器右移,且当前元素的索引大于分区指示器,交换
        for (int i = left; i <= right; i++) {
            if (array[i] <= array[right]) {//当前元素小于等于基准数
                partitionIndex++;
                if (i > partitionIndex) {//当前元素的索引大于分区指示器
                    //交换
                    swap(array, i, partitionIndex);
                }
            }
        }
        return partitionIndex;
    }
    
    public static int partitionTwoWay(int[] array, int left, int right) {
        //基准数
        int pivot = array[right];
        //左分区指示器索引
        int leftIndex = left - 1;
        //右分区指示器索引
        int rightIndex = right;
        //索引
        int index = left;
        while (index < rightIndex) {
            //若当前元素大于基准数,右分区指示器左移一位,当前元素和右分区指示器所指元素交换,索引保持不变
            if (array[index] > pivot) {
                swap(array, index, --rightIndex);
            } else if (array[index] <= pivot) {//当前元素小于等于基准数时,左分割指示器右移一位,索引右移
                leftIndex++;
                index++;
                //当前元素小于等于左分区指示器所指元素,交换
                if (array[index] < array[leftIndex]) {
                    swap(array, index, leftIndex);
                }
            }
        }
        //索引和 L 指向同一个元素
        swap(array, right, rightIndex);
        return 1;
    }
    //交换
    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static void print(int[] array) {
        for (int i : array) {
            System.out.print(i + "  ");
        }
        System.out.println("");
    }

    public static void main(String[] args) {
        print(ARRAY);
        System.out.println("============================================");
        print(sort(ARRAY, 0, ARRAY.length - 1));
        System.out.println("====================双向排序==================");
        print(ARRAY2);
        System.out.println("============================================");
        print(sort(ARRAY2, 0, ARRAY2.length - 1));
    }
}

时间复杂度

在拆分数组的时候可能会出现一种极端的情况,每次拆分的时候,基准数左边的元素个数都为0,而右边都为n-1个。这个时候,就需要拆分n次了。而每次拆分整理的时间复杂度为O(n),所以最坏的时间复杂度为O(n2)。什么意思?举个简单例子:

在不知道初始序列已经有序的情况下进行排序,第1趟排序经过n-1次比较后,将第1个元素仍然定在原来的位置上,并得到一个长度为n-1的子序列;第2趟排序经过n-2次比较后,将第2个元素确定在它原来的位置上,又得到一个长度为n-2的子序列;以此类推,最终总的比较次数:

C(n) = (n-1) + (n-2) + … + 1 = n(n-1)/2

所以最坏的情况下,快速排序的时间复杂度为O(n^2)

而最好的情况就是每次拆分都能够从数组的中间拆分,这样拆分logn次就行了,此时的时间复杂度为O(nlogn)。

而平均时间复杂度,则是假设每次基准数随机,最后算出来的时间复杂度为O(nlogn)

参考:快速排序的时间复杂度与空间复杂度

算法稳定性

通过上面的分析可以知道,在随机取基准数的时候,数据是可能会发生变化的,所以快速排序有不是稳定的情况。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: JAVA十大排序算法之快速排序详解

本文链接: https://www.lsjlt.com/news/133303.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JAVA十大排序算法之快速排序详解
    目录快速排序问题思路荷兰国旗问题代码实现时间复杂度算法稳定性总结快速排序 快速排序是对冒泡排序的一种改进,也是采用分治法的一个典型的应用。JDK中Arrays的sort()方法,具体...
    99+
    2022-11-12
  • JAVA十大排序算法之堆排序详解
    目录堆排序知识补充二叉树满二叉树完全二叉树二叉堆代码实现时间复杂度算法稳定性思考总结堆排序 这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆。它具有以下特点: ...
    99+
    2022-11-12
  • JAVA十大排序算法之桶排序详解
    目录桶排序代码实现时间复杂度算法稳定性总结桶排序 桶排序是计数排序的升级,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组...
    99+
    2022-11-12
  • JAVA十大排序算法之基数排序详解
    目录基数排序代码实现时间复杂度算法稳定性基数排序 vs 桶排序 vs 计数排序总结基数排序 常见的数据元素一般是由若干位组成的,比如字符串由若干字符组成,整数由若干位0~9数字组成。...
    99+
    2022-11-12
  • JAVA十大排序算法之冒泡排序详解
    目录冒泡排序代码实现代码实现时间复杂度算法稳定性总结冒泡排序 1.从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个 2.对每一对相邻元素作同样的工作,从开始第...
    99+
    2022-11-12
  • JAVA十大排序算法之选择排序详解
    目录选择排序代码实现动图演示代码实现时间复杂度算法稳定性总结选择排序 1.找到数组中最大(或最小)的元素 2.将它和数组的第一个元素交换位置(如果第一个元素就是最大(小)元素那么它就...
    99+
    2022-11-12
  • JAVA十大排序算法之插入排序详解
    目录插入排序代码实现动图演示代码实现时间复杂度算法稳定性总结插入排序 当我们在玩扑克牌的时候,总是在牌堆里面抽取最顶部的一张然后按顺序在手中排列。 插入排序是指在待排序的元素中,假设...
    99+
    2022-11-12
  • JAVA十大排序算法之希尔排序详解
    目录希尔排序代码实现时间复杂度算法稳定性总结希尔排序 一种基于插入排序的快速的排序算法。简单插入排序对于大规模乱序数组很慢,因为元素只能一点一点地从数组的一端移动到另一端。例如,如果...
    99+
    2022-11-12
  • JAVA十大排序算法之归并排序详解
    目录归并排序怎么分怎么治代码实现时间复杂度算法稳定性总结归并排序 归并,指合并,合在一起。归并排序(Merge Sort)是建立在归并操作上的一种排序算法。其主要思想是分而治之。什么...
    99+
    2022-11-12
  • JAVA十大排序算法之计数排序详解
    目录计数排序问题代码实现时间复杂度算法稳定性总结计数排序 一种非比较排序。计数排序对一定范围内的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进...
    99+
    2022-11-12
  • java 排序算法之快速排序
    目录简单介绍基本思想思路分析代码实现推导实现完整实现大数据量耗时测试性能分析简单介绍 快速排序(Quicksort) 是对 冒泡排序的一种改进。 基本思想 快速排序算法通过多次比较和...
    99+
    2022-11-12
  • Java十大排序算法之堆排序刨析
    二叉堆是完全二叉树或者是近似完全二叉树。 二叉堆满足二个特性︰ 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。 2.每个结点的左子树和右子树都是一个二叉堆(都是最...
    99+
    2022-11-12
  • Java 十大排序算法之希尔排序刨析
    目录希尔排序原理希尔排序的API设计希尔排序的代码实现希尔排序是插入排序的一种,又称"缩小增量排序”,是插入排序算法的一种更高效的改进版本。 希尔排序原理 1.选定一个增长量h,按照...
    99+
    2022-11-12
  • Java 十大排序算法之归并排序刨析
    目录归并排序原理归并排序API设计归并排序代码实现归并排序的时间复杂度分析归并排序原理 1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元...
    99+
    2022-11-12
  • Java 十大排序算法之插入排序刨析
    目录插入排序原理插入排序API设计插入排序代码实现插入排序的时间复杂度分析插入排序原理 ①把所有元素分成已排序和未排序两组 ②找到未排序组的第一个元素,向已经排序的组中进行插入 ③倒...
    99+
    2022-11-12
  • Java 十大排序算法之选择排序刨析
    目录选择排序原理选择排序API设计选择排序代码实现选择排序的时间复杂度选择排序原理 ①假设第一个索引处的元素为最小值,和其他值进行比较,如果当前的索引处的元素大于其他某个索引处的值,...
    99+
    2022-11-12
  • Java 十大排序算法之冒泡排序刨析
    目录冒泡排序原理冒泡排序API设计冒泡排序的代码实现冒泡排序的时间复杂度分析冒泡排序原理 ①比较相邻的元素,如果前一个元素比后一个元素大,则交换这两个元素的位置 ②对每一对相邻的元素...
    99+
    2022-11-12
  • Java十大排序算法之计数排序刨析
    计数排序是非比较的排序算法,用辅助数组对数组中出现的数字计数,元素转下标,下标转元素 计数排序优缺点 优点:快 缺点:数据范围很大,比较稀疏,会导致辅助空间很大,造成空间的浪费 使用...
    99+
    2022-11-12
  • TypeScript实现十大排序算法之归并排序示例详解
    目录一. 归并排序的定义二. 归并排序的流程三. 归并排序的图解四. 归并排序的代码五. 归并排序的时间复杂度六. 归并排序的总结一. 归并排序的定义 归并排序(merge sor...
    99+
    2023-02-23
    TypeScript算法归并排序 TypeScript算法
  • TypeScript十大排序算法之选择排序实现示例详解
    目录一. 选择排序的定义二. 选择排序的流程三. 选择排序的图解四. 选择排序的代码五. 选择排序的时间复杂度六. 选择排序的总结一. 选择排序的定义 选择排序(Selection...
    99+
    2023-02-23
    TypeScript 选择排序算法 TypeScript 算法
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作