快速排序 C++

快速排序 C++

本文图示借鉴自清华大学邓俊辉老师数据结构课程。

快速排序的思想

快速排序是分治思想的典型应用。该排序算法可以原地实现,即空间复杂度为 O(1)O(1)O(1),而时间复杂度为 O(nlogn)O(nlogn)O(nlogn)

算法将待排序的序列 SSS 分为两个子序列 SLS_LSLSRS_RSR ,随着这种子序列的划分,可以保证问题的规模在不断缩小,即有:
max(∣SL∣,∣SR∣)<nmax(|S_L|,|S_R|) < n max(SL,SR)<n
并要求左侧子序列 SLS_LSL 中的最大值不大于右侧子序列 SRS_RSR 中的最小值,即有:
max(SL)≤min(SR)max(S_L)\le min(S_R) max(SL)min(SR)
这样在递归地再对子序列进行划分之后,原序列自然有序。递归的终止条件为划分子序列的平凡情况,即子序列的规模为1。

在这里插入图片描述

我们将划分两个子序列的点称为 pivot(轴点) ,在轴点左侧的元素,均不比它更大;在轴点右侧的元素,均不比它更小。轴点的位置,即为该元素在最终排序完成的序列中的位置。

即将原序列按如下划分:
[l,r]=[l,pivot)+pivot+(pivot,r][\ l, r\ ]=[\ l, pivot\ )+pivot+(\ pivot,r\ ] [ l,r ]=[ l,pivot )+pivot+( pivot,r ]
但是对于待排序的序列,我们并不知道谁是轴点,甚至,有可能原序列中所有元素都不是轴点,比如将一个已排序的序列循环右移一位,则此时所有元素都不在自己的排序位置上,都不是轴点。

好在我们可以通过对待排序的序列的元素进行移动交换,从而使得某个元素成为轴点,在递归之后,所有元素都成为轴点,这时所有元素都位于自己的排序位置上,整个排序算法完成。

可以看到,整个算法的框架是递归地选取轴点并划分子序列,究竟怎样划分,怎样划分效率更高,是快排算法的关键之处。

算法框架

我们先给出整个快排算法的递归框架,其中关键的 partition 划分函数我们会在后面详细介绍,整体框架是一个递归函数:

void quickSort(vector<int>& vec, int l, int r) {if (l<r) {int pivot = partition(vec, l, r);quickSort(vec, l, pivot-1);quickSort(vec, pivot+1, r);}
}

当 l 不小于 r 时,即 l 已经等于 r,此时即为递归退出的平凡情况:子序列只有一个元素。在此之前,我们都需要不断地执行 if 语句中的内容:得到传入序列的一个轴点,并分别递归地处理该轴点两侧的子序列。

最终当传入的子序列规模为1是,即 l<r 的判断条件不成立,达到返回条件,整个递归过程开始返回。

partition

版本1

接下来我们来介绍最为关键的 partition 函数。

在这里插入图片描述

我们先任选一个元素作为轴点的 “培养对象” 并备份,在我们划分完毕之后,该元素可以 “名正言顺” 地位于自己应该处于的最终排序位置,不妨就取序列的最左端元素。

然后将两个指针分别放在整个序列的左右两端,分别向中间靠拢。

我们将

  • 未确定区域称为 UUU (图中粉色部分),初始为全集
  • 确定大于轴点的部分称为 GGG (图中蓝色部分),初始为空集
  • 确定小于轴点的部分称为 LLL (图中绿色部分),初始为空集

交替地将左右两端的指针向中间移动,分别检查移动时当前元素与轴点 pivot 的大小关系,若小于轴点,则归入 LLL;若大于轴点,则归入 RRR

当左右两指针相等时,该位置即为我们的 “培养对象” 轴点应该处于的排序位置,将备份的轴点放入该位置即可。最后同样返回轴点位置。

整个过程中,左右指针所指的位置交替空闲。所谓空闲即是指其中元素可以被覆盖。一开始时,我们将 ”培养对象“ 备份,故其位置是 “空闲” 的,在随后的交还过程中,左右指针总有一个所指的位置是空闲的。

在这里插入图片描述

可以参考上图,图中紫色部分即为 “空闲” 的。红色的 lo、hi 分别对应我们描述中的左右指针。

代码实现如下:

int partition(vector<int>& vec, int l, int r) {int pivot = vec[l];while(l < r) {while (l<r && pivot<=vec[r]) r--;swap(vec[r], vec[l]);while (l<r && pivot>=vec[l]) l++;swap(vec[r], vec[l]);}vec[l] = pivot;return l;
}

版本2

