快速排序QuickSort

news2024/10/7 4:34:03

目录

1.Hoare法

2.挖坑法

3.前后指针法

4.快排分治

5.关于快排

 6.关于快排的优化

7.总体实现

总结:


快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法

其基本思想为:任待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。(分治思想)

概括快排的思想:
找一个key(基准值),通过交换,使key的左边全小于key,右边全部大于key。这样key就在正确的位置上。重复该过程,直到所有的值都在正确位置上,完成排序。

对比选择排序、插入排序,每次调整无非就第一个元素和最后一个元素在正确位置上。快速排序的单趟排序,可以将调整中间元素,类似一个二叉树的结构。

快排具有高效和广泛的使用,它是最经典的排序之一,并且在各种编程语言的算法库中被广泛使用。

1.Hoare法

Hoare法就是快速排序的创始人Hoare最初的方法,是目前快排中学习成本最高的方法,让我来带大家领略Hoare大佬的思想吧。


 

单趟排序思想:

左边或者右边第一个位置当作key的位置,本文以左边为例。

双指针Left和Right。Right先走,Right找小,找到了停下。Left再走,Left找大,找到了停下,交换Left和Right的值。Right再走找小,Left找到,交换......直到二者相遇,交换key和相遇点的值。这样的一趟排序后key就在正确的位置上。

下图解释单趟排序的过程:

本趟排序,使左边的元素都比key小,右边的元素都比key大。从而使key在正确的位置上。

单趟排序的实现:

// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
    int keyi = left;
    while (left < right)
    {
        while (left < right && a[right] >= a[keyi])
        {
            right--;
        }
        while (left < right && a[left] <= a[keyi])
        {
            left++;
        }
        Sweap(&a[left], &a[right]);
    }
    Sweap(&a[keyi], &a[left]);
    keyi = left;
    return keyi;
}

 关于单趟排序的俩个问题:

1.a[right]一定要>=a[key]?a[right]>a[keyi]行吗?

不行。当出现L 和 R同时遇到与K相同的元素时,会出现死循环。

 2.如果没有越界条件left<right

会出现栈溢出。如果出现极端情况下,key是最小的数,R就会一直减,出现栈溢出。

Hoare法的快速排序是坑比较多,但是他的思想是非常的高超。领略Hoare的方法,在理解代码起巨大的作用。

2.挖坑法

找一个key为坑,假定左边第一个元素为坑,R.L双指针指向左右,R找小,把坑的值填给R中,R形成新坑,L找大,直到L R相遇,把key值给坑。key就在正确位置上

下面动图展示:

 由于挖坑法的思路较简单,就不画图演示。

下面展示挖坑法的单趟实现:

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
    int key = a[left];
    int hole = left;
    while (left < right)
    {
        while (left < right && a[right] >= key)
        {
            right--;
        }
        a[hole] = a[right];
        hole = right;

        while (left < right && a[left] <= key)
        {
            left++;
        }
        a[hole] = a[left];
        hole = left;
    }
    a[hole] = key;
    return hole;
}

 

 挖坑法与Hoare的思想差不多,二者殊途同归。

3.前后指针法

前后指针法的思路:定义key prev cur指针

cur找小,找到了++prev 交换prev和cur的值。cur再走,在交换,直到cur>=n,交换prev和key的值

下面为动图演示:

单趟排序画图详解:

 从图中可以看出,将大的元素翻滚式的推到后面。使左边的值全部小于key,右边的值全部大于key。从而key在正确的位置上。

前后指针法单趟排序实现:

// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
    int keyi = left;
    int prev = left;
    int cur = left + 1;
    while (cur <= right)
    {
        if (a[cur] < a[keyi] && ++prev != cur)
        {
            Sweap(&a[cur], &a[prev]);
        }
        ++cur;
    }
    Sweap(&a[keyi], &a[prev]);
    keyi = prev;
    return keyi;
}

前后指针法的实现较为简单,如果能熟练掌握前俩种方法,那么第三种方法便游刃有余。

关于Hoare法的思考:

L 和 R相遇点,会比key大吗?

  不会。相遇的值一定小于key。

