排序算法之快速排序(Java实现)
一、介绍及原理
简单来说,快速排序就是每次选择一个基准。在遍历整个数组的过程中,将比基准小的数放到左侧,比基准大的数放到右侧。这样在一次循环过后,虽然整体依然无序,但是算法将数列分为两部分:左侧部分小于基准数而右侧部分大于基准数。之后利用分治思想分别将左侧部分与右侧部分进行快排,最终则可以得到一个完全有序的数组。
二、代码实现
public static void QuickSort(int[] arr , int left , int right) {
// 递归的出口必须仔细考虑清楚,否则就会陷入无穷循环从而使栈溢出
if (left >= right) {
return;
}
int pivot = arr[left];
int i = left;
int j = right;
while (i < j) {
// 这里如果pivot 选在左侧,就要先从右侧开始遍历,反之则先从左侧开始
while (arr[j] > pivot && i < j) {
j--;
}
// 找到比基准小的数换到左侧去
arr[i] = arr[j];
while (arr[i] < pivot && i < j) {
i++;
}
// 找到比基准大的数换到右侧去
arr[j] = arr[i];
}
// 最后将基准放到中间位置
arr[i] = pivot;
// 递归快排左侧数列
QuickSort(arr,left,i - 1);
// 递归遍历右侧数列
QuickSort(arr, i + 1, right);
}
三、时间复杂度
在算法中有一个主定理可用来考虑算法的时间复杂度:
\[T(n) = aT(\frac{n}{b}) + O(n^{d}),(a>1,b>1,d>0)
\]
\[if \ \ d <log_{b}^{a}:T(n) = O(n^{log_{b}^{a}}) \\
if \ \ d =log_{b}^{a}:T(n) = O(n^{log_{b}^{a}}log_{n}) \\
if \ \ d >log_{b}^{a}:T(n) = O(n^{d})
\]
在快速排序这个问题中:
\[T(n) = 2T(\frac{n}{2}) + O(n) \ = 4T(\frac{n}{4}) + O(n) + O(n)
\]
仔细考虑排序过程,第一次将数组整个扫描了一遍,时间复杂度为O(n),扫描之后将数组分成了两个子问题:之后两个问题分为四个子问题,时间复杂度又用了O(n),正好符合上式。
其中,O(n) 是每一次分区所用的时间复杂度,将参数带入可知,快速排序的时间复杂度为O(nLogn)。
其实不利用公式来考虑时间复杂度的话:每次用O(n) 的时间复杂度将问题为了两个子问题。假设m 次过后子问题划分为1 ,那么有
\[n/2/2/.../2 = \frac{n}{2^{m}} = 1
\]
所以经过了m次迭代后,问题归一。每次的时间复杂度为O(n) = cn,那么可得快速排序的时间复杂度为:
\[cmn = clog_{2}{n}n = O(nlogn)
\]