基于分治的思想,首先在数组中选择一个基准点,选取基准点的效率决定了时间复杂度,从数组两端扫描数组,设置 low
和 high
两个索引,图中蓝标代表 low
红标代表 high
,橘色表代表重合位置
首次分割,数列被分成了两个子数列,长度分别是 i-1
和 n-i
,排序时间表示为 T(i-1)
和 T(n-i)
,计算总时间还得加上分割操作的时间,分割操作只用了循环 while(low<high)
,此段时间记为 cn
,所以 Tn = T(i-1)+T(n-i)+cn
;
# 演示
public class quicksort { | |
public static void main(String[] args) { | |
int[] arr = { 23, 46, 0, 8, 11, 18 }; | |
quickSort(arr, 0, arr.length - 1); | |
System.out.print("排序后 "); | |
for (int i : arr) { | |
System.out.print(i+" "); | |
} | |
} | |
private static void quickSort(int[] arr, int low, int high) { | |
if (low < high) { | |
// 找寻基准数据的正确索引 | |
int index = getIndex(arr, low, high); | |
// 进行迭代对 index 之前和之后的数组进行相同的操作使整个数组变成有序 | |
//quickSort (arr, 0, index - 1); 之前的版本,这种姿势有很大的性能问题,谢谢大家的建议 | |
quickSort(arr, low, index - 1); | |
quickSort(arr, index + 1, high); | |
} | |
} | |
private static int getIndex(int[] arr, int low, int high) { | |
// 基准数据 | |
int tmp = arr[low]; | |
while (low < high) { | |
// 当队尾的元素大于等于基准数据时,向前挪动 high 指针 | |
while (low < high && arr[high] >= tmp) { | |
high--; | |
} | |
// 如果队尾元素小于 tmp 了,需要将其赋值给 low | |
arr[low] = arr[high]; | |
// 当队首元素小于等于 tmp 时,向前挪动 low 指针 | |
while (low < high && arr[low] <= tmp) { | |
low++; | |
} | |
// 当队首元素大于 tmp 时,需要将其赋值给 high | |
arr[high] = arr[low]; | |
} | |
// 跳出循环时 low 和 high 相等,此时的 low 或 high 就是 tmp 的正确索引位置 | |
// 由原理部分可以很清楚的知道 low 位置的值并不是 tmp, 所以需要将 tmp 赋值给 arr [low] | |
arr[low] = tmp; | |
return low; // 返回 tmp 的正确位置 | |
} | |
} |
# 最坏的情况
数列每次分割得到的两个子数列长度为 n-1
和 0
,也就是其中的每一个元素都要移动 n
次,总时间 T(n^2)
T(n) = T(n - 1) + C*n | |
T(n - 1) = T(n - 2) + C*(n - 1) # 将 n 减 1,得到下个子序列 | |
T(n - 2) = T(n - 3) + C*(n - 3) | |
.... | |
T(2) = T(1) + C*2 | |
T(1) = T(0) + C | |
# 将下一级表达式代入上一级 | |
T(n) = T(0) + C*n + C*(n-1) + C*(n-2) + C*(n-3).....+C*(2)+C | |
# T(0)是等于0的 | |
T(n) = C*n + C*(n-1) + C*(n-2) + C*(n-3).....+C*(2)+C | |
T(n) = C*[n + (n-1) + (n-2) + (n-3).....+2+1]T(n) = C*(n + 1)*n/2 |
最高阶为 n^2
,最差时间复杂度为 O(n^2)
# 最好情况
数列每次分割得到的两个子数列长度等长,除分割时间以外左右各为 T(n/2)
,总时间 2T(n/2)+cn
T(n) = 2T(n / 2) + C*n | |
T(n / 2) = 2T(n / 4) + C*n / 2 | |
T(n / 4) = 2T(n / 8) + C*n / 4 | |
.... | |
# 将下一级表达式代入上一级,化简找到通项 | |
T(n) = 2^k*T(n/2^k)+ kcn | |
# 当n/2^k->1, k = lg2n | |
T(n) = nT(1) + cnlg2n |
最优时间复杂度为 O(nlgn)
# 平均时间复杂度
<<算法导论>> p98 p99