十大排序算法:快速排序算法

2023-11-01

一、快速排序算法思想或步骤

  1. 分解: 数组A[p…r]被划分为两个子数组A[p…q-1]和A[q+1…r],使得A[q]为大小居中的数,左侧A[p…q-1]中的每个元素都小于等于它,而右边A[q+1…r]每个元素都大于等于它。
  2. 解决: 通过递归调用快速排序,对子数组A[p…q-1]和A[q+1…r]进行排序。
  3. 合并: 因为子数组都是在原址进行排序,所以不需要合并,原数组已经有序。
    通过上述描述发现:划分是问题的关键

算法的就可以写出如下框架:

void quickSort(A,p,r){
	if(p<r){
		q = divAlgo(A,p,r);
		quick(A,p,q-1);
		quick(A,q+1,r);
	}
}

二、划分算法divAlgo:快排算法的关键

下面介绍几种划分的思路和算法

1. 一遍单向扫描法

首先确定一个基元,一般基元确定为区间的第一个元素,然后通过两个指针pi,pj来进行移动划分,初始值pi为该区间的第二个位置,即pi=1;pj为该区间的最后一个为pj = size()-1。划分过程大致如下:
在pi小于等于pj的情况下,执行下面步骤:

  1. 不断向后移动pi指针,直到pi所指元素大于基元。
  2. 交换pi和pj所指内容,pj前移一位
  3. 执行第1步
  4. 当pi小于等于pj条件不满足时,交换pj所指元素和基元,并返回pj。

有了上述步骤,则可以轻松写出相应的划分算法。

int divAlgo(vector<int> &src,int p,int r){
    int baseVal = src[p];
    int pi = p + 1,pj = r;
    while (pi <= pj){
        if(src[pi] <= baseVal) ++pi;
        else swap(src[pi],src[pj--]);
    }
    swap(src[p],src[pj]);
    return pj;
}

那么接着使用这个划分算法测试一下能否完成排序。

#include<bits/stdc++.h>
using namespace std;
int divAlgo(vector<int> &src,int p,int r){
    int baseVal = src[p];
    int pi = p + 1,pj = r;
    while (pi <= pj){
        if(src[pi] <= baseVal) ++pi;
        else swap(src[pi],src[pj--]);
    }
    swap(src[p],src[pj]);
    return pj;
}
void quickSort(vector<int> &src,int p,int r){
    if(p<r){
        int q = divAlgo(src,p,r);
        quickSort(src,p,q-1);
        quickSort(src,q+1,r);
    }
}
int main(){
    vector<int> ad{4,3,2,5,1,8,9};
    quickSort(ad,0,ad.size()-1);
    copy(ad.begin(),ad.end(),ostream_iterator<int>(cout," "));
    return 0;
}
/*
1 2 3 4 5 8 9
*/

2. 双向扫描法

双向扫描的思路为:头指针往中间进行扫描,尾指针往中间进行扫描,左边找到大于对应基元的位置,右边找到小于基元的位置,进行交换,继续扫描。
有了单向扫描的基础,该代码就比较好写,代码如下:

int divAlgo2(vector<int> &src,int p,int r){
    int baseVal = src[p];
    int pi = p + 1,pj = r;
    while (pi <= pj){
        while(pi <= pj && src[pi] <= baseVal) ++pi;
        while(pi <= pj && src[pj] > baseVal) --pj;
        if(pi < pj)
            swap(src[pi],src[pj]);
    }
    swap(src[p],src[pj]);
    return pj;
}

测试代码如下:

#include<bits/stdc++.h>
using namespace std;
int divAlgo2(vector<int> &src,int p,int r){
    int baseVal = src[p];
    int pi = p + 1,pj = r;
    while (pi <= pj){
        while(pi <= pj && src[pi] <= baseVal) ++pi;
        while(pi <= pj && src[pj] > baseVal) --pj;
        if(pi < pj)
            swap(src[pi],src[pj]);
    }
    swap(src[p],src[pj]);
    return pj;
}
void quickSort(vector<int> &src,int p,int r){
    if(p<r){
        int q = divAlgo2(src,p,r);
        quickSort(src,p,q-1);
        quickSort(src,q+1,r);
    }
}
int main(){
    vector<int> ad{4,3,2,5,1,8,9};
    quickSort(ad,0,ad.size()-1);
    copy(ad.begin(),ad.end(),ostream_iterator<int>(cout," "));
    return 0;
}
/*
1 2 3 4 5 8 9
*/

