1. 简介

神说, 要有光. 于是就有了光. 神说要有快排, 于是就有了快排. 

快速排序Quick Sort的发明者 托尼 霍尔 是1980年的图灵奖得主. 快速排序就是他发明的. 当时发明的背景是: 由于霍尔要高效地对俄语词汇进行排序以优化翻译程序, 而当时的排序算法(如冒泡, 插入排序)效率较低, 平均时间复杂度为 O(n2). 霍尔受分治法 (Divide and Conquer)思想启发, 提出了基于 "分区" (Partition) 的递归排序方法. (这段AI获取)

2. 核心思想

分区 (Partition): 指的是选一个基准值 Pivot, 将比基准值小的放在它的左侧, 比它大的放在右侧. 然后, 分别在左边区域 (比基准值小的区域) 和右边区域 (比基准值大的区域) 做相同的操作即再在左右两边选基准值, 再划分为两个区域, 一直到划分的区域即子序列长度为1, 即只有一个元素为止.这个就是快速排序的核心思想.

3.算法实现方式

分区法据我了解到有三种:   (因为很早前写的,细节解释可能有误, 注意甄别)

3.1 第一种是 Naive Partition (朴素分区法)

这种方法我用 C++ 实现过, 不过我是看到某个Python的实现, 我根据它的实现, 用 C++ 翻译而已. 就是我们选一个基准值, 然后遍历元素, 比它小的我们额外存放到一个临时空间中, 比它大的也一样. 然后再将临时空间中的比基准值小的搬回原来的存储空间中, 再放基准值, 然后把比基准值大的放到基准值的右边. 再分别递归处理基准值左边和右边的区域. 如果某一轮没有找到比基准值小或大的值, 则左边或右边不再递归处理.

3.2 第二种是 Lomuto Partition (洛穆托分区法) 

它维护两个指针 (下标), i 和 j . 依次从最左边的元素遍历到最末元素的前一个 (倒数第二个) , 并且选择最末元素为基准值. i 初始值指向 -1, j 指向第一个元素, 如果当前 j 指向的元素比基准值小, 则增加 i 的值, 并交换 i 和 j 指向的元素, 也就是将比基准值小的元素往后摆放. 如果 j 指向的元素比基准值大, 则不动它.  i 增加只在发生交换时增加, j 是每次无论交换与否都要增加.最终 i + 1 的位置就是返回的基准值的位置, 再递归处理 i + 1左边和右边.

3.3 第三种是 Hoare Partition (霍尔分区法)

这也是快排原始版本, 由霍尔本人发明, 好像也是最快的实现方式. 它也维护两个下标 low 和 high, 只是从元素序列的两边分别遍历, 一个往后, 一个往前. 并且一般选第一个元素为基准值. 往后移的下标 low 找到比基准值大于等于的值, 往前移的 high 下标找比基准值小于等于的值, 找到则停止, 此时如果 low 和 high 还没相遇, 则交换它俩. 即将大的往后放, 小的往前放. 若相遇则返回 high下标. 这个 high 下标的位置就是基准值的位置. 然后递归处理基准值左边和右边.

(如果文字抽象或这里没写正确, 可以看实现代码在纸上推演, 推两轮自然明白, 我这里就不画图了, 画图有点耗时间)

4. 代码实现

4.1 第一种朴素分区法

注释也比较详细, 思想就是刚刚介绍的, std::array 模版类是 C++ 中的一种数组/容器, std::vector可能更适合长度经常变化的数组, std::array 数组长度固定, 效率比 std::vector 高, 而 std::array 又封装了一些额外的功能 (什么边界检查,还有一些方法) 比原始数组好用. ( C++的几种数组效率可以用百万级的元素或千万级的元素来测试) . 这个版本虽不能完全算原创, 但也是半原创, 就是根据某个Python实现, 用 C++ 独立完成.

