详解十大经典排序算法(六):快速排序(QuickSort)

经典算法—快速排序(Quicksort)使用详解 快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第 阅读详情

算法原理

  1. 分区(Partition): 选择一个基准元素,将数组分为两个子数组,小于基准的放在左边,大于基2准的放在右边。
  2. 递归排序: 对左右两个子数组分别进行快速排序。
  3. 合并: 不需要实际的合并操作,因为在分解和递归排序阶段已经完成了排序。

算法描述

快速排序是一种基于分治思想的高效排序算法,由英国计算机科学家 Tony Hoare 于1960年提出。它的核心思想是选择一个基准元素,将数组分成两个子数组,小于基准的在左边,大于基准的在右边,然后对子数组进行递归排序。这一过程持续进行,直到整个数组有序。

算法原理可以简要概括为以下步骤:

  1. 选择基准元素: 从待排序的数组中选择一个元素作为基准(pivot)。选择基准的方法有多种,常见的包括选择第一个元素、最后一个元素、中间元素,或者随机选择一个元素。

  2. 分区过程: 将数组中的其他元素按照与基准的大小关系划分到基准的两侧,使得基准左边的元素都小于基准,右边的元素都大于基准。这个过程称为分区(partition)。具体的分区算法有多种,其中一种常见的是Lomuto分区方案和Hoare分区方案。

  • Lomuto分区方案:
    a. 从左到右遍历数组,将比基准小的元素放到基准的左侧。
    b. 使用一个索引i来记录当前小于基准的元素的位置,初始时i指向数组的起始位置。
    c. 遇到小于基准的元素时,将它与索引i指向的元素交换,并将i递增。
    d. 最后,将基准元素与索引i指向的元素交换,这样基准元素就放置在了其最终的位置。

  • Hoare分区方案:
    a. 使用两个指针,一个从数组的左端移动到右端,另一个从右端移动到左端,分别找到大于和小于基准的元素。
    b. 交换这两个元素的位置。
    c. 重复这个过程,直到两个指针相遇。最后,基准元素的位置就是两指针相遇的位置。

  1. 递归排序: 对基准左右两侧的子数组分别递归地应用快速排序算法。即,对左侧的子数组和右侧的子数组分别进行步骤1和步骤2,直到每个子数组只有一个元素,此时整个数组就是有序的。

  2. 合并结果:不需要实际的合并操作,因为在分区和递归排序阶段已经完成了排序。

动画演示

在这里插入图片描述
能看懂动画你就理解了快速排序

好吧,可能这个图不太好理解,看个视频讲解把: B站小姐姐讲解快速排序>>>

代码实现

   public static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            // 获取分区索引
            int partitionIndex = partition(arr, low, high);

            // 递归排序左右子数组
            quickSort(arr, low, partitionIndex - 1);
            quickSort(arr, partitionIndex + 1, high);
        }
    }

    private static int partition(int[] arr, int low, int high) {
        // 选择最右边的元素作为基准
        int pivot = arr[high];

        // 将小于基准的元素移动到左边,大于基准的元素移动到右边
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr, i, j);
            }
        }

        // 将基准元素放到正确的位置
        swap(arr, i + 1, high);
        return i + 1;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

算法复杂度

时间复杂度(最坏)时间复杂度(最好)时间复杂度(平均)空间复杂度稳定性
O(n^2)O(n log n)O(n log n)O(log n)不稳定
  • 最好情况:在最优情况下,快速排序的时间复杂度是O(n log n)。这通常发生在每次分区都能将数组均匀地分成两半的情况下。
  • 平均情况:在平均情况下,快速排序的时间复杂度是O(n log n)。这是因为每次分区都将数组分成两部分,每一部分都需要O(log n)次分区操作。
  • 最坏情况:最坏情况下的时间复杂度是O(n^2),当每次分区都选择最大或最小的元素作为基准时,会导致分区不均衡。
  • 空间复杂度:快速排序是一种原地排序算法,它不需要额外的存储空间来存储临时数据。因此,其空间复杂度是O(1)。这是因为所有的操作都是在输入数组上进行的,只需要常数级别的辅助空间用于存储一些变量,如循环索引、基准元素等。

