快速排序
快速排序是对冒泡排序的一种改进,被认为是目前最好的一种内部排序方法.
快速排序的核心思想是分治思想,先分再合.
1 算法过程
1
2
3
4
5
6(从小到大排序) 1. 设定一个分界值(分界值任意,一般是数列首值,或数列中间值); 2. 将不大于分界值的数置于分界值左边,大于分界值的数置于分界值右边,从而将数列以分界值为分割点,分割为左右两个数列. 3. 对左右两个数列,分别重复上面的步骤. 4. 左、右两个数列各数据排序完成后,整个数列的排序也就完成了
2 排序演示
对下面的数列进行快速排序
1
23, 4, 6, 5, 7, 1, 2
我们需要设定一个分界值(这里设定数列首值
3为分界值s)
,和两个指针,一个指针(这里叫c)
用来遍历数列(除去分界值),另一个指针(这里叫i)
用来确定交换位.
排序开始,c
指向4
,i
指向4
,比较c
和s
1
2
3
43, 4, 6, 5, 7, 1, 2 s i c
4
大于3
,不发生交换,c
指针向右移一位,i
指针不动
1
2
3
43, 4, 6, 5, 7, 1, 2 s i c
6
大于3
,不发生交换,c
指针向右移一位,i
指针不动
1
2
3
43, 4, 6, 5, 7, 1, 2 s i c
5
大于3
,不发生交换,c
指针向右移一位,i
指针不动
1
2
3
43, 4, 6, 5, 7, 1, 2 s i c
7
大于3
,不发生交换,c
指针向右移一位,i
指针不动
1
2
3
43, 4, 6, 5, 7, 1, 2 s i c
1
小于3
,交换c
和i
所指向的数值(不是交换指针哈),即交换1
和4
,i
向右移一位(只要发生了交换,i
就向右移一位),c
向右移一位
1
2
3
43, 1, 6, 5, 7, 4, 2 s i c
2
小于3
,交换c
和i
所指向的数值,即交换2
和6
,i
向右移一位
1
2
33, 1, 2, 5, 7, 4, 6 s i
再交换s
和i-1
所指向的数值
1
2
32, 1, 3, 5, 7, 4, 6 s
此时,就实现了把数列不大于分界值(3)
的数置于分界值左边,大于分界值的数置于分界值右边.
对分界值左右两个数列重复上面的步骤(递归定义)
,就可以实现数列从小到大的排序.
3 代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51const arr = [3, 4, 6, 5, 7, 1, 2]; // 交换方法 function swap(arr, i, j) { let temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 快排主函数 function quickSort(arr, left, right) { let len = arr.length, l = typeof left === 'number' ? left : 0, r = typeof right === 'number' ? right : len - 1; if (l < r) { // 由分区函数划分完后,获得分界值的索引 const separator = separate(arr, l, r); // 以分界值索引为分割点,分割为两个子数组,递归调用quickSort主函数进行快排 quickSort(arr, l, separator - 1); quickSort(arr, separator + 1, r); } return arr; } /** * 分区函数 * 根据分界值(数组首值),将不大于分界值的数划分到分界值左边,大于分界值的数划分到分界值右边 * */ function separate(arr, left, right) { // separator为分界值索引,设定为待分区数组的首位 // index为交换位,每交换一次,index+1 let separator = left, index = left + 1; // 缓存分界值 const separatorValue = arr[separator]; // 遍历待分区数组(分界值除外) for (let c = index; c <= right; c++) { // 如果当前值不大于分界值,交换当前值与交换位index值 if (arr[c] <= separatorValue) { swap(arr, c, index); // 每交换一次,index+1 index++; } } // 最后交换分界值和上一个交换位的值(就是最后交换的值),达到不大于分界值的项都在分界值左边 swap(arr, separator, index - 1); // 返回分界值在原数组arr的索引 return index - 1; }
4 时间复杂度
快速排序的分界值选取是任意的,因此快速排序也带有偶然性,时间复杂度好坏,是由选取的分界值来决定的.
-
最好的情况下,我们每次选取的分界值,都正好是待排数列的中间数,此时可以确保每一次分割都能把数列分为两半,进而只需递归
log(n)
次,此时,快速排序的时间复杂度为O(nlog(n))
. -
最坏的情况下,我们每次选取的分界值,都正好是待排数列的最小或最大值,此时,快速排序退化成了未优化过的冒泡排序,需要比较
n(n-1)/2
次,时间复杂度为O(n^2)
; -
平均时间复杂度为
O(nlog(n))
.
5 稳定性
快速排序是不稳定的,可以看个例子:
1
2
35, 3, 4, 6, 4*, 2, 1 p ↑
假设5
是分界值,[3]
比5
小的子数列,当前比较值是2
2
比5
小,交换2
与4
1
25, 3, 2, 6, 4*, 4, 1
可以看到 此时4*
到了4
的前面.
最后
以上就是微笑含羞草最近收集整理的关于JavaScript实现排序算法(2)——快速排序的全部内容,更多相关JavaScript实现排序算法(2)——快速排序内容请搜索靠谱客的其他文章。
发表评论 取消回复