前言
快速排序是一种很实用的排序算法。今天在网上看到有网友谈论快速排序是什么。想想自己记得也不是很清楚了,索性就写一篇小记,复习一下。
1 简介
快速排序是对冒泡排序的一种改进。
它的核心思想是:通过一趟排序将要排序的数列分成两个独立的新数列,其中一个新数列的每个元素都比另一个新数列的任何一个元素要小。之后,对这两个数列再次进行快速排序。
2 算法性质
内部排序
快速排序是一种内部排序算法。即参与快速排序的对象都是存储在内存中的。(了解一下 外部排序 )
不稳定性
在排序算法中,假设待排序数列中有两个元素a、b,其中a.key == b.key,若排序算法不能保证a和b在数列中的相对位置,则称排序算法是不稳定的。
例如:有数列 'yabx',其中 y.key > a.key == b.key > x.key。经排序,得到数列 'xbay'。此时,数列为升序,是有序的。但a和b的相对位置想比较于排序前,发生了颠倒,我们称这种排序就是不稳定的,它可能改变元素的相对排列顺序。
原地排序
原地排序意味着排序算法随问题规模增大,对额外内存空间的需求是常数级的(S(1))。所以,在内存受限的系统中,快速排序也能很好地工作。
3 算法描述
为了便于理解。这里给出一个待排序的数列: 6, 2, 3, 7, 1, 5, 4, 8, 9
第一趟排序
step1,选取待排序数列(以下称 '数列')中索引为 '0' 的元素(记为 pivot
)作为本趟排序的主轴,记 pivot
当前索引位置为 pre_pos
。 (pivot.key == 6
, pre_pos == 0
,此时数列为原始数列: 6, 2, 3, 7, 1, 5, 8, 4, 9
)
step2,从数列末尾对数列遍历(为便于理解,以下称 '从右到左遍历'),找到第一个比 pivot.key
小的元素(记为 rear
),rear
的索引记为 rear_pos
。将 pivot
与 rear
交换位置(即交换 '4' 和 '6')。 (pivot.key == 6
, pre_pos == 0
, rear_pos == 7
,交换后数列为: 4, 2, 3, 7, 1, 5, 8, 6, 9
)
step3,现在,从数列头部开始,找到索引为 pre+1
的元素,以索引为 pre+1
的元素为起点,向数列尾部方向做遍历(为方便理解,以下称 '从左到右遍历')。直到找到比 pivot
大的元素(记为 pre
),更新 p_pos
的值为 pre
当前的索引位置,交换 pre
与 pivot
的位置(即交换 '6' 和 '7')。(pivot.key == 6
, pre_pos == 3
, rear_pos == 7
,交换后数列为: 4, 2, 3, 6, 1, 5, 8, 7, 9
)
到这里,我们发现,经过两次交换, pre
(pre.key == 6
)左侧的元素都比 pivot
小,rear
(rear.key == 7
)右侧的元素都比 pivot
大。
重复 step2
和 step3
,完成第一趟排序,得到数列 4, 2, 3, 5, 1, 6, 8, 7, 9
。
经过第一趟排序,我们得到了两个新的数列 4, 2, 3, 5, 1
(记为 数列 a) 和 8, 7, 9
(记为 数列 b)。显然,数列 a 中的元素都小于 pivot
(pivot.key == 6
),而数列 b 中的元素都大于 piovt
。
这时候,我们分别以数列 a、 数列 b 为待排序数列,对它们进行快速排序的第一趟排序。
数列 a 经过第一趟排序,结果为 1, 2, 3, 4, 5
,已经有序;数列 b 经过第一趟排序,结果为 7, 8, 9
。
这时候,原始的待排序数列中的所有元素已经是有序排列的了。即 1, 2, 3, 4, 5
+ 6
+ 7, 8, 9
。
此时,快速排序已完成。