1)假设是key在左边,R去遇L,L不动,存在俩种情况 

1.L一直没动,就在key的位置 ,可以直接交换

2.L经过上一轮交换后,已经比key小,相遇后交换必定比key小,可以直接交换

.

2)L去遇R,由于每一轮都是R先走,R找小 找到停下,L去遇R,相遇点就是R停下的位置,一定比key小,可以直接交换。

4.快排分治

每一趟排序后,key在正确位置上,即将数组分成[Left,keyi-1]  keyi  [keyi+1,Right]

后续就要对[Left,keyi-1]  和 [keyi+1,Right]排序即可。

void QuickSort(int* a, int left, int right)
{
    if (left >= right)
        return;

    int keyi = PartSort2(a, left, right);
    QuickSort(a, left, keyi - 1);
    QuickSort(a, keyi + 1, right);

}

类似二叉树的前序遍历结构

当最小分割只有一个元素时,(即条件left>=right )停止递归。

5.关于快排

1)时间复杂度

单趟排序:类似满二叉树,树的高度为logN  

整体排序:对N个元素排序,N

总的时间复杂度O(N)=N*log(N)

2)空间复杂度:

最大二叉树的叶子结点为N 空间复杂度为N

3)稳定性

不稳定。快排是通过交换 

例如  3  3  4   2  6  6  单趟排序完 俩个3的次序会发生改变

快排是不稳定的算法。

 6.关于快排的优化

如果key为最小的元素,那么每趟排序都要经过N次 总的时间复杂度为N^2

引入三数取中,找到mid left right 中间大的元素,交换后 以它做key

int GetMidIndex(int *a,int left, int right)
{
    int mid = (left + right) / 2;
    if (a[left] > a[mid])
    {
        if (a[mid] > a[right])
            return mid;
        else if (a[right] > a[left])
            return left;
        else
            return right;
    }
    else //a[left]<a[mid]
    {
        if (a[mid] < a[right])
            return mid;
        else if (a[right] < a[left])
            return left;
        else
            return right;
    }
}

7.总体实现

void Sweap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}



int GetMidIndex(int *a,int left, int right)
{
    int mid = (left + right) / 2;
    if (a[left] > a[mid])
    {
        if (a[mid] > a[right])
            return mid;
        else if (a[right] > a[left])
            return left;
        else
            return right;
    }
    else //a[left]<a[mid]
    {
        if (a[mid] < a[right])
            return mid;
        else if (a[right] < a[left])
            return left;
        else
            return right;
    }
}

// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
    int mid = GetMidIndex(a, left, right);
    Sweap(&a[left], &a[mid]);
    
    int keyi = left;
    while (left < right)
    {
        while (left < right && a[right] >= a[keyi])
        {
            right--;
        }
        while (left < right && a[left] <= a[keyi])
        {
            left++;
        }
        Sweap(&a[left], &a[right]);
    }
    Sweap(&a[keyi], &a[left]);
    keyi = left;
    return keyi;
}
// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
    int mid = GetMidIndex(a, left, right);
    Sweap(&a[left], &a[mid]);
    int key=a[left];
    int hole = left;
    while (left < right)
    {
        while (left < right && a[right] >= key)
        {
            right--;
        }
        a[hole] = a[right];
        hole = right;

        while (left < right && a[left] <= key)
        {
            left++;
        }
        a[hole] = a[left];
        hole = left;
    }
    a[hole] = key;
    return hole;
}
// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
    int mid = GetMidIndex(a, left, right);
    Sweap(&a[left], &a[mid]);
    int keyi = left;
    int prev = left;
    int cur = left + 1;
    while (cur <= right)
    {
        if (a[cur] < a[keyi]&&++prev!=cur)
        {
            Sweap(&a[cur], &a[prev]);
        }
        ++cur;
    }
    Sweap(&a[keyi], &a[prev]);
    keyi = prev;
    return keyi;
}


void QuickSort(int* a, int left, int right)
{
    if (left >= right)
        return;

    int keyi = PartSort2(a, left, right);
    QuickSort(a, left, keyi - 1);
    QuickSort(a, keyi + 1, right);

}

