快速排序最新理解

快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包括像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考试如软考,考研中也常常出现快速排序的身影。

总的说来,要直接默写出快速排序还是有一定难度的,因为本人就自己的理解对快速排序作了下白话解释,希望对大家理解有帮助,达到快速排序,快速搞定。

快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。

该方法的基本思想是:

1.先从数列中取出一个数作为基准数。

2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

3.再对左右区间重复第二步,直到各区间只有一个数。

虽然快速排序称为分治法,但分治法这三个字显然无法很好的概括快速排序的全部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法:

先来看实例吧,定义下面再给出(最好能用自己的话来总结定义,这样对实现代码会有帮助)。

以一个数组作为示例,取区间第一个数为基准数。


0


1


2


3


4


5


6


7


8


9


72


6


57


88


60


42


83


73


48


85

初始时,i = 0;  j = 9;   X = a[i] = 72

由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。

从j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;

数组变为:


0


1


2


3


4


5


6


7


8


9


48


6


57


88


60


42


83


73


88


85

i = 3;   j = 7;   X=72

再重复上面的步骤,先从后向前找,再从前向后找

从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;

从i开始向后找,当i=5时,由于i==j退出。

此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。

数组变为:


0


1


2


3


4


5


6


7


8


9


48


6


57


42


60


72


83


73


88


85

可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。

对挖坑填数进行总结

1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。

2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。

3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。

4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。

照着这个总结很容易实现挖坑填数的代码:

int AdjustArray(int s[], int l, int r) //返回调整后基准数的位置

{

int i = l, j = r;

int x = s[l]; //s[l]即s[i]就是第一个坑

while (i < j)

{

// 从右向左找小于x的数来填s[i]

while(i < j && s[j] >= x)

j--;

if(i < j)

{

s[i] = s[j]; //将s[j]填到s[i]中,s[j]就形成了一个新的坑

i++;

}

// 从左向右找大于或等于x的数来填s[j]

while(i < j && s[i] < x)

i++;

if(i < j)

{

s[j] = s[i]; //将s[i]填到s[j]中,s[i]就形成了一个新的坑

j--;

}

}

//退出时,i等于j。将x填到这个坑中。

s[i] = x;

return i;

}

再写分治法的代码:

void quick_sort1(int s[], int l, int r)

{

if (l < r)

{

int i = AdjustArray(s, l, r);//先成挖坑填数法调整s[]

quick_sort1(s, l, i - 1); // 递归调用

quick_sort1(s, i + 1, r);

}

}

这样的代码显然不够简洁,对其组合整理下:

//快速排序

void quick_sort(int s[], int l, int r)

{

if (l < r)

{

//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1

int i = l, j = r, x = s[l];

while (i < j)

{

while(i < j && s[j] >= x) // 从右向左找第一个小于x的数

j--;

if(i < j)

s[i++] = s[j];

while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数

i++;

if(i < j)

s[j--] = s[i];

}

s[i] = x;

quick_sort(s, l, i - 1); // 递归调用

quick_sort(s, i + 1, r);

}

}

快速排序还有很多改进版本,如随机选择基准数,区间内数据较少时直接用另的方法排序以减小递归深度。有兴趣的筒子可以再深入的研究下。

注1,有的书上是以中间的数作为基准数的,要实现这个方便非常方便,直接将中间的数和第一个数进行交换就可以了。

转载请标明出处,原文地址:http://www.cnblogs.com/morewindows/archive/2011/08/13/2137415.html

时间: 2024-12-28 20:19:53

快速排序最新理解的相关文章

[math] 我对对数的最新理解

前言 作为资深学渣,每次遇到对数就极度恐慌.恐慌不是因为要考试---.而是因为不理解,只能靠死记硬背运算规则.不能进行有效的推理,这让我极度不爽,因为会忘记.故惶恐. 所以总是耿耿于怀,想要试图理解对数的本质.最近看到了一篇文章,再一次的加深了理解.故整理了一些自己最新的感悟记录如下: 新的理解 接下来说一下我的最新理解,那么对数到底要怎么理解呢? 我们要知道自然数与四则运算的演化过程,之后自然便能导出到底啥是对数了. 推理过程如下: 1.有一天,上帝创造了自然数.自然数除了天然存在以外,还有一

对快速排序的理解以及相关c++代码

快速排序:在一组数据中,可以将左边的数字当作枢轴(右边也可以),接下来要做的就是,先从右边找到比枢轴小的数, 再从左边找到比枢轴大的数,接着将这两个数进行交换,重复上述步骤找出所有符合条件的数进行交换, 最后将枢轴放到比枢轴大的数与比枢轴小的数之间.之所以要从右边开始找,并且找到比枢轴小的数是因为交换后小的数就在枢轴的左边了. 下面举个比较特殊的例子希望能增加理解. 1 9 8 5 6 7 3 2 0 4 先从右往左找到比1小的第一个数字为0,此时的索引位置j=8,再从左往右找到比1大的第一个数