快速排序的优化方式

快速排序是一种高效的排序算法,但在某些情况下可能面临最坏情况,即分区不均衡,导致性能下降。

  1. 随机选择基准元素:
    在每次排序开始时,随机选择数组中的一个元素作为基准元素,而不是固定选择第一个或最后一个元素。这可以避免在某些情况下导致最坏情况发生。
 public static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            // 随机选择基准元素
            int randomIndex = getRandomIndex(low, high);
            swap(arr, randomIndex, high);

            int partitionIndex = partition(arr, low, high);

            quickSort(arr, low, partitionIndex - 1);
            quickSort(arr, partitionIndex + 1, high);
        }
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr, i, j);
            }
        }
        swap(arr, i + 1, high);
        return i + 1;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    private static int getRandomIndex(int low, int high) {
        Random random = new Random();
        return random.nextInt(high - low + 1) + low;
    }

  1. 三数取中法:
    选择基准元素时,不是简单地选择第一个或最后一个元素,而是选择第一个、中间和最后一个元素中的中间值作为基准元素。
public static void quickSort(int[] arr, int low, int high) {
    if (low < high) {
        // 三数取中法选择基准元素
        int mid = low + (high - low) / 2;
        int pivotIndex = getMedianIndex(arr, low, mid, high);
        swap(arr, pivotIndex, high);

        int partitionIndex = partition(arr, low, high);

        quickSort(arr, low, partitionIndex - 1);
        quickSort(arr, partitionIndex + 1, high);
    }
}

private static int getMedianIndex(int[] arr, int i, int j, int k) {
    if ((arr[i] - arr[j]) * (arr[k] - arr[i]) >= 0) {
        return i;
    } else if ((arr[j] - arr[i]) * (arr[k] - arr[j]) >= 0) {
        return j;
    } else {
        return k;
    }
}

这两种优化方式可以在某些情况下提高快速排序的性能,特别是在面对特定输入数据的时候。随机选择基准和三数取中法都有助于减少最坏情况的发生概率,提高算法的鲁棒性。


相关概念

• 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
• 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
• 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
• 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

Ultra-QuickSort 如果题目中的“Ultra-QuickSort”指的是某种更高效的排序算法,则此代码可能无法正确模拟。但根据题目的描述,这个算法仅通过交换相邻元素进行排序,所以以上代码应该符合题意。请注意,真正的快速排序通常不会按照这种方式(只交换相邻元素)进行操作,其交换元素的距离可能远大于相邻位置。实际的快速排序平均时间复杂度为O(n log n),而上述代码的时间复杂度是O(n^2)。由于题目中没有具体说明Ultra-QuickSort算法的具体实现细节,我们将假设它始终将较小的元素交换到前面(类似于冒泡排序)。 阅读详情

相关推荐

C# 快速排序详解:7步带你彻底掌握Quicksort

通过本文的详细讲解和代码示例,相信你已经掌握了快速排序的基本原理和实现方法。快速排序是一种非常高效的排序算法,通过合理的优化,可以在各种场景中发挥出色的表现。如果你有任何问题或建议,欢迎在评论区留言交流!希望这篇教程对你有所帮助,期待你在C#编程中取得更大的成就!🌟✨😊✨。

java专栏 1614

数组排序算法——快速排序(Quick)算法精讲及python实现

在这种选择下,每一次都将序列从 $n$ 个元素变为 $\frac{n}{2}$ 个元素。每一次选择的基准数都是当前序列的中位数,此时算法时间复杂度满足的递推式为 $T(n) = 2 \times T(\frac{n}{2}) + \Theta(n)$,由主定理可得 $T(n) = O(n \times \log_2n)$。每一次选择的基准数都是序列的最终位置上的值,此时算法时间复杂度满足的递推式为 $T(n) = T(n - 1) + \Theta(n)$,累加可得 $T(n) = O(n^2)$。

白话机器学习 2120

排序算法——快速排序 Quicksort

