快排算法的特点
- 实用性强。
很多实际的项目中使用了快排算法。但通常对算法都进行了调整(tuning),比如Java.util.Arrays类中的sort函数就使用了快排算法,但使用了双参考值(Dual-Pivot Quicksort)等一些改进措施。由于快排算法为递归算法,可以用循环代替递归函数调用,改进性能。
- 不需要额外的空间。
可以将数组中的数据直接交换位置实现排序,所以理论上不需要额外的空间。
时间复杂度
- 平均情况:O(nlgn)
- 最坏情况:O(n*n),发生在当数据已经是排序状态时
快排算法的基本原理
1、从数据中选取一个值a[i]作为参考
2、以a[i] 为参考,将数据分成2部分:P1、P2,P1中的数据全部≤a[i],P2中的数据全部>a[i],数据变为{{P1}{a[i]}{P2}}
3、将P1、P2重复上述步骤,直到各部分中只剩1个数据
4、数据完成升序排列
示例:
1
2
3
4
5
6
7
8
9
10
|
原始数据:
{
3
,
9
,
8
,
5
,
2
,
1
,
6
}
第
1
步:选取第
1
个数据:
3
第
2
步:将数据分成
2
部分,左边≤
3
,右边大于>
3
:
{
2
,
1
} {
3
} {
9
,
8
,
5
,
6
}
第
3
步:将各部分重复以上步骤,直到每部分只剩
1
个数据:
{
2
,
1
} => {
1
} {
2
}
{
9
,
8
,
5
,
6
} => {
8
,
5
,
6
} {
9
}=> {
5
,
6
} {
8
} {
9
}=> {
5
} {
6
} {
8
} {
9
}
第
4
步:数据完成升序排列:
{
1
} {
2
} {
3
} {
5
} {
6
} {
8
} {
9
}
|
程序中数据通常保存在数组中,以int类型的数组为例,可以将上面的步骤写成一个quickSort函数原型:
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
|
quickSort(
int
begin,
int
end) {
//begin为数组的第一个数据的索引值,end为数组的最后一个数据的索引值+1
//如果只有1个数据或0个数据,则程序返回
if
( begin == end || begin == (end-
1
) )
return
;
int
p = in[begin];
//p为选择的参考数据,选择第一个数据
int
a = begin +
1
;
//a作为2部分数据分界线的索引值
int
b = a;
//b为待比较的数据的索引值
for
( ; b < end; b++) {
//将数组中的各个数据依次与参考数据进行比较
if
( in[b] < p) {
//如果该数据<参考数据则将其移动到左边
if
(a == b){a++;
continue
;}
//如果该数据已经在左边则不动
int
temp = in[a];
//将数据移动到左边
in[a] = in[b];
in[b] = temp;
a++;
//将分界线右移
}
}
in[begin] = in[a-
1
];
//讲参考值移动到2组数据中间
in[a-
1
] = p;
if
( a-
1
> begin){
// 如果左边有数据则将其重复上述步骤
quickSort(begin, a);
}
if
( end-
1
> a ) {
// 如果右边有数据则将其重复上述步骤
quickSort(a, end);
}
return
;
// 如果无数据返回
}
|
使用泛型实现快排算法
下面设计一个QuickSort类,包含了静态函数sort(),可以对任意类型数组进行排序。如果为对象类型数组,则该对象类型必须实现Comparable接口,这样才能使用compareTo函数进行比较。
使用了最基本的快排算法,没有进行优化处理。
源代码如下:
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
import
java.util.LinkedList;
import
java.util.List;
import
java.util.ListIterator;
import
java.util.Random;
public
class
QuickSort {
@SuppressWarnings
(
"unchecked"
)
//对上述快排函数原型修改,使其可以对任意对象类型数组进行排序。这个函数为内部使用,外部排序函数接口为sort(),sort函数要求对象必须实现Comparable接口,可以提供编译时类型检测,见后文。
private
static
void
quickSort(Object[] in,
int
begin,
int
end) {
if
( begin == end || begin == (end-
1
) )
return
;
Object p = in[begin];
int
a = begin +
1
;
int
b = a;
for
( ; b < end; b++) {
//该对象类型数组必须实现Comparable接口,这样才能使用compareTo函数进行比较
if
( ((Comparable
0
) {
if
(a == b){a++;
continue
;}
Object temp = in[a];
in[a] = in[b];
in[b] = temp;
a++;
}
}
in[begin] = in[a-
1
];
in[a-
1
] = p;
if
( a-
1
> begin){
quickSort(in,begin, a);
}
if
( end-
1
> a ) {
quickSort(in,a, end);
}
return
;
}
//使用泛型,对任意对象数组排序,该对象类型数组必须实现Comparable接口
public
static
quickSort(input,
0
,input.length);
}
//添加对List对象进行排序的功能,参考了Java中的Java.util.Collections类的sort()函数
public
static
Object[] t = list.toArray();
//将列表转换为数组
quickSort(t,
0
,t.length);
//对数组进行排序
//数组排序完成后再写回到列表中
ListIterator
for
(
int
j=
0
; j
i.next();
i.set((T)t[j]);
}
}
//由于Java中原始数据类型(int、double、byte等)无法使用泛型,所以只能使用函数重载机制实现对这些原始类型数组(int[]、double[]、byte[]等)的排序。这里为了共用同一个排序函数,利用原始类型的(AutoBoxing,UnBoxing)机制将其封装为对应对象类型,组成新的对象数组,排序后再解封装,这样的缺点是需要额外的转换步骤、额外的空间保存封装后的数组。另一种方式是将排序代码复制到各个重载函数中,官方API中的Java.util.Arrays这个类中的sort()函数就是使用这种方法,可以从Arrays类的源代码看出。
public
static
void
sort(
int
[] input){
Integer[] t =
new
Integer[input.length];
for
(
int
i =
0
; i < input.length; i++){
t[i] = input[i];
//封装
}
quickSort(t,
0
,t.length);
//排序
for
(
int
i =
0
; i < input.length; i++){
input[i] = t[i];
//解封装
}
}
//double[]数组的重载函数
public
static
void
sort(
double
[] input){
Double[] t =
new
Double[input.length];
for
(
int
i =
0
; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,
0
,t.length);
for
(
int
i =
0
; i < input.length; i++){
input[i] = t[i];
}
}
//byte[]数组的重载函数
public
static
void
sort(
byte
[] input){
Byte[] t =
new
Byte[input.length];
for
(
int
i =
0
; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,
0
,t.length);
for
(
int
i =
0
; i < input.length; i++){
input[i] = t[i];
}
}
//short[]数组的重载函数
public
static
void
sort(
short
[] input){
Short[] t =
new
Short[input.length];
for
(
int
i =
0
; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,
0
,t.length);
for
(
int
i =
0
; i < input.length; i++){
input[i] = t[i];
}
}
//char[]数组的重载函数
public
static
void
sort(
char
[] input){
Character[] t =
new
Character[input.length];
for
(
int
i =
0
; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,
0
,t.length);
for
(
int
i =
0
; i < input.length; i++){
input[i] = t[i];
}
}
//float[]数组的重载函数
public
static
void
sort(
float
[] input){
Float[] t =
new
Float[input.length];
for
(
int
i =
0
; i < input.length; i++){
t[i] = input[i];
}
quickSort(t,
0
,t.length);
for
(
int
i =
0
; i < input.length; i++){
input[i] = t[i];
}
}
//测试用的main函数
public
static
void
main(String[] args) {
//生产一个随机数组成的int[]数组,用来测试
int
LEN =
10
;
int
[] input =
new
int
[LEN];
Random r =
new
Random();
System.out.print(
"int[] before sorting: "
);
for
(
int
i =
0
; i < input.length; i++) {
input[i] = r.nextInt(
10
*LEN);
System.out.print(input[i] +
" "
);
}
System.out.println();
System.out.print(
"int[] after sorting: "
);
sort(input);
for
(
int
i : input) {
System.out.print(i +
" "
);
}
System.out.println();
//生成一个字符串数组,用来测试
String[] s =
new
String[]{
"b"
,
"a"
,
"e"
,
"d"
,
"f"
,
"c"
};
System.out.print(
"String[] before sorting: "
);
for
(
int
i =
0
; i < s.length; i++) {
System.out.print(s[i] +
" "
);
}
System.out.println();
System.out.print(
"String[] after sorting: "
);
sort(s);
for
(
int
i =
0
; i < s.length; i++) {
System.out.print(s[i] +
" "
);
}
System.out.println();
//生成一个字符串列表,用来测试
List
new
LinkedList
s =
new
String[]{
"b"
,
"a"
,
"e"
,
"d"
,
"f"
,
"c"
};
System.out.print(
"LinkedList
);
for
(
int
j=
0
; j
l.add(s[j]);
System.out.print(s[j] +
" "
);
}
System.out.println();
sort(l);
System.out.print(
"LinkedList
);
for
(String ts : l) {
System.out.print(ts +
" "
);
}
System.out.println();
}
}
|
运行main函数测试,从输出可以看出QuickSort类工作正常:
1
2
3
4
5
6
|
int
[] before sorting:
65
48
92
26
3
8
59
21
16
45
int
[] after sorting:
3
8
16
21
26
45
48
59
65
92
String[] before sorting: b a e d f c
String[] after sorting: a b c d e f
LinkedList
LinkedList
|
参考资料:
[1] 麻省理工学院公开课:算法导论> 快排及随机化算法
http://v.163.com/movie/2010/12/S/4/M6UTT5U0I_M6V2T7IS4.html
[2] Java官方API(Oracle Java SE7)源代码,下载安装JDK后,源代码位于安装根目录的src.zip文件中
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
[3] OpenJDK源代码下载(包括了HotSpot虚拟机、各个系统下API的源代码,其中API源代码位于openjdk\jdk\src\share\classes文件夹下):
https://jdk7.java.net/source.html
http://my.oschina.net/u/1382972/blog/169747
http://www.tuicool.com/articles/BfY7Nz