三、快排存在的问题

  1. 如果原数组已经有序,算法效率不好
  2. 数组长度较大时,时间复杂度高
  3. 选取基元的方法本身存在缺陷。

四、优化

  1. 待排序元素个数比较少时,使用插入排序
  2. 快排时,选取基元使用绝对中值法三点中值法
  3. 排序元素较多时,使用堆排序

结合这三点,可以封装一个跟标准库中sort函数思想差不多的排序算法。
这三种优化,1和3后面会有相应的排序算法,针对2,修改divAlgo函数,三点中值法:选取基元尽可能地可以将数组分为个数相同的两半【选取左、中、右三个元素中中间大的元素作为基元】,或者绝对中值法:选取数组中间位置元素作为基元。

五、使用快排思想找数组中第k大元素框架

主框架都是这个,不同的是找枢纽的部分需要修改

int findKth(vector<int>& a, int low, int high, int k){//找第k个
		int p = divAlgo(a, low, high);
		if (k == p - low + 1)
			return a[p];
        
		else if (k - 1 < p - low)//则第k大的元素在前半段
			return findKth(a, low, p - 1, k);
 
		else //则第k大的元素在后半段
			return findKth(a, p + 1, high, k - p + low - 1);
	}

int divAlgo(vector<int> &src,int p,int r){
    int baseVal = src[p];
    int pi = p + 1,pj = r;
    while (pi <= pj){
        if(src[pi] <= baseVal) ++pi;//找第k小的,当变为>=时找第k大
        else swap(src[pi],src[pj--]);
    }
    swap(src[p],src[pj]);
    return pj;
}

最小的k个数
方法1:优先队列,大顶堆

vector<int> GetLeastNumbers_Solution_1(vector<int> input, int k) {
        if(k == 0 || input.empty()) return vector<int>();
        // priority_queue默认为大顶堆,声明为priority_queue<T,vector<T>,less<T>>
        // priority_queue<T, vector<T>, greater<T>>为小顶堆
        priority_queue<int> pq_int;// 默认是大顶堆,也就是top处的元素为当前优先队列的最大值
        vector<int> res;
        for(int i = 0; i < input.size(); ++i){
            if(pq_int.size() < k){
                pq_int.push(input[i]);
            }else{
                if(pq_int.top() > input[i]){
                    pq_int.pop();
                    pq_int.push(input[i]);
                }
            }
        }
        while(!pq_int.empty()){
            res.push_back(pq_int.top());
            pq_int.pop();
        }
        return res;
    }

方法2:快排