对于 partition 函数,还有一种实现的方式,可以称为 LGU 的方式,在这种方式中,L 仍旧排在原序列的最左端,而中间是 G ,最右端是 U。

在这里插入图片描述

同样可以选取最左端的元素作为 pivot 。

然后一根指针从序列的最左端遍历到最右端,分别判断每个当前元素与 pivot 的关系,并分别划分到 L 或 G 中。

如果是划分到 G 中,直接将 k++ 即可;而如果是划分到 L 中,则需要将 G 滚动后移一个元素,并将当前元素交换到 L 的末尾。

代码实现如下:

int partition(vector<int>& vec, int l, int r) {int m = l, pivot = vec[l];for (int k=l+1; k<=r; k++) {if (pivot > vec[k])swap(vec[++m], vec[k]);}swap(vec[l], vec[m]);return m;
}

这里中间判断中的 if 分支是判断当前元素小于轴点 pivot ,需要将当前元素加入到 L 中,将当前元素 vec[k] 与 L 的最后一个元素 vec[++m] 互换,然后k++;而它对应的 else 分支,即当前元素小于轴点 pivot,需要将当前元素加入到 G 中,此时只需将 k++。两个分支都需要 k++,直接放在循环变量的更新中。

最后同样返回轴点位置。

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

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

相关文章

llvm与gcc

llvm与gcc llvm 是一个编译器&#xff0c;也是一个编译器架构&#xff0c;是一系列编译工具&#xff0c;也是一个编译器工具链&#xff0c;开源 C11 实现。 gcc 相对于 clang 的优势&#xff1a; gcc 支持更过语言前端&#xff0c;如 Java, Ada, FORTRAN, Go等gcc 支持更多地 …

攻防世界web新手区解题 view_source / robots / backup

1**. view_source** 题目描述&#xff1a;X老师让小宁同学查看一个网页的源代码&#xff0c;但小宁同学发现鼠标右键好像不管用了。 f12查看源码即可发现flag 2. robots 题目描述&#xff1a;X老师上课讲了Robots协议&#xff0c;小宁同学却上课打了瞌睡&#xff0c;赶紧来教教…

听GPT 讲Rust源代码--src/tools(25)

File: rust/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs 在Rust源代码中&#xff0c;suspicious_command_arg_space.rs文件位于clippy_lints工具包的methods目录下&#xff0c;用于实现Clippy lint SUSPICIOUS_COMMAND_ARG_SPACE。 Clippy是Ru…

Java一次编译,到处运行是如何实现的

Java一次编译&#xff0c;到处运行是如何实现的 转自&#xff1a;https://cloud.tencent.com/developer/article/1415194 &#xff08;排版微调&#xff09; JAVA编译运行总览 Java是一种高级语言&#xff0c;要让计算机执行你撰写的Java程序&#xff0c;也得通过编译程序的…

攻防世界web新手区解题 /cookie / disabled_button / weak_auth

cookie 题目描述&#xff1a;X老师告诉小宁他在cookie里放了些东西&#xff0c;小宁疑惑地想&#xff1a;‘这是夹心饼干的意思吗&#xff1f;’ 使用burp suite抓包查看 发现提示&#xff1a; look-herecookie.php 于是在url后加上 cookie.php 得到提示查看返回 就得到了f…

基于GET报错的sql注入,sqli-lab 1~4

根据注入类型可将sql注入分为两类&#xff1a;数字型和字符型 例如&#xff1a; 数字型&#xff1a; sleect * from table where if 用户输入id 字符型&#xff1a;select * from table where id 用户输入id &#xff08;有引号) 通过URL中修改对应的D值&#xff0c;为正常数字…

xss原理和注入类型

XSS漏洞原理 : XSS又叫CSS(cross Site Script), 跨站脚本攻击,指的是恶意攻击者往Web页面里插入恶意JS代码,当用户浏览该页时,嵌入其中的Web里的JS代码就会被执行,从而达到恶意的特殊目的. 比如:拿到cooike XSS漏洞分类: 反射性(非存储型) payload没有经过存储,后端接收后,直接…

存储型xss案例

存储型xss原理: 攻击者在页面插入xss代码,服务端将数据存入数据库,当用户访问存在xss漏洞的页面时,服务端从数据库取出数据展示到页面上,导致xss代码执行,达到攻击效果 案例: 在一个搭建的论坛网站中, 根据存储型xss注入的条件,要找到可以存储到数据库的输入位置,并且这个位置…

反射型XSS案例