/*快速排序 朴素分区法 ?? C++版 08042024 by losangelx*/
#include <iostream>
#include <array>
const int SIZE = 10;/*快速排序函数,start为排序起始下标,len为要排序的范围*/
bool quickSort(std::array<int, SIZE>& ar, int start, int len);int main(void)
{std::array<int, SIZE> numbers = { 0, 422, 12, 4, 111, 3, 5, 22, 200, 123 };std::array<int, SIZE>::iterator ir; /*array模版类迭代器*/std::cout << "The oringal array: "; /*原始数组*/for (ir = numbers.begin(); ir != numbers.end(); ir++)std::cout << *ir << " ";quickSort(numbers, 0, numbers.size());std::cout << "\nAfter quick sorted: "; /*排序后数组*/for (int x : numbers)std::cout << x << " ";return 0;
}/*快速排序函数,start为排序起始下标,len为要排序的范围*/
bool quickSort(std::array<int, SIZE>& ar, int start, int len)
{if (len == 1) //基线条件:如果长度为1不再递归排序return 1;std::array<int, SIZE> small; /*临时元素存放区*/std::array<int, SIZE> larger;/*small为比基准因子小的元素*/int i, j, k;                 /*larger为比基准因子大的元素*//*将原数组中比基准因子小和大的先搬出来*/for (i = start + 1, j = 0, k = 0; i - start < len; i++)if (ar[i] <= ar[start])small[j++] = ar[i];elselarger[k++] = ar[i];/*基准因子arr[start]最终排在由比它小的元素组成的子*//*数组之后并且在比它大的元素组成的子数组之前的位置上*/ar[start + j] = ar[start];/*如果有比基准因子小的元素则搬回原数组,并对子*//*数组(由比基准因子小的组成)执行相同操作(递归)*/if (j > 0) {int m = 0;for (i = start; i - start < j; i++)ar[i] = small[m++];quickSort(ar, start, j);//只对长度为j的部分排序}                           //即只对原数组比基准因子//小的那部分执行相同操作/*若有比基准因子大的元素则执行相同操作*/if (k > 0) {int n = 0;for (i = start + j + 1; (i - (start + j + 1)) < k; i++)ar[i] = larger[n++];quickSort(ar, start+j+1, k);}return 1; /*排序完成返回*/
}

我用 Dev C++ 编译这个代码遇到下面错误, 这里是要在dev c++中添加 C++ 11 支持才行, std::array是 C++ 11 才引入的. 如果你用 VS 应该不用.

#ifndef _CXX0X_WARNING_H
#define _CXX0X_WARNING_H 1

#if __cplusplus < 201103L
#error This file requires compiler and library support for the \
ISO C++ 2011 standard. This support is currently experimental, and must be \
enabled with the -std=c++11 or -std=gnu++11 compiler options.
#endif

#endif

4.1.1 运行结果:

4.2 第二种和第三种 Lomuto 分区法和 Hoare 分区法

/* Quick Sort 快速排序 本程序使用Lomuto/Hoare两种分区法实现 */
#include <stdio.h>
#define MAX 10void quicksort(long arr[], int low, int high);    /* 快速排序主函数 */
int hoare_parti(long arr[], int low, int high);   /* 霍尔分区法     */
int lomuto_parti(long arr[], int low, int high);  /* 洛穆托分区法   */
void swap(long *pl, long *ph);
void printarr(long arr[], int len);
int main(void)
{long array[MAX] = {50, 80, 10, 70, 100,30, 90, 40, 60, 20};printarr(array, MAX);printf("\n");quicksort(array, 0, MAX - 1);printarr(array, MAX);printf("\n");return 0;
}void quicksort(long arr[], int low, int high)
{if (low < high) {  /* 递归基线条件: low与high还没相遇才继续 */int pindex = lomuto_parti(arr, low, high);  /* 这里可选Hoare分区法或洛穆托分区法 */quicksort(arr, low, pindex - 1);     /* 递归处理左右两边的序列 */quicksort(arr, pindex + 1, high);}
}/* Hoare分区法 */
int hoare_parti(long arr[], int low, int high)
{long pivot = arr[low];                      /* 选第一个为基准值 */while (1) {while (arr[low] < pivot) { low++; }     /* 直到找到比基准值大的停下 */while (arr[high] > pivot) { high--; }   /* 直到找到比基准值小的停下 */if (low >= high) { return high; }       /* low和high没相遇继续否则返回基准值位置 */swap(&arr[low], &arr[high]);            /* 交换比基准值大的到后面反之放前面 */}
}/* Lomuto分区法 */
int lomuto_parti(long arr[], int low, int high)
{int i, j;long pivot = arr[high];                     /* 选最后一个元素为基准值 */i = low - 1;for (j = low; j < high; j++) {              /* i开始指向比基准值小的后一个 */if (arr[j] <= pivot) {                  /* j从第一个开始, 若小于等于基准值 */i++;swap(&arr[i], &arr[j]);             /* 则交换即将小的往前放,大的往后放 */}}swap(&arr[i + 1], &arr[high]);              /* 将基准值放到适当位置 */return (i + 1);                             /* 返回基准值位置 */
}void printarr(long arr[], int len)
{int i;for (i = 0; i < len; i++) {printf("%ld ", arr[i]);}
}void swap(long *pl, long *ph)
{long temp = *pl;*pl = *ph;*ph = temp;
}

上面两种分区法都测试通过了. 不过如果Dev C++编译选项还有 C++ 11的话, 会有警告, 应该关闭.因为这是 C. (C 和 C++ 有时候一样, 但有些特性还是不太一样, C++ 复杂一些,据大老C++之父本贾尼的说法是 C 的语法检查太松散. 所以C++检查也更严格)

4.2.1 运行结果:

