背景
之前我们介绍的选择排序、插入排序、快速排序的时间复杂度都是O(n2),在数据量较大时效率较低。因此我们利用分治思想设计出了两种效率更高的算法:归并排序和快速排序。归并排序和快速排序的时间复杂度都是O(nlogn),且在大多数情况下,快速排序比归并排序效率更高。
原理
快速排序的基本思想是分治原理,具体操作如下:
1. 在待排序的数组中先选择一个数作为基准值,然后遍历整个数组,将小于基准值的元素放到基准值左边,将大于基准值的元素放到右边,这样就确定了基准值最后的正确位置。
2. 递归处理基准值左侧子数组和右侧子数组,直到数组中只剩下一个元素,这样就实现了整个数组的排序。
具体实现步骤
1. 选取待排序数组的第一个元素作为基准值iPivot,并定义iLeft和iRight分别指向待排序数组的最左侧和最右侧。
2. 判断iRight指向的元素值nums[iRight]是否大于基准值iPivot。若是,则iRight继续向左移动,iRight--;否则,将nums[iRight]存放到nums[iLeft]处。(注意,此时nums[iLeft]值已经被保存或者放到了其他地方,因此该处可以用于保存新的元素值)
3. 判断iLeft指向的元素值nums[iLeft]是否小于等于基准值(带等于这样将等于基准值的元素都存放到基准值的左侧)。若是,则iLeft继续向右移动,iLeft++;否则,将nums[iLeft]存放到nums[iRight]处。(注意,此时nums[iRight]值已经被存放到其他地方,因此该处可以用于保存新的元素值)
4. 重复步骤2、3,直到iLeft、iRight相遇。此时需要将之前定义的基准值iPivot保存到中间位置,即下标iLeft处,这样即实现了基准值iPivot左侧的值都小于等于基准值,基准值右侧的值都大于基准值,基准值iPivot即为最终需要保存的位置。
5. 分别对基准值iPivot左侧子数组和右侧子数组分别递归使用上述方法实现排序,直至单个待排序数组大小个数为1,最终实现了整个数组的排序。
代码实现
/* 区间为左闭右闭原则 */
void QuickSort(int* piArray, int iLeft, int iRight)
{
int iCurLeft = iLeft;
int iCurRight = iRight;
/* 选取第一个元素作为基准值,这样遍历的时候需要从右侧开始遍历 */
int iPivot = piArray[iLeft];
if (iRight < iLeft)
{
/* 当待排序数组只有一个元素时结束递归 */
return;
}
while (iCurLeft < iCurRight)
{
/* 先从右侧开始遍历 */
while ((iCurLeft < iCurRight) && (piArray[iCurRight] > iPivot))
{
iCurRight--;
}
/* 将右侧不符合条件元素保存到左侧 */
piArray[iCurLeft] = piArray[iCurRight];
/* 再从左侧开始遍历 */
while ((iCurLeft < iCurRight) && (piArray[iCurLeft] <= iPivot))
{
iCurLeft++;
}
/* 将左侧不符合条件元素保存到右侧 */
piArray[iCurRight] = piArray[iCurLeft];
}
/* 将基准值iPivot放到中间位置,保证左边小于等于基准值,右边大于基准值 */
piArray[iCurLeft] = iPivot;
/* 递归排序基准值左侧子数组 */
QuickSort(piArray, iLeft, iCurLeft - 1);
/* 递归排序基准值右侧子数组 */
QuickSort(piArray, iCurLeft + 1, iRight);
return;
}
参考:快速排序算法_elma_tww的博客-CSDN博客_快速排序算法
|