**原理:**攻击者将url中插入xss代码,服务端将url中的xss代码输出到页面上,攻击者将带有xss代码的url发送给用户,用户打开后受到xss攻击 需要url中有可以修改的参数 案例: 可能存在反射型xss的功能(点) : 搜索框等&#xff08;所有url会出现参数的地方都可以尝试&#xff09;……

xss-lab靶场通关writeup(1~6.......在更新)

level 2 : 标签被编码&#xff0c;利用属性完成弹窗 输入 发现没有弹窗 查看源代码&#xff1a; 发现&#xff1a; <>符号被编码 说明keybord参数进行了处理&#xff0c;那么只能从属性上进行恶意编码&#xff1a;先将属性的引号和标签闭合&#xff0c;用 // 将后面的…

PyTorch 分布式训练DDP 单机多卡快速上手

PyTorch 分布式训练DDP 单机多卡快速上手 本文旨在帮助新人快速上手最有效的 PyTorch 单机多卡训练&#xff0c;对于 PyTorch 分布式训练的理论介绍、多方案对比&#xff0c;本文不做详细介绍&#xff0c;有兴趣的读者可参考&#xff1a; [分布式训练] 单机多卡的正确打开方式…

Linux free 命令详解

Linux free 命令详解 free 命令用来查看系统中已用的和可用的内存。 命令选项及输出简介 关于各种命令的功能和命令选项&#xff0c;还是推荐英语比较好的同学直接看手册 RTFM&#xff1a;man free。这里简单总结一下一些重点&#xff1a; 功能及输出简介 free 命令显示系…

CTF web题 wp:

1.签到题 火狐F12查看源码&#xff0c;发现注释&#xff1a; 一次base64解码出flag 2.Encode 在这里插入图片描述 和第一题界面一样&#xff1f;&#xff1f; 轻车熟路f12&#xff1a; 发现编码&#xff1a; 格式看上去是base64&#xff0c;连续两次base64后&#xff0c;观…

【深度学习】深入理解Batch Normalization批归一化

【深度学习】深入理解Batch Normalization批归一化 转自&#xff1a;https://www.cnblogs.com/guoyaohua/p/8724433.html 这几天面试经常被问到BN层的原理&#xff0c;虽然回答上来了&#xff0c;但还是感觉答得不是很好&#xff0c;今天仔细研究了一下Batch Normalization的原…

ThinkPHP V5 漏洞利用

ThinkPHP 5漏洞简介 ThinkPHP官方2018年12月9日发布重要的安全更新&#xff0c;修复了一个严重的远程代码执行漏洞。该更新主要涉及一个安全更新&#xff0c;由于框架对控制器名没有进行足够的检测会导致在没有开启强制路由的情况下可能的getshell漏洞&#xff0c;受影响的版本…

Vim 进阶1

Vim 进阶1 所有你觉得简单重复&#xff0c;可以自动化实现的操作&#xff0c;都是可以自动化实现的。 Vim光标移动拾遗 w&#xff1a;下一个单词的开头&#xff0c;e&#xff1a;下一个单词的结尾&#xff0c;b&#xff1a;上一个单词的开头&#xff0c; 0&#xff1a;行首…

攻防世界web题ics-06(爆破id值)

打开界面&#xff1a;嚯&#xff01;这花里胡哨 点来点去只有报表中心有回显&#xff1a; 发现url中id等于1&#xff0c;sql注入尝试无果&#xff0c; burp工具爆破id 对id的值进行爆破 burp报ERROR的话这是个bug&#xff0c;先点击Hex后点decimal手动刷新就可以使用 强行总…

crontab用法与实例

crontab用法与实例 本文基于 ubuntu 18.04 在Linux系统的实际使用中&#xff0c;可能会经常碰到让系统在某个特定时间执行某些任务的情况&#xff0c;比如定时采集服务器的状态信息、负载状况&#xff1b;定时执行某些任务/脚本来对远端进行数据采集等。这里将介绍下crontab的配…

手工sql注入常规总结

1.发现注入点 2.报数据库 先用单引号&#xff08;也尝试双引号&#xff09;闭合前面的语句&#xff0c;使注入的语句能够执行&#xff0c; 数字 0 :匹配字段&#xff0c;还有 11 12 等等都可以使用&#xff0c;有些网站会有过滤处理&#xff0c;建议采用 1%2b12 1%2b1>1 绕…

Systemd入门教程:命令篇

Systemd入门教程&#xff1a;命令篇 转自&#xff1a;http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html 作者&#xff1a; 阮一峰 日期&#xff1a; 2016年3月 7日 Systemd 是 Linux 系统工具&#xff0c;用来启动守护进程&#xff0c;已成为大多数…