注意: 以上的三种实现方式代码如果有错误注意甄别, 我并未仔细测试, 通过即停.

5. 结语

快速排序Quick Sort 还有多种优化方式, 感兴趣的读者可以研究一下. 例如基准值的选择就有多种不同的算法, 比如选第一个和中间, 还有最后一个元素三个取其一等等. 优化空间很大. 如果这篇文章缺少图示, 就自己在纸上根据代码推演, 推两盘就立刻明白是如何实现排序的了.

关于快速排序详细的时间复杂度分析请搜相关文章了解,不同分区方式它的时间复杂度有些区别,当然朴素分区因为要递归创建临时数组并且要搬运,所以是最慢的。而洛穆托和霍尔分区法因为是在原始数组上处理不用创建临时空间和搬运要快点,霍尔分区法因为是从两头往中间同时双向遍历所以最快,比洛穆托的要快。

这里我也人云亦云的给岀快排平均时间复杂度函数是 n * log n,其中 n 是元素个数。这个函数是对数,随着处理元素数量的增加,函数值增量趋势没有n * n 大,也就是说比什么冒泡,插入排序的n * n 要快,花费的时间更少,就是说快排的n*log n让计算机干的活儿的量比n * n的算法要少。至于如何得岀n * log n是前面也说了去搜相关文章,网上很多,都是分析要干多少活儿得岀的数学函数。

我实在无法理解为什么要把time complexity直译成时间复杂度这个抽象词汇,这个直接翻译为性能不行吗,反正只是衡量算法性能的一个数学函数。也许计算机科学家和数学家翻译有讲究吧。

喜欢就点赞收藏, 点赞收藏是我创作的动力.

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flink TiDB CDC 环境配置与验证

一、TiDB 数据库核心配置 1. 启用 TiCDC 服务 确保 TiDB 集群已部署 TiCDC 组件&#xff08;版本需兼容 Flink CDC 3.0.1&#xff09;&#xff0c;并启动同步服务&#xff1a; # 示例&#xff1a;启动 TiCDC 捕获 changefeed cdc cli changefeed create \--pd"localhos…

2025年数据挖掘与计算机科学国际会议 (DMCS 2025)

2025 International Conference on Data Mining and Computer Science【一】、大会信息 会议简称&#xff1a;DMCS 2025 大会地点&#xff1a;中国广州 收录检索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等【二】会议简介2025年数…

腾讯轻量云和云服务器的区别

从问题本身来看&#xff0c;用户应该对云计算有基本了解&#xff0c;但可能不太清楚腾讯云产品线的细分定位。这类问题通常出现在项目初期技术选型阶段&#xff0c;用户需要权衡成本和性能。 让我先梳理两者的核心差异点。轻量云本质是面向轻量级应用的打包解决方案&#xff0c…

在使用ffmpeg时遇到了复制路径在终端输入指令后,报错的解决方法

错误如下所示&#xff1a;解决方法&#xff1a;​​检查路径中的特殊字符​​&#xff1a;你的路径中包含了一个不可见的Unicode字符&#xff08;‪&#xff0c;即LEFT-TO-RIGHT MARK&#xff09;&#xff0c;这是从网页复制路径时常见的隐藏字符​​解决方案​​&#xff1a;直…

高频变压器材料新解:纳米晶的涡流损耗逆袭之路

通过带材做薄纳米晶&#xff0c;可以降低涡流损耗。原因有二&#xff1a;一、纳米晶做薄可以减小磁场的趋肤效应&#xff1b;二、纳米晶越薄材料电阻越高&#xff0c;整体电阻越大&#xff0c;涡流损耗越小。本篇&#xff0c;就来详细谈谈变压器的涡流损耗。 铁氧体材料成本低&…

DMA技术与音频数据的存储和播放

基本概念 采样率: 每秒采集的采样点次数。如480000HZ, 就是我们常见的48KHZ采样点(Sample):每一个采样点代表一个时间点的声音幅度值。对于立体声,每个采样点包含了两个声道(左声道,右声道)的数据。帧:一帧就是一个时刻采集的数据,如果音频是立体声则会产生2个采样点,如…

项目进度受外包团队影响,如何管控交付节奏

项目进度受外包团队影响时&#xff0c;管控交付节奏的关键措施包括明确交付标准与节点、建立可视化进度监控机制、强化合同约束与激励条款、保持高频沟通与快速响应机制、建立联合质量审查机制。其中&#xff0c;明确交付标准与节点最为关键。通过制定具体、可量化的交付标准与…

BM9 删除链表的倒数第n个节点

