【初探】快速排序

来源:互联网 发布:美工的进阶之路 编辑:程序博客网 时间:2024/06/07 00:01

快速排序是冒泡排序的改进版,也是最好的一种内排序,在很多面试题中都会出现,也是作为程序员必须掌握的一种排序方法。

  快速排序的基本思想是,通过一轮的排序将序列分割成独立的两部分,其中一部分序列的关键字(这里主要用值来表示)均比另一部分关键字小。继续对长度较短的序列进行同样的分割,最后到达整体有序。在排序过程中,由于已经分开的两部分的元素不需要进行比较,故减少了比较次数,降低了排序时间。

思想: 1.在待排序的元素任取一个元素作为基准(通常选第一个元素,但最的选择方法是从待排序元素中随机选取一个作为基准),称为基准元素;

   2.将待排序的元素进行分区,比基准元素大的元素放在它的右边,比其小的放在它的左边;   3.对左右两个分区重复以上步骤直到所有元素都是有序的。

  “基准值”的选择有很多种方法。最简单的是使用第一个记录的关键字值。但是如果输入的数组是正序或者逆序的,就会将所有的记录分到“基准值”的一边。较好的方法是随机选取“基准值”,这样可以减少原始输入对排序造成的影响。但是随机选取“基准值”的开销大。

虽然快速排序称为分治法,但分治法这三个字显然无法很好的概括快速排序的全部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法:先来看实例吧,定义下面再给出(最好能用自己的话来总结定义,这样对实现代码会有帮助)。以一个数组作为示例,取区间第一个数为基准数。

这里写图片描述

初始时,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–;数组变为:

这里写图片描述

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]。

数组变为:
这里写图片描述

可以看出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]中。

这里写图片描述

注意:快速排序还有很多改进版本,如随机选择基准数,区间内数据较少时直接用另的方法排序以减小递归深度。有兴趣的筒子可以再深入的研究下。注1,有的书上是以中间的数作为基准数的,要实现这个方便非常方便,直接将中间的数和第一个数进行交换就可以了。

以上是写快速排序的第一种思想方法

总结第一种方法:
1. 首先让j从右往左找比基准数值小的数, 找到后,把j上的值赋值到k(就是基准数值)
2. 然后在i从左往右找基准数值大的数, 找到后,把i的值赋值到j上去,
3. 当i=j时候, 就是相遇时,即同一个位置, 就把基准数值添加到“i=j”的位置上,

这里写图片描述

这是快速排序的第二种思想方法

总结第二种方法为: 1.首先让j从右往左找比基准数值小的数。 然后在i从左往右找基准数值大的数, 此时就交换i和j的位置 注意:每次先让j先走

       2.  当i=j时候,  就是相遇时,即同一个位置,  就把基准数值与“i=j”这个位置上的数交换即可,         这样就完成了第一次数的划分,        3.  就这样一轮结束后  再对左右两个分区重复以上的步骤即可,  直到所有元素都是有序的

以上两种方法本人都已经实现过了, 个人觉得第二种方法简单易懂, 觉得有难度的朋友可以先学习第二种方法, 弄懂后再去搞第一种方法, 但是第一种方法的实现代码要比第二种方法的实现代码的语句要少,

算法分析:1.当分区选取的基准元素为待排序元素中的最大或最小值时,为最坏的情况,时间复杂度和直接插入排序的一样,移动次数达到最大值

 Cmax = 1+2+...+(n-1) = n*(n-1)/2 = O(n2) 此时最坏时间复杂为O(n2) 2.当分区选取的基准元素为待排序元素中的"中值",为最好的情况,时间复杂度为O(nlog2n)。3.快速排序的空间复杂度为O(log2n). 4.当待排序元素类似[6,1,3,7,3]且基准元素为6时,经过分区,形成[1,3,3,6,7],两个3的相对位置发生了改变,所是快速排序是一种不稳定排序。

算法分析


这里写图片描述

时间复杂度

当数据有序时,以第一个关键字为基准分为两个子序列,前一个子序列为空,此时执行效率最差。而当数据随机分布时,以第一个关键字为基准分为两个子序列,两个子序列的元素个数接近相等,此时执行效率最好。所以,数据越随机分布时,快速排序性能越好;数据越接近有序,快速排序性能越差。

快速排序的时间复杂度在最坏情况下是O(N2),平均的时间复杂度是O(N*lgN)。

这句话很好理解:假设被排序的数列中有N个数。遍历一次的时间复杂度是O(N),需要遍历多少次呢?至少lg(N+1)次,最多N次。

(01) 为什么最少是lg(N+1)次?快速排序是采用的分治法进行遍历的,我们将它看作一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的定义,它的深度至少是lg(N+1)。因此,快速排序的遍历次数最少是lg(N+1)次。

(02) 为什么最多是N次?这个应该非常简单,还是将快速排序看作一棵二叉树,它的深度最大是N。因此,快读排序的遍历次数最多是N次。

空间复杂度

快速排序在每次分割的过程中,需要 1 个空间存储基准值。而快速排序的大概需要 Nlog2N次 的分割处理,所以占用空间也是 Nlog2N 个。

算法稳定性

在快速排序中,相等元素可能会因为分区而交换顺序,所以它是不稳定的算法。
原创粉丝点击