总结:

关于快排的书写,重点是画好图,多对单趟排序模拟。

Hoare法的难度较大,要多留意。在面试时选择题经常会出现快排的思想。

作者水平有限,如有问题,欢迎探讨。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/778987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《5.linux驱动开发-第2部分-5.2.字符设备驱动基础》5.2.5.用开发板来调试模块

1. 首先 开发板 可以运行 Uboot 2. Ubuntu 安装好了 t f t p(启动内核zImage) 和 NFS &#xff08;挂载 根文件系统&#xff09; 3. 提前 制作好了 根文件系统&#xff08;2022年做的&#xff0c;早就忘记 怎么做了&#xff09; 4.内核 需要设置 nfs 作为根文件系统 启动…

聊聊spring-cloud的负载均衡

聊聊spring-cloud的负载均衡 1. 选择合适的负载均衡算法2. 合理设置超时时间3. 缓存服务实例列表4. 使用断路器5. 使用缓存Spring Cloud负载均衡组件对比RibbonLoadBalancerWebClient对比 总结 在微服务架构中&#xff0c;负载均衡是非常重要的一个环节&#xff0c;可以有效地提…

ES6基础知识三:对象新增了哪些扩展?

一、属性的简写 ES6中&#xff0c;当对象键名与对应值名相等的时候&#xff0c;可以进行简写 const baz {foo:foo}// 等同于 const baz {foo}方法也能够进行简写 const o {method() {return "Hello!";} };// 等同于const o {method: function() {return "…

C# List 详解四

目录 18.FindLast(Predicate) 19.FindLastIndex(Int32, Int32, Predicate) 20.FindLastIndex(Int32, Predicate) 21.FindLastIndex(Predicate) 22.ForEach(Action) 23.GetEnumerator() 24.GetHashCode() 25.GetRange(Int32, Int32) C#…

协作实现时序数据高效流转链路 | 7.20 IoTDB X RocketMQ 技术沙龙线上直播回顾

7 月 20 日&#xff0c;IoTDB X RocketMQ 技术沙龙线上直播圆满结束。工业物联网时序数据库研发商天谋科技、云原生事件流平台 Apache RocketMQ 社区的四位技术专家&#xff0c;针对实时数据接入、多样数据处理与系统的高扩展、高可靠特性的数据流转处理平台实现难点&#xff0…

计算机服务器被devos勒索病毒攻击怎么解决,数据库解密恢复方式

科学技术的发展为企业的生产运行提供了极大的便利性&#xff0c;但随之而来的网络安全也应该引起人们的重视。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的计算机服务器内的数据库被devos后缀勒索病毒攻击&#xff0c;导致企业许多工作无法正常运行。Devos后缀勒…

89、简述RabbitMQ的架构设计

简述RabbitMQ的架构设计 BrokerQueueExchangeRoutingKeyBinding信道架构设计图 Broker RabbitMQ的服务节点 Queue 队列&#xff0c;是RabbitMQ的内部对象&#xff0c;用于存储消息。RabbitMQ中消息只能存储在队列中。生产者投递消息到队列&#xff0c;消费者从队列中获取消息…

Sql Developer日期显示格式问题

sqldeveloper模式日期显示不是很美观 并且使用日期条件查询需要将月份转为中文&#xff0c;系统兼容性差 容易以前如下报错 ORA-01861: 文字与格式字符串不匹配 01861. 00000 - "literal does not match format string"-- sqldeveloper 中执行日期条件 &#xff08;…

2023杭电多校第二场1007-foreverlasting and fried-chicken

链接&#xff1a;Problem - 7293 (hdu.edu.cn) 思路&#xff1a; 枚举度大于4 和 6 且 共同连接 4个以上点 的两个点, 其度分别记为a 和 b 若a为上面的点, 那么答案为C(a-4, 2) * C(b, 4), 反之同理 如果直接搜点会tle, 此时用bitset优化, 状态压缩, 时间复杂度为O(n^3 /32) …

