为什么算法容易忘记之快速排序

本文用来帮助大家理解记忆快速排序,方法和上篇文章一样,着重理解算法基本思想及其代码中的循环控制变量的意义。

基本思想

快速排序属于拿着元素找位置的算法。思路非常简单明了,首先给第一个元素找到它正确的位置并把它放置其中,此时该元素将原数组分为两半,左半边的元素都小于或等于它,右半边的元素都大于它,对这两个子数组重复刚才的操作,直到子数组中只有一个元素,此时排序完成。

由思想到代码

首先,我们用一个forInsert变量存储数组第一个位置上的元素的值。可以通俗理解为我们将第一个位置上的元素“挖”了出来,以便为它找到合适的位置,第一个位置此时已经是“空”的,位置是空的这一概念很关键,后面会用到。

如何为该元素找到合适的位置呢?答案是先确定该元素所在位置的范围,不断缩小该范围,直到该范围是一个确定的位置,查找结束,把forInsert的值放到该位置上,再对该位置左右两个子数组进行迭代,直到子数组大小为1时结束,排序完成。为表示该元素所在位置的范围,我们需要定义两个变量left,right,代表元素所在位置的范围的左端和右端,显然left的初始值应为0,right的初始值应为N-1。

下面开始缩小这一范围,将right位置上的元素与forInsert进行比较,如果大于forInsert,那么可以断定right这个位置肯定不是forInsert应当在的位置,因为如果将forInsert放置在right位置上,该位置上原来的元素将无处安放。所以我们可以将right减1(right--),这就缩小了位置的范围,然后我们继续将新的right位置上的元素与forInsert比较,如果还是大于forInsert,则可以继续将right减1后继续比较,直到right位置上的值小于forInsert的值时,就是magic发生的地方。

由于right位置上的元素比forInsert小,我们无法判断该位置是否是forInsert应当在的位置,BTW,我们可以判定left这个位置肯定不是forInsert应当在的位置,为什么?请参照上文叙述自行理解。

然后呢?我们可以将right位置上的值放置到left位置上,让left加1(left++),这进一步缩小了位置的范围。

此时right位置我们认为是“空的”了,看到没,刚才left是空的,现在right是空的了。

下步的思路肯定还是想办法继续缩小位置的范围。我们可以将left位置上的元素与forInsert比较,如果小于forInsert的值,我们可以断定,left这个位置肯定不是forInsert应当在的位置,为啥?因为将forInsert放置到该位置上,该位置上的元素只能往左边挪,而左边每个位置上都是比forInsert小的元素导致“无处可挪”,矛盾出现,反证结束。然后我们又可以放心地将left加1了,位置范围又缩小了,哦耶!

我们继续将left位置上的元素与forInsert比较,直到发现left位置上的元素大于forInsert时,又要有magic发生了,我们将left位置上的元素放置到right位置上(还记得right位置此时是空的吗?),现在,left位置变成空的了,由于此时right位置上的元素是大于forInsert的,right位置肯定不是forInsert应当在的位置,所以我们要将right减1,进一步缩小待确定位置的范围。

好了,让我们停一停,看看现在是什么状况,显然left增加了一些值,并且left位置此时是空的,right减少了一些值,整体上[left , right]包含的范围比初始时小了好多。如果left=right,我们知道,要找的位置就是现在left所指示的空位置,直接将forInsert放置到left位置上即可。然后开始左右两个子数组的迭代,如果left还是小于right,那我们只能继续进行缩小位置范围的工作,直到确定位置为止。

这是代码

void quick_sort(int s[], int l, int r)
{
if (l < r)
{
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x)
j--;
if(i < j)      s[i++] = s[j];
while(i < j && s[i] < x)
i++;
if(i < j)    s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}

原文发布于微信公众号 - 人工智能LeadAI(atleadai)

原文发表时间:2018-02-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我的博客

插入排序

原理: 1. 从第一个元素开始,该元素可以认为已经被排序 2. 取出下一个元素,在已经排序的元素序列中从后向前扫描 3. 如果该元素(已排序)大于新元素,...

2456
来自专栏xx_Cc的学习总结专栏

OC-基础总结(一)

35111
来自专栏机器学习算法与Python学习

python基础-数据类型与变量

转载于:廖雪峰的官方网站-python教程 数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是,计算机能处理的远...

3547
来自专栏我的博客

Go的基础知识1

1.关键字 break    default      func    interface    select case     defer        ...

3619
来自专栏Flutter入门

Kotlin中的函数

函数还可以用中缀表示法调用,当他们是成员函数或扩展函数,只有一个参数,用 infix关键字标注

1734
来自专栏Android干货园

Kotlin初级(3)- - - 基础函数.md

area函数在printArea外部无效,它只服务于printArea。这在实现一个大函数时隐藏实现的细节是非常有用的。除此之外,本地函数还有一个好处就是可以访...

863
来自专栏Python小屋

Python元组与列表的相同点与区别

列表和元组都属于有序序列,支持使用双向索引访问其中的元素、使用内置函数len()统计元素个数、使用运算符in测试是否包含某个元素、使用count()方法统计指定...

2786
来自专栏Python研发

最全Python内置函数

判断真假,  True:真  ,  False:假,   把一个对象转换成bool值

1802
来自专栏小古哥的博客园

JavaScript 正则表达式入门教程

正则表达式是描述一组字符串特征的模式,用来匹配特定的字符串 主要分三个部分:基本语法、RegExp对象的方法、JS中支持正则表达式的String对象方法 一、基...

3343
来自专栏有趣的django

5.python函数

函数介绍 定义:  函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可。 特性:减少重复代码、使程序变的可扩展、使程...

2866

扫码关注云+社区