快速排序
快速排序是分治策略的另一个典型例子,对于任一待排序的序列 S 将其分为前后两个更小的子序列 S1和 S2,并对两个规模更小的序列进行递归的排序。待所有子序列递归的排序结束之后,整个序列S自然变成一个有序的序列。
如果是从小到大排序的序列
要求 S1 序列中的最大元素必须 小于等于 S2序列中最小的元素 既 Max(S1) <= Min(S2)
如果是从大到小排序的序列
要求 S1 序列中的最小元素必须 大于等于 S2序列中最大的元素 既 Min(S1) <= Max(S2)
平凡解:只含有单个元素的序列,本身就是有序序列。
以图例展示排序过程如下:
public void QuickSort(int[] arr)
{
QuickSort(arr, 0, arr.Length - 1);
}
private void QuickSort(int[] arr, int low, int high)
{
if (low >= high)
{
return;
}
int mid = Partition(arr, low, high);
QuickSort(arr, low, mid);
QuickSort(arr, mid + 1, high);
}
private int Partition(int[] arr, int low, int high)
{
// 取最左侧的数据为轴点
int pivot = arr[low];
while (low < high)
{
while (low < high && pivot <= arr[high])
{
--high;
}
arr[low] = arr[high];
while (low < high && arr[low] < pivot)
{
++low;
}
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
性能分析:
稳定性:快速排序算法是不稳定的,元素移动到轴点左右两侧的过程中,会破坏元素的先后顺序,出现相同元素前/后颠倒的情况,导致结果是不稳定的。
空间复杂度:只需要附加 O(1)的常数空间
时间复杂度:
最好情况:每次划分,轴点总是接近中间位置,此时达到最优的 O(nlogn)
最坏情况:每次划分极不均衡,如 轴点总是最大/最小的元素,导致所有元素均被划分到一侧,此时达到最坏情况 O(n^2),如对已排序的序列进行快速排序,如果每次都取第 0 个元素为轴点,则轴点一定是最小/最大的元素
如何减少最坏情况的发生,方案:每次随机选取序列中的一个元素作为轴点、两三个元素比较策略取其一等,但是任何方案都只能降低最坏情况的发生概率,而无法杜绝。