// 快排思想,进行局部性排序
    bool flag = true;
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k){
        if(0 == k || input.empty()) return {};
        quickSort(input, 0, input.size() - 1, k);
        return vector<int>(input.begin(), input.begin() + k);
    }
    
    int divide(vector<int> &data, int l, int r){
        int base_val = data[l];
        int pi = l + 1, pj = r;
        while(pi <= pj){
            while(pi <= pj && data[pi] <= base_val) ++pi;
            while(pi <= pj && data[pj] >= base_val) --pj;
            if(pi < pj) swap(data[pi++], data[pj++]);
        }
        swap(data[l], data[pj]);
        return pj;
    }
    
    void quickSort(vector<int> &data, int pi, int pj, const int &k){
        if(!flag || pi >= pj) return;
        int index = divide(data, pi, pj);
        if(index > k){
            quickSort(data, pi, index - 1, k);
        }else if(index < k){
            quickSort(data, index + 1, pj, k);
        }else{
            flag = false;
            return;
        }
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

十大排序算法:快速排序算法 的相关文章

  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • 没有强命名的代码签名是否会让您的应用程序容易被滥用?

    尝试了解authenticode代码签名和强命名 我是否正确地认为 如果我对引用一些 dll 非强命名 的 exe 进行代码签名 恶意用户就可以替换我的 DLL 并以看似由我签名但正在运行的方式分发应用程序他们的代码 假设这是真的 那么您似
  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • 从Web API同步调用外部api

    我需要从我的 Web API 2 控制器调用外部 api 类似于此处的要求 使用 HttpClient 从 Web API 操作调用外部 HTTP 服务 https stackoverflow com questions 13222998
  • 在 Windows 窗体中保存带有 Alpha 通道的单色位图会保存不同(错误)的颜色

    在 C NET 2 0 Windows 窗体 Visual Studio Express 2010 中 我保存由相同颜色组成的图像 Bitmap bitmap new Bitmap width height PixelFormat Form
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • 将多个表映射到实体框架中的单个实体类

    我正在开发一个旧数据库 该数据库有 2 个具有 1 1 关系的表 目前 我为每个定义的表定义了一种类型 1Test 1Result 我想将这些特定的表合并到一个类中 当前的类型如下所示 public class Result public
  • 垃圾收集器是否在单独的进程中运行?

    垃圾收集器是否在单独的进程中启动 例如 如果我们尝试测量某段代码所花费的进程时间 并且在此期间垃圾收集器开始收集 它会在新进程上启动还是在同一进程中启动 它的工作原理如下吗 Code Process 1 gt Garbage Collect
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 向现有 TCP 和 UDP 代码添加 SSL 支持?

    这是我的问题 现在我有一个 Linux 服务器应用程序 使用 C gcc 编写 它与 Windows C 客户端应用程序 Visual Studio 9 Qt 4 5 进行通信 是什么very在不完全破坏现有协议的情况下向双方添加 SSL
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co

随机推荐

  • java:方法重载和方法重写的区别

    方法重载 代码示例 public void set System out println 好好学习 public void set String name System out println 好好学习 方法重写 在不同的类中 在有继承关系
  • 服务器部署JavaWeb的war包(完整版)

    本文章内容操作环境采用的技术是docker部署war 提前下载好Xshell7 终端 如果你买的服务器有终端窗口 那么用你的服务器终端窗口也行 和Xftp7 传输文件 并下载navicat15 版本过低会因为1045 连不上服务器 一 导出
  • 网络基础:协议层次

    目录 一 理论 1 OSI参考模型 2 TCP IP模型 3 OSI模型对应协议 4 TCP IP模型对应协议 5 OSI模型传输数据过程 二 实验 1 TCP IP模型封装 一 理论 一个协议层能够用软件 硬件或者两者的结合来实现 各个层
  • 谷歌浏览器插件Automa_2.点击和输入文字

    操作 普通玩家对于组件的操作无非就输入文字 点击控件跳转页面 但高端玩家会为这些操作加上各种限制条件以让其适应各种网页 而这些内容将在进阶篇介绍 点击 1 找到你要点击的位置 2 定位它 这里有讲如何定位 3 复制那个位置 粘贴到元素选择器
  • Pytorch学习笔记(1)第四章 神经网络工具箱nn

    今天学习内容 https github com chenyuntc pytorch book blob master chapter4 E7 A5 9E E7 BB 8F E7 BD 91 E7 BB 9C E5 B7 A5 E5 85 B
  • 账号和权限管理——设置目录和文件的归属(五)

    设置目录和文件的归属 1 chown 命令 需要设置文件或者目录的归属时 主要通过 chown 命令进行 可以只设置属主或属组 也可以同时设置属主 属组 使用 chown 命令的基本格式如下 chown 属主 属组 文件或目录 同时设置属主
  • django入门:Admin管理系统及表单(干货)

    点击上方蓝字关注公众号 码个蛋第310次推文 作者 Kuky xs 博客 https www jianshu com p 8cdf099e974f 前言 django入门 环境及项目搭建 django入门 数据模型 django入门 视图及
  • Anaconda安装虚拟环境下的Jupyter Notebook没有快捷方式怎么办

    Anaconda安装虚拟环境下的Jupyter Notebook没有快捷方式怎么办 今天为了安装tensorflow 在anaconda环境下创建了一个名称为tensorflow虚拟环境 一波操作装完了tensorflow之后 为了在jup
  • 食品行业仓储条码管理系统解决方案

    食品行业总是保持一贯的稳健增势 而且整体行业在产品结构 市场竞争力 运营成本等方面仍有相当的潜力可以发掘 但食品安全问题 消费者口味的不断变化 多变的渠道模式和供应链效率问题 这些都为食品饮料行业建立了一个动荡的充满挑战和机遇的商业环境 而
  • Oracle 数据库升级

    转载来源 Oracle 数据库升级 https mp weixin qq com s LIDIsmeZRRfZmOVtOkeznQ 一 环境准备 本次测试尽量按照生产环境升级进行模拟 故而使用2台主机进行测试 注意 源库为生产环境 linu
  • 【Java编程】JavaSE基础总结(六):多线程

    JavaSE基础总结 六 进程是程序执行的实体 每一个进程都是一个应用程序 比如我们运行QQ 浏览器 LOL 网易云音乐等软件 都有自己的内存空间 CPU 一个核心同时只能处理一件事情 当出现多个进程需要同时运行时 CPU一般通过 时间片轮
  • [精通Objective-C]块(block)

    精通Objective C 块 block 参考书籍 精通Objective C 美 Keith Lee 目录 精通Objective C块block 目录 块的语法 块的词汇范围 块的内存管理 块的使用 使用块为数组排序 使用块的并行编程
  • 让你真正明白cinder与swift、glance的区别

    http www aboutyun com thread 10060 1 1 html 问题导读 1 你认为cinder与swift区别是什么 2 cinder是否存在单点故障 3 cinder是如何发展而来的 在openstack中 我们
  • 计算机辅助绘图考试题,计算机辅助设计绘图考试题(A)(大学期末复习试题).doc...

    教师试做时间出题教师 取题时间审核教研室主任出题单位使用班级考试日期院 部 主任考试成绩期望值印刷份数规定完成时间交教务科印刷日期 学号 姓名 班级 密 封 线 专业 年级 班 学年 第 学期 计算机辅助设计绘图 A 课试卷 题号一二三四五
  • Swagger 的简介和使用

    文章目录 Swagger 的简介和使用 什么是Swagger 简介 Swagger页面 Swagger快速上手 pom xml文件中引入依赖 构建Swagger配置类 Swagger使用 常用注解说明 注解的使用 总结 Swagger 的简
  • Spark jar包加载顺序及冲突解决

    一 spark jar包加载顺序 1 SystemClasspath Spark安装时候提供的依赖包 通常是spark home目录下的jars文件夹 SystemClassPath 2 Spark submit jars 提交的依赖包 U
  • Java-Map集合

    基本使用 public class Demomap public static void main String args Map
  • 关于VMware workstation Player的虚拟网络编辑器没有的情况

    VMware workstation Player 是没有 虚拟网络编辑器的 如果要按照韦东山老师的方法去配置NAT网络 可以再下载VMware workstation pro 尽管不在试用期 依然会给你虚拟网络编辑器的应用安装
  • 光照 (5) 光照贴图

    物体在不同的部件上都有不同的材质属性 1 1 漫反射 允许我们对物体的漫反射分量 以及间接地对环境光分量 它们几乎总是一样的 和镜面光分量有着更精确的控制 漫反射贴图 Diffuse Map 使用一张覆盖物体的图像 让我们能够逐片段索引其独
  • 十大排序算法:快速排序算法

    一 快速排序算法思想或步骤 分解 数组A p r 被划分为两个子数组A p q 1 和A q 1 r 使得A q 为大小居中的数 左侧A p q 1 中的每个元素都小于等于它 而右边A q 1 r 每个元素都大于等于它 解决 通过递归调用快