快速排序(Quicksort)是一种很高效的排序算法,不管是在教学还是在实际应用上,它的使用都非常广泛。Quicksort算法的时间复杂度平均为O(nLogn),它很适合用于大量数据的排序,而且其算法原理也很简单。 快速排序是对冒泡排序的一种改进,它的基本思想是,通过一趟排序将待排序序列分割成独立的两个子序列,其中一个序列的值比另一序列的小,然后对这两个序列继续进行排序,直到整个序列有序。假设输入子数组为a[p:r],则其的快排过程如下: (1)分解(divide):以a[p]为基准元素将a[p:r]划

愿君出走半生,归来时仍是少年 445

java排序算法之快速排序

一、排序算法概述 常用的内部排序方法有:交换排序(冒泡排序、快速排序)、选择排序(简单选择排序、堆排序)、插入排序(直接插入排序、希尔排序)、归并排序、基数排序(一关键字、多关键字)。 所需辅助空间最多:归并排序 所需辅助空间最少:堆排序 平均速度最快:快速排序 不稳定:快速排序,希尔排序,堆排序。 选择排序算法的依据:任何排序算法在数据量小的时候基本体现不出来差距。选择依据有 1.数...

孔子说 651

手教你如何使用Simulink和相关工具箱模拟和实现无人机的多机接力信号中继的建模

扩大通信范围:单架无人机的通信能力有限,通过多机接力的方式可以显著扩展通信覆盖范围。提高可靠性:当某架无人机出现故障或电量不足时,其他无人机可以接管其任务,确保通信不中断。适应复杂环境:在山区、森林等复杂地形中,多机接力可以绕过障碍物,建立稳定的通信链路。使用Communications Toolbox中的模块来模拟无线通信链路。例如,可以使用模块模拟噪声信道,或者使用模块计算信号衰减。matlab深色版本。

MHD0815的博客 766

set_output_delay的本质

set_output_delay的本质

qq_33300585的博客 3387

快速排序的几种pivot基数选择方法

随机基数快排:随机选取index,然后和最左或者最右元素交换,转换成已解决的问题。随机基数下我们期望平均情况下对数组的paritition比较均衡,并且对一些极端例子的最坏情况下的处理会更好。(主要是最后pivot是和i还是i+1交换,注意到 i作为维持partition的分界线,i 左边的元素都比pivot小,i 右边的元素都比pivot大。以右pivot为例子,与右pivot交换的必定是i右边的元素,否则的话不能维持右侧parition都大于pivot的性质)partition中的index非常易错。.

qq_61936207的博客 863

RK3568/RK3588之修改8250驱动实现RS485收发的自动切换

在串口发送前,若为485,则将控制引脚拉为发送状态,在发送后,循环查看是否发送完成,若发送完成则将引脚置回读状态,如此一来,就能实现在内核测控制方向切换,而不会因为延时造成数据丢失,经过测试此办法可行。全部修改完成后,重新编译生成内核文件和设备树文件,烧写到板子上即可。

哒宰的博客 3111

快速排序算法

