常见排序算法-快速排序

排序算法原理

快速排序(Quick Sort)的原理简述如下:

(以升序为例)

1.利用分治的思想。选择数组中某元素为“基准数”,将所有小于它的元素移至其左侧,暂称为“左子数组”;大于它的元素移至其右侧,暂称为“右子数组”。对左右子数组重复执行此步骤;

2.执行步骤1,直至子数组划分为最小单位,则完成排序。

代码示例

此处以c#代码为例,其他语言大同小异(转换为所需语言请看<语法说明>板块以作参考)

        考虑到,需要对每个子数组进行同样的操作步骤,即将所有小于基准数的元素移至其左侧,大于它的元素移至其右侧(我们称之为哨兵划分)。因此需先提供该方法以供调用:

·哨兵划分
private static int Partition(int[] nums, int left, int right)
{
    int i = left;
    int j = right;
    while (i < j)
    {
        // 以nums[left]为基准数
        while (i < j && nums[j] >= nums[left])
            j--;
        while (i < j && nums[i] <= nums[left])
            i++;
        Swap(nums, i, j);
    }
    Swap(nums, i, left);
    return i;
}

// 交换数组中的两个值
private static void Swap<T>(T[] arr, int i, int j
{
    (arr[i], arr[j]) = (arr[j], arr[i]);
}

        对原数组递归进行哨兵划分,直至最小数组单位,即完成排序

·快速排序
public static void QuickSort(int[] nums, int left, int right)
{
    while (left < right)
    {
        int pivot = Partition(nums, left, right);
        QuickSort(nums, left, pivot - 1);
        QuickSort(nums, pivot + 1, right);
    }
}

算法优化

        注意到,假如出现极端情况,如原数组为倒序,那么在递归进行哨兵划分时,总有一个数组长度为0,分治策略则无法实现。因此对基准数的选取进行优化:每次选取数组左中右三个元素,取其中间值使用。

        同时考虑到,当前算法每轮递归都需开启两个新的快速排序,递归树高度将达到n-1,这也会占用较大的栈帧空间。因此对算法进行尾递归优化(仅限于支持尾递归优化的语言)。

优化后的代码

public static void QuickSort(int[] nums, int left, int right)
{
    //尾递归优化
    while (left < right)
    {
        int pivot = Partition(nums, left, right);
        int temp;
        if (pivot - left < right - pivot)
        {
            temp = left;
            left = pivot + 1;
            QuickSort(nums, temp, pivot - 1);
        }
        else
        {
            temp = right;
            right = pivot - 1;
            QuickSort(nums, pivot + 1, temp);
        }
    }
}

// 获取三个值的中间值
private static int MedianThree(int[] nums, int left, int mid, int right)
{
    if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right]))
        return left;
    else if ((nums[left] < nums[mid]) ^ (nums[mid] > nums[right]))
        return mid;
    return right;
}
private static int Partition(int[] nums, int left, int right)
{
    //为防止出现极端情况(如数组完全倒序),导致一端子数组长度持续为0,从而劣化为冒泡排序。对基数选择做出调整。
    int mid = MedianThree(nums, left, (left + right) / 2, right);
    Swap(nums, left, mid);
    int i = left;
    int j = right;
    while (i < j)
    {
        while (i < j && nums[j] >= nums[left])
            j--;
        while (i < j && nums[i] <= nums[left])
            i++;
        Swap(nums, i, j);
    }
    Swap(nums, i, left);
    return i;
}

语法说明

为便于其他语言使用者进行代码转换,此处对可能存在的语法疑问作出注解。

1.public表示该函数是公开的,可根据具体语言及需求调整;

2.static表示该函数是静态的,可根据具体语言及需求调整;

3.void表示该函数返回值为空,可根据具体语言调整;

4.int是表示Int32类型的关键字,意为4个字节的有符号整形;

5.bool是表示Boolean类型的关键字,值为true或false;

6.for循环的语法结构为:

for(初始化索引;保持循环的条件;每次循环结束执行的语句){循环体}

这与C,C++,Java,Go,JS,TS,Dart类似。在Python,Swift,Rust中可替换为for index in range结构;

7.while循环的语法结构为:

while(保持循环的条件){循环体} 与其他语言类似,可进行简单替换;

8.方法名<T>的写法表示泛型,有些语言也称作参数多态或模板;

9.Func<T, T, bool> func的写法表示委托,相当于Java的接口和匿名内部类;Python的函数指针、高阶函数和回调函数;JS的回调函数和事件处理程序;Swift的代理等。在示例代码中,意为将一个函数作为参数传入,该函数需要两个类型为T的参数,并有一个bool类型的返回值。

算法特性

时间复杂度O(n log n):优化后算法较为稳定。

空间复杂度O(1)到O(log n):仅对原数组进行操作。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值