目录 题目链接 题目 解题思路 代码 题目链接 删除链表的倒数第n个节点_牛客题霸_牛客网 题目 解题思路 先利用快慢指针找到删除位置的前一个节点,然后进行删除即可(具体就是快指针先移动n1个,因为要找到删除指针的前一个节点) 代码 import java.util.*;/** public clas…

java中ehcache因为可以缓存到本地,假如生产环境使用ehcache是不是需要在生产环境服务器创建缓存文件夹目录以存储ehcache缓存的数据

是的&#xff0c;当在生产环境中使用 Ehcache 的磁盘持久化功能时&#xff0c;确实需要在服务器上创建相应的缓存文件夹目录&#xff0c;并确保应用程序有权限读写该目录。 以下是详细说明和配置建议&#xff1a;1. 为什么需要创建缓存目录&#xff1f;Ehcache 的磁盘持久化功能…

day55

1. 序列预测介绍序列预测就是根据过去的序列数据&#xff08;比如时间顺序的数据&#xff09;&#xff0c;预测未来的结果。• 单步预测&#xff1a;只预测下一个时刻的值。比如根据前7天的气温&#xff0c;只预测第8天的气温。• 多步预测的2种方式&#xff1a;◦ 递归式&…

javaweb———html

我才开始学javaweb&#xff08;重点不在这&#xff09;可能学的比较慢&#xff0c;勿说HTML 基础结构HTML 文档的基本结构包含 <!DOCTYPE html> 声明、<html> 根元素、<head> 头部和 <body> 主体部分。<head> 中包含页面元信息&#xff0c;如标题…

OpenCV在Visual Studio 2022下的配置

OpenCV是一个开源的计算机视觉和机器学习软件库&#xff0c;广泛应用于图像处理、目标检测、模式识别等领域。它通常搭配在Visual Studio集成开发环境中使用&#xff0c;配置步骤主要有下载安装、加入系统环境变量、设置VS项目属性等。 1. 下载安装 a) 进入OpenCV官网&#xf…

kafka如何让消息均匀的写入到每个partition

在Kafka中,要实现消息均匀写入每个partition,核心是通过合理的分区分配策略让消息在partition间均衡分布。具体机制和实践方式如下: 一、Kafka默认的分区分配逻辑(核心机制) Kafka生产者发送消息时,通过Partitioner接口(默认实现为DefaultPartitioner)决定消息写入哪…

centos7修改yum源并安装Ansible

1、修改yum源在 CentOS 系统中&#xff0c;将默认的 yum 源修改为阿里云的镜像源&#xff0c;可以加快软件包的下载速度。以下是详细步骤&#xff1a;1&#xff09;备份原有的 yum 源配置sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup2…

Ubuntu 25.04安装搜狗输入法

0x00 安装思路 1. 卸载 ibus 和 fcitx5。 # 更新系统软件包 sudo apt update# 卸载 Fcitx5 和 IBus&#xff08;如果存在&#xff09; sudo apt remove --purge fcitx5* ibus*# 清理系统残留 sudo apt autoremove && sudo apt autoclean 2. 安装fcitx4。 # 安装 Fc…

二、Docker安装部署教程

作者&#xff1a;IvanCodes 日期&#xff1a;2025年7月7日 专栏&#xff1a;Docker教程 在前一篇文章中&#xff0c;我们了解了 Docker 的历史、能做什么以及核心概念 (镜像、容器、仓库)。现在&#xff0c;我们将更进一步&#xff0c;深入探究 Docker 中最常用也最核心的命令—…

【debug】git clone 报错

报错如下&#xff1a; error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err 8) error: 1964 bytes of body are still expected fetch-pack: unexpected disconnect while reading sideband packet fatal: early EOF fatal: fetch-pack: invalid…

二、MySQL 8.0 之《场景分析:不牺牲数据完整性下提供最大性能改进》

文章目录前言一、场景二、场景问题分析正确的四项选择 (B, C, E, H)错误的五项选择 (A, D, F, G, I)三、场景问题收获1. MySQL I/O子系统优化 (I/O Subsystem Optimization)2. InnoDB存储引擎关键参数调优 (InnoDB Key Parameter Tuning)3. 数据完整性与ACID特性 (Data Integri…

Nuxt.js 静态生成中的跨域问题解决方案

当您运行 npm run generate 生成静态页面时&#xff0c;Vite 的代理服务器确实无法使用&#xff0c;因为生成阶段是在 Node.js 环境中执行的构建过程。但别担心&#xff0c;我将为您提供一套完整的解决方案来处理构建阶段的跨域问题。核心解决方案1. 构建阶段&#xff1a;使用服…

【AI总结】Git vs GitHub vs GitLab:深度解析三者联系与核心区别

目录1 Git&#xff1a;版本控制的核心引擎1.1 Git的核心架构与工作原理1.2 Git的工作流程与区域划分1.3 Git的核心能力2 GitHub vs GitLab&#xff1a;云端双雄的差异化定位2.1 核心定位与市场策略2.2 技术架构深度对比2.2.1 核心功能差异2.2.2 AI能力演进路线&#xff08;2025…