java中快速排序的理解以及实例

所谓的快速排序的思想就是,首先把数组的第一个数拿出来做为一个key,在前后分别设置一个i,j做为标识,然后拿这个key对这个数组从后面往前遍历,及j--,直到找到第一个小于这个key的那个数,然后交换这两个值,交换完成后,我们拿着这个key要从i往后遍历了,及i++;一直循环到i=j结束,当这里结束后,我们会发现大于这个key的值都会跑到这个key的后面,不是的话就可能你写错了,小于这个key的就会跑到这个值的前面:然后我们对这个分段的数组再时行递归调用就可以完成整个数组的排序. 用图形法表示由

对try-catch-finally异常处理的最新理解

try{ ...... }catch(......){ }finally{ ...... } 这个结构是用来处理Java所有可能出现的异常的,这个我很早其实就已经学过,不过最近看了个视频,感觉自己虽然明白这里边的东西都是什么时候执行的,但是还是不太清楚每一块具体要实现怎样的一个功能. 下面我具体说一下: 一.首先,先说一下try块,try块里存放的是可能会出现问题的代码,对于Java编程来说,我们经常考虑不到哪里可能会出现错误,尤其是在一些代码中,它并不是总会出现错误,对于有些数据,它可以通过,

Manifest文件的最新理解

今天看了Manifest文件内容的相关视频,感觉对知识的理解深刻了一些: 首先,先来说说这个文件的作用,这个文件可以说是聚集了很多个标签,其实对于每个主标签,在将来编译的时候,都会被处理成一个类,而标签里的属性也就成为了该类的成员变量,这些类究竟起到一个什么样的作用呢,主要无外乎标注了一些文件的执行顺序,以及一些权限的赋予,起到一个辅助的作用. 先来说说这个Manifest文件的整体结构,在默认的情况下,最外边标签是为了声明此文件为XML文件,以及XML文件的版本问题,接下来包裹的标签就到正题了

使用python实现冒泡排序和快速排序

1 def bubble(arr): 2 """冒泡排序""" 3 loop = len(arr) - 1 4 if loop > 0: 5 for l in range(loop): 6 for i in range(loop - l): 7 if arr[i] > arr[i + 1]: 8 arr[i], arr[i + 1] = arr[i + 1], arr[i] 9 return arr 10 11 12 def spli

细致分析快速排序!

快速排序是当下面试最容易考的题之一.初学算法基础的伙伴一定饱受它的折磨,因为快速排序的思想让初学者有一些发懵.我写下这篇文章谈一谈自己对快速排序的理解,希望能够帮助初学者理解一下快速排序算法的流程. 快速排序的思想: 我要探讨的思想呢,不是教科书上的,而是我自己按容易理解的方法进行了改编. 请大家跟着我的思路: 宏观的来看: 1 我们拿出第一个数当作基准数,把所有比他小的数放在他左边,所有比他大的数放在右边 2 对mid左侧所有的数当成一个新的数列 进行上述排序 3 对mid右侧所有的数当成一个

排序算法系列——快速排序

记录学习点滴 快速排序算法是一种很有趣的算法,短小精悍,性能强劲,对于大部分情况都可以胜任,但对极端环境难以应付. 快速排序我理解为:这是一个“以自我为中心的”+“分治法”思想的算法. 分治法不必多说,化繁为简,那就是逐个击破. 那什么是“以自我为中心”?顾名思义,就是每次都一个“我”,每个人都要围绕“我”行事,比“我”小的都去左边站着,比“我”他大的都去右边站着,而且“我”不去关心每一边都有谁,反正你没“我”大或者小就行.一旦“我”落位了妥帖了,“我”就不动了.然后再在左右两边分别产生新“我”

总结4种常用排序(快排、选择排序、冒泡排序、插入排序)

一. 选择排序 概念理解: 在一个长度为3的数组中,在第一趟遍历3个数据,找出其中最小的数值与第一个元素交换: 第二趟遍历2个数据,找出其中最小的元素与第一个数交换(注意:这里的第一个数是指遍历的第一个数,实质上是数组的第二个数) 而第三趟则是和自己比较,位置还是原来的位置 复杂度: 平均时间复杂度:O(n^2) 例子: //选择排序 function selectionSortFn(arr){ console.log('原数组:['+ arr + ']') for (var i = 0; i