交换排序:冒泡排序、快速排序(改进的)
1、基本思想:确定一个基准值,将所有元素划分为两个序列,前一序列元素比基准值小,后一序列元素比基准值大,再对两个子序列进行相同操作,每次划分都将确定一个基准值的最终位置。
2、标准快排(挖坑法)实现步骤:
- 假设数组为array,将每个序列的 第一个值作为基准值pivot;
- 初始化i,j:i为第一个元素所在位置,j为最后一个元素所在位置,当前坑为array[i];
- 如果array[j]>=pivot,那么j--, 否则array[i] = array[j],i++,当前坑为array[j];
- 如果array[i]<=pivot,那么i++,否则array[j] = array[i],j--,当前坑为array[i];
- 对于其余子序列也做相同操作,先j变化再i变化,直到i==j为止。
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
public
void
quickSort(
int[]
array){
sort(
0,
array.
length
-1,
array);
}
private
void
sort(
int
left,
int
right,
int[]
array){
if(
left
<
right){
int
pivotPos
=
partion(
left,
right,
array);
sort(
left,
pivotPos
-1,
array);
sort(
pivotPos
+1,
right,
array);
}
}
private
int
partion((
int
left,
int
right,
int[]
array){
int
pivotPos
=
left;
//基准值的最终位置
int
pivot
=
array[
left];
//选定序列的第一个值作为基准值
int
i
=
left,
i
=
right;
while(
i
<
j){
while(
i
<
j
&&
array[
j]
>=
pivot)
j
--;
if(
i
<
j){
array[
i]
=
array[
j];
j刚好指向当前坑,变化i的值
i
++;
}
while(
i
<
j
&&
array[
i]
<=
pivot)
一定要加等于,否则有相等值时会产生死循环
i
++;
if(
i
<
j){
array[
j]
=
array[
i];
i刚好指向当前坑,变化j的值
j
--;
}
}
pivotPos
=
i;
//此时i,j相等,为基准值最终应该存放的位置
array[
pivotPos]
=
pivot;
return
pivotPos;
}
4、时间复杂度
(1)最好情况:每次划分都从中间分成相等的两个子序列。每次定位基准值的位置需要的时间为O(n),那么
T(n) <= cn + 2T(n/2) = cn + 2(cn/2 + 2T(n/4)) = 2cn+4T(n/4)=2cn+4(cn/4+2T(n/8))=3cn+8T(n/8)=cnlogn+nT(1)=O(nlogn)
从另一个角度来看,快速排序的过程实际就是一棵递归树,树的深度logn就是递归的次数,每次对O(n)个元素排序,所以时间复杂度为O(nlogn);
(2)最坏情况:元素按照正序排列,每次划分后都只排序好基准值左边的序列,需要对右边的序列继续划分,则划分次数总共为n-1次,每次划分需要比较n-k次(1<k<n-1),那么
T(n) = n-1+n-2+n-3+...+1 = n(n-1)/2 = O(n^2)
(3)平均情况:T(n) = O(nlogn)
5、空间复杂度
每次递归都要是用一个栈,所以空间复杂度为O(logn)
6、稳定性:不稳定
7、改进快排
改进快排的着手点主要在基准值的选取上,基准值要选取的尽量能将序列划分均匀。
(1)对于每个序列,比较第一个元素、中间元素和最后一个元素的值,取值居中的一个数作为基准值,将第一个数与值居中的这个数交换位置。
代码实现:
private
int
partion (
int
left,
int
right,
int[]
array){
//比较获得居中的值
int
mid
= (
left
+
right)/
2;
int
a
=
array[
left];
int
b
=
array[
right];
int
c
=
array[
mid];
int
pivot
=
a
>
b?(
a
>
c?((
b
>
c)?
b:
c):
a):(
a
>
c?
a:(
b
>
c:
c:
b));
int
pivotPos
=
left;
if(
pivot
==
b)
pivotPos
=
right;
else
if(
pivot
==
c)
pivotPos
=
mid;
//交换第一个元素与中值的位置
if(
pivotPos
!=
left){
int
temp
=
array[
pivotPos];
array[
pivotPos]
=
array[
left];
array[
left]
=
temp;
pivotPos
=
left;
}
int
i
=
left,
j
=
right;
while(
i
<
j){
while(
i
<
j
&&
array[
j]
>=
pivot)
j
--;
if(
i
<
j
&){
array[
i]
=
array[
j];
i
++;
}
while(
i
<
j
&&
array[
i]
<=
pivot)
i
++;
if(
i
<
j){
array[
j]
=
array[
i];
j
--;
}
}
pivotPos
=
i;
array[
pivotPos]
=
pivot;
return
pivotPos;
}
(2)对于每个序列,随机选取从头到尾的一个随机数作为基准值,这种排序叫做随机快速排序。