快速排序 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,一经查实,立即删除!

相关文章

Linux命令行下感叹号的几个用法

Linux命令行下 " ! " 的几个用法 ! 在大多数编程语言中表示取反的意思&#xff0c;但是在命令行中&#xff0c;他还有一些其他的神奇用法。熟练掌握这些用法&#xff0c;可以大大提高我们日常命令行操作的效率。 1 执行历史命令 !! ! 在命令行中可以用来执行历史…

三地址码简介

三地址码简介 三地址码&#xff08;Three Address Code&#xff09;是一种最常用的中间语言&#xff0c;编译器可以通过它来改进代码转换效率。每个三地址码指令&#xff0c;都可以被分解为一个四元组&#xff08;4-tuple&#xff09;的形式&#xff1a;&#xff08;运算符&am…

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;赶紧来教教…

python参数传递*args和**kwargs

python参数传递*args和**kwargs 和* 实际上真正的Python参数传递语法是 * 和 ** 。*args 和 **kwargs 只是一种约定俗成的编程实践。我们也可以写成 *vars 和 **kvars 。就如同其他常规变量的命名一样&#xff0c; args 和 kwargs 只是一种习惯的名称。 *args 和 **kwargs 一…

听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;也得通过编译程序的…

JIT(动态编译)和AOT(静态编译)编译技术比较

JIT&#xff08;动态编译&#xff09;和AOT&#xff08;静态编译&#xff09;编译技术比较 转自&#xff1a;https://www.cnblogs.com/tinytiny/p/3200448.html Java 应用程序的性能经常成为开发社区中的讨论热点。因为该语言的设计初衷是使用解释的方式支持应用程序的可移植…

python解释器

python解释器 计算机编程语言 本部分参考自&#xff1a;https://zhuanlan.zhihu.com/p/141212114 从计算机编程语言说起&#xff0c;它主要分为三类&#xff1a;机器语言、汇编语言、高级语言。 机器语言是一种计算机可以直接识别并执行的二进制指令集。由于其可以直接交给…

编译型语言与解释型语言

编译型语言与解释型语言 首先要说明&#xff0c;编译型语言与解释型语言这种分类方法是不科学的&#xff0c;或者说已经过时了&#xff0c;但是这种称呼大抵还是能够让人明白我们将要讨论的是什么东西。 文中所列参考是笔者认为比较有帮助的一些扩展阅读内容。 首先贴一个很形…

常见的各种shell及其区别

常见的各种shell及其区别 引子 for((i1;i<10;i)); do echo $(expr $i \* 3 1); done 网上搜到的 shell for循环脚本&#xff0c;别人都能正常运行&#xff0c;我却报错&#xff1a; Syntax error: Bad for loop variable究竟是怎么回事呢&#xff1f; shell简介…

shell脚本 变量

shell脚本 变量类型 什么是Shell变量 用一个固定的字符串去表示不固定的内容。 Shell变量的类型 shell脚本中自定义变量的类型&#xff0c;我们这里分为&#xff1a; 自定义变量环境变量位置变量与定义变量 这四类&#xff0c;它们有一些相同点&#xff0c;但又有些不同点…

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

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

Python 函数式编程

Python 函数式编程 转自&#xff1a;https://www.liaoxuefeng.com/wiki/1016959663602400/1017328525009056&#xff0c;推荐去该链接读原文&#xff0c;有习题和热烈的评论区交流。 函数式编程 函数是Python内建支持的一种封装&#xff0c;我们通过把大段代码拆成函数&…

Python中的生成器与迭代器

Python中的生成器与迭代器 转自&#xff1a;https://www.liaoxuefeng.com/wiki/1016959663602400/1017323698112640&#xff0c;推荐去该链接读原文&#xff0c;有习题和热烈的评论区交流。 生成器 通过列表生成式&#xff0c;我们可以直接创建一个列表。但是&#xff0c;受…

基于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;为正常数字…

Python 装饰器详解(上)

Python 装饰器详解&#xff08;上&#xff09; 转自&#xff1a;https://blog.csdn.net/qq_27825451/article/details/84396970&#xff0c;博主仅对其中 demo 实现中不适合python3 版本的语法进行修改&#xff0c;并微调了排版&#xff0c;本转载博客全部例程博主均已亲测可行…

xss原理和注入类型

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

Python 装饰器详解(中)

Python 装饰器详解&#xff08;中&#xff09; 转自&#xff1a;https://blog.csdn.net/qq_27825451/article/details/84581272&#xff0c;博主仅对其中 demo 实现中不适合python3 版本的语法进行修改&#xff0c;并微调了排版&#xff0c;本转载博客全部例程博主均已亲测可行…

存储型xss案例

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