只需3步,使用Stable Diffusion无限生产AI数字人视频

效果演示 先看效果&#xff0c;感兴趣的可以继续读下去。 没有找到可以上传视频的地方&#xff0c;大家打开这个网盘链接观看&#xff1a;https://www.aliyundrive.com/s/CRBm5NL3xAE 基本方法 搞一张照片&#xff0c;搞一段语音&#xff0c;合成照片和语音&#xff0c;同…

APP抓包-资产获取+Frida反代理绕过和证书校验绕过

app抓包获取资产 1.打开模拟器&#xff0c;和电脑连接同一个wifi&#xff0c;让模拟器和电脑处于同一局域网&#xff0c;在模拟器配置代理。 burp开启监听 模拟器开启app&#xff0c;burp成功获取资产信息 有时候明明配置没问题&#xff0c;为什么抓不到app数据包呢&#xff1f…

Folx Pro 5 最好用的Mac磁力链接BT种子下载工具

除了迅雷&#xff0c;还有哪个支持磁力链接下载&#xff1f;Mac电脑如何下载磁力链接&#xff1f;经常有小伙伴问老宅。今天&#xff0c;老宅给大家推荐Folx Pro For Mac&#xff0c;Mac系统超好用的磁力下载工具。 Folx是一款功能强大且易于使用的Mac下载管理器&#xff0c;并…

ChatGPT开放自定义系统级别的指令,可设置偏好变成专属助理

OpenAI官方消息https://openai.com/blog/custom-instructions-for-chatgpt OpenAI为其大型语言模型接口ChatGPT引入了自定义指令&#xff0c;旨在为用户提供更加量身定制和个性化的体验&#xff0c;可以设置您的偏好&#xff0c;ChatGPT将在未来的所有对话中记住它们。 该功…

linux:cloudfare证书申请及应用到nginx

参考&#xff1a; 免费申请网站SSL证书 有效期15年 全站开启https_哔哩哔哩_bilibili 总结&#xff1a; 登陆www.cloudfare.com 注册账号 Add a Site 增加站点 站点设置完毕后Add record 记住这个Proxy status一定要勾选&#xff0c;这是cloudfare代理https请求转发到你的服务器…

Java开发基础系列(十):异常处理

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; Java开发基础系列(十):异常处理 ⏱️ 创作时间&#xff1a; 2023年07月…

深度学习——生成对抗网络GAN

基本概念 概述 GAN是一种深度学习模型&#xff0c;它是一种无监督学习算法&#xff0c;用于从随机噪声中生成逼真的数据&#xff0c;比如图像、音频、文本等。GAN的结构由两个神经网络组成&#xff1a;生成器&#xff08;Generator&#xff09;和判别器&#xff08;Discrimin…

谈谈面试大厂中碰到的问题

面试IT公司的小技巧 非常不建议在简历上造假&#xff0c;简历上能起到关键作用、有分量的部分&#xff0c;别人都是有办法去核实的&#xff0c;比如教育背景、关键性的证书、奖项等&#xff1b;核实不了的&#xff0c;又基本上也对结果产生不了太大影响&#xff0c;又何必去画…

USG6000v防火墙的基本使用:制定安全策略让不同安全区域的设备进行访问

目录 一、首先配置环境&#xff1a; 二、实验拓扑及说明 拓扑&#xff1a; PC1和PC2配置ip地址&#xff1a;​编辑​编辑 r4路由器配置ip&#xff1a; 进行防火墙的设置&#xff1a; 1、创建trust1区域和untrust1区域 2、制定防火墙的策略&#xff1a; 3、为防火墙增加可以…

【PostgreSQL内核学习(四)—— 查询规划】

查询规划 查询规划总体处理流程pg_plan_queries函数standard_planner函数subquery_planner函数inheritance_planner函数grouping_planner函数 总结 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求…

SpringBoot原理分析 | 任务:异步、邮件、定时

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 任务 异步任务 Java异步指的是在程序执行过程中&#xff0c;某些任务可以在后台进行&#xff0c;而不会阻塞程序的执行。通常情况下&#xff0c;Java异步使用线程池来…