思路:1.现在数组元素中选出一个基准值(pivot)(比较值) 2.将数组中大于基准值(pivot)的元素统一移到基准值右边 将数组中小于基准值(pivot)的元素统一移到基准值左边 3.用递归的方式分别对基准值(pivot)左右两边的子序列重复前两步操作,完成快速排序 1.main方法调用排序算法对数组进行排序 public class QuickSort { private static int count.........

m0_51697147的博客 496

前导0预测(Leading-Zero Anticipator,LZA)

前导0预测(Leading-Zero Anticipator,LZA) 浮点加法在完成之后会进行尾数规格化移位,而在进行尾数加法时,可以使用前导0预测(Leading-Zero Anticipator,LZA)同时计算移位,前导0预测只发生在一个正数加上一个负数的情况下,电路图如图1所示。 图1 传统LZD算法和LZA算法 1. 基础算法 算法的目标是计算出加法结果序列左边连续0的数量,下文中对...

crw1111111的博客 7374

《Python星球日记》 第73天:情感分析与主题建模

《Python星球日记》 第73天:情感分析与主题建模,今天我们将探索两个自然语言处理领域的重要技术:情感分析和主题建模。这两项技术在社交媒体分析、用户反馈处理、市场研究等多个领域有着广泛应用。

Code_流苏:在代码中寻诗意,在实践中觅真知 919

算法学习 - 快速排序相关

1. 划分 快速排序中最重要的步骤就是划分,即选择一个元素(很多地方称为枢纽元素),将序列划分成两部分,比枢纽元素大的放在枢纽元素的右边,比枢纽元素小的放在枢纽元素的左边。 划分函数的思想: 1. 选择一个枢纽元素(以选择序列的第一个元素为例),暂存到临时变量中 2. 从序列的右边向左查找,找到一个比枢纽元素小的值。若找到,则将这个值填入到枢纽元素的位置 3. 从序列的左边向右查找,找到

翼遥君的博客 1458

34、实数域上高效负循环卷积算法解析

本文介绍了一种高效的实数域负循环卷积算法,通过引入离散傅里叶负循环变换(DFNT)避免传统方法的冗余,显著提升计算效率。文章详细分析了算法原理、误差传播机制,并推导出误差与方差界,提出无误差和基于3σ规则的参数设置方案。结合Cooley-Tukey与Gentleman-Sande FFT路径优化,实验验证了算法在不同精度和多项式长度下的性能优势与结果正确性,为密码学与信号处理等领域提供了高精度、高性能的卷积计算解决方案。

salt的博客 114

c++ 快速排序(QuickSort)

为了缓解最坏的情况,使用了各种技术,例如选择一个好的主元(例如,三的中位数)并使用随机算法(随机快速排序)在排序之前对元素进行混洗。分区的目标是将主元(可以选择任何元素作为主元)放置在已排序数组中的正确位置,并将所有较小的元素放在主元的左侧,将所有较大的元素放在主元的右侧。它不是稳定的排序,这意味着如果两个元素具有相同的键,在快速排序的情况下,它们的相对顺序将不会保留在排序的输出中,因为这里我们根据枢轴的位置交换元素(不考虑它们的原始位置)职位)。QuickSort 中的分区:将枢轴与 80 进行比较。

csdn_aspnet的专栏,请点击博客主页右上角三个点中的私信联系 9万+

多测师肖sir_高级金牌讲师_数据库之存储练习题(8.13.1)

实验 一、 实验要求: 理解存储过程的概念 掌握存储过程的语法格式、使用方法 掌握存 储过程的创建、执行 二、实验前提: – drop table if exists student; – Create table student – (Id varchar(255), #学号 – Name varchar(255), #姓名 – Roomid varchar(255), #班级 – Sex char(1), #性别 – Degree varchar(3) );#成绩; Insert into stud

多测师肖sir的博客 701

SIWAVE+ADS提取PCB走线寄生参数

本文将介绍一种很适合PCB走线提取寄生电感等参数的方法,即是通过SIWAVE将PCB走线的S参数提取出来,再通过ADS将S参数转换成电感、电容值。

sinat_15150363的博客 1763

【从零学习经典算法系列】分治策略实例——快速排序(QuickSort)

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个项目要O(nlogn)次比较。在最坏状况下则需要O(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。 此种排序的思路是:如果在分开的时候,不是从中间位置上分界,二是按照元素的大小分开为两个一大一小的子序列(一个子序列的所有元素大于另一个子序列里的所有元素),这样的话,因为两个子序列之间的相对次序已经正确,所有在合并的时候

Jason Ding的专栏 5250

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

7种经典排序算法详解:从冒泡到快速排序的完整实现指南

GitHub 加速计划 / in / interview 项目是 C/C++ 技术面试基础知识的全面总结,涵盖了排序算法在内的多种数据结构与算法实现。本文将深入解析7种最常用的排序算法原理与实现,帮助开发者掌握排序算法的核心思想与应用场景。 ## 为什么学习排序算法很重要? 排序算法是计算机科学的基础,也是技术面试的高频考点。无论是开发高效数据处理系统,还是解决实际业务问题,掌握排序算法都能帮

gitblog_00516的博客 1033

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

六大排序算法精讲(SelectSort、BubbleSort、InsertSort、MergeSort、QuickSort、HeapSort)

一、前言 最近在看算法公开课,听了很多老师讲了不少很有用的东西,但是只听不练很快就忘了,所以我还是选择以博客的方式巩固自己的学习。 二、排序算法概述 今天主要讲的是比较经典的六大排序算法:选择排序、冒泡排序、插入排序、归并排序、快速排序和堆排序,除此之外还有一个比较重点的基数排序,但是因为它是不基于比较的排序,所以用法不是很广泛,所以我也只讲解它的实现原理,不多做阐述,并且,在讲解之余,我也会穿插一些其他内容。 三、选择排序 选择排序是最基础的一...

少渊的博客 2369

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

快速排序(QuickSort)

快速排序(QuickSort)也是一种排序算法,对包含n个数组的输入数组,最坏情况运行时间为O(n^2)。虽然这个最坏情况运行时间比较差,但是快速排序通常是用于排序的最佳实用选择,这是因为其平均性能相当好,期望的运行时间为O(nlgn),且O(nlgn)中隐含的常数因子很小,另外它还能够进行就地排序在虚拟环境中也能很好的工作。 一、快速排序原理        快速排序也和合并排序一样,基于分治

指尖飞舞 1967

QuickSort 快速排序

quickSort 快速排序算法 借鉴《啊哈!算法》这本萌书和python菜鸟教程的想法。 思想: 挑选基准值:从数列中挑出一个元素,称为"基准"(pivot); 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成; 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元...

weixin_43973433的博客 332

排序算法:快速排序

十大经典排序算法(动图演示) public static void quickSort(int[] a, int start, int end) { if (start &lt; end) { int baseNum = a[start];// 选基准值 int i = start; int j = end; while (i != j) { while (...

Jerry的博客 223

十大排序算法之快速排序(QuickSort)

一、前言 数据结构与算法,顺便刷leetcode,无意间发现了一个 我觉得讲解的比较好的网页,并且会拿leetcode里面的题目当做例题,如有需要,奉上网址:五分钟学算法 二、算法的介绍 选取两个哨兵i,j哨兵i指向序列的最前端,哨兵j指向序列的最后端,选取一个基准点。 判断哨兵i指向的元素是否大于基准点?若小于,则哨兵i后移一位,若大于,则哨兵i动。 判断哨兵j指向的元素是否大于基准点?若大于...

Cxf2018的博客 323

排序算法之快速排序QuickSort

快速排序分为两步:1)分割,2)分别对分割所得两部分进行快速排序 所以重点在于partittion(),下面是java实现 import java.util.Scanner; public class Main { public static void main(String[] args){ Scanner cin = new Scanner(System.in); ...

小黑屋 306

排序算法之快速排序(QuickSort)

排序算法之快速排序(QuickSort) 一.基本思想(分治思想) 核心思想:快速排序是一种 划分交换排序 ,其采用分治的策略思想 。 算法思路: 首先从待排序序列中随机取出一个数作为基准数。 分区过程,将比基准数大的数全放到它的右边,小于或等于基准数的数全放到它的左边。 对左右区间重复第二步,直到各区间只有一个数。 实例说明 如上图所示,参考《算法导论》中给出的示例,对快速排序进行算法演示,待排序序列为A[2,8,7,1,3,5,6,4]: 第一轮:将数组A分为两个子数组,左数组中数均小

m0_49263811的博客 553

蓝桥杯试题 快速排序 QuickSort

蓝桥杯试题 ------快速排序 问题描述   用递归来实现快速排序(quick sort)算法。快速排序算法的基本思路是:假设要对一个数组a进行排序,且a[0] = x。首先对数组中的元素进行调整,使x放在正确的位置上。同时,所有比x小的数都位于它的左边,所有比x大的数都位于它的右边。然后对于左、右两段区域,递归地调用快速排序算法来进行排序。   输入格式:输入只有一行,包括若干个整数(不超过1...

TreeCode的博客 645
上一篇: 详解十大经典排序算法(五):归并排序(Merge Sort)
下一篇: 维护真实时间:应对系统时间篡改的技巧
_蓝橙
博客等级 码龄7年 1179粉丝 174原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值