排序算法学习之快速排序

快速排序

快速排序,简称快排,在排序中,快速排序其实就是我们前面认为最慢的冒泡排序的升级,它们都属于交换排序类。即快排也是通过不断比较和移动交换来实现排序的,不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。
基本思想

       任取待排元素序列中的某元素中的作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于
    基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

这里写图片描述

void QuickSort(Datatype* a, int left, int right)
{
    assert(a);
    if (left >= right)
        return;
    int div = Partition(a, left, right);
    QuickSort(a, left, div - 1);
    QuickSort(a, div + 1, right);

}

来看下面几种按照基准值将区间划分的方法
1.左右指针法:

//快速排序左右指针法
int Partition1(Datatype* a, int left, int right)
{
    Datatype key = a[right];
    int begin = left, end = right;
    while (begin < end)
    {
        if (a[begin] <= key)
        {
            begin++;
            continue;
        }
        /*while (begin < end&&a[begin] <= key)
            begin++;*/
        if (a[end] >= key)
        {
            end--;
            continue;
        }
        /*while (begin < end&& a[end] >= key)
            end--;*/
        Swap(&a[begin], &a[end]);
    }
    Swap(&a[begin], &a[right]);
    return begin;
}

2.挖坑法

//挖坑法
int Partition2(Datatype* a, int left, int right)
{
    Datatype key = a[right];
    int tig = right;
    int begin = left, end = right;
    while (begin < end)
    {
        if (a[begin] <= key)
        {
            begin++;
            continue;
        }
        a[tig] = a[begin];
        tig = begin;
        if (a[end] >= key)
        {
            end--;
            continue;
        }
        a[tig] = a[end];
        tig = end;
    }
    a[tig] = key;
    return tig;
}

3.神奇的联动(prev)指针

//神奇的prev指针
int Partition3(Datatype* a, int left, int right)
{
    Datatype key = a[right];
    int prev = left - 1;
    int cur = left;
    while (cur < right)
    {
        /*if (a[cur] < key&& ++prev!=cur)
        {   //代码短,难理解,以下代码代替
            Swap(&a[prev], &a[cur]);
        }*/
        if (a[cur] < key)
        {
            prev++;
            if (prev!=cur)
                Swap(&a[prev], &a[cur]);
        }
        cur++;
    }
    Swap(&a[++prev], &a[right]);
    return prev;
}

快速排序算法虽然整体上比其他算法更快,但当数据基本趋于有序的时候,每次基准值都是最后一位,区间没有被划分开。所以算法最差的时间复杂度为O(N2).

常见的优化方案有:
1.随机数法
2.三数取中法
3.小区间优化法

//1.随机数法
int Partition(Datatype* a, int left, int right)
{
    int index = RandomInRange(start,end);
    Swap(&a[index],&a[right]);
    Datatype key = a[right];
    int begin = left, end = right;
    while (begin < end)
    {
        while (begin < end&&a[begin] <= key)
            begin++;
        while (begin < end&& a[end] >= key)
            end--;
        Swap(&a[begin], &a[end]);
    }
    Swap(&a[begin], &a[right]);
    return begin;
}
//2.三数取中法:
int MidInRange(Datatype* a, int left, int right)
{
    int mid = left + ((right - left) >> 1);
    if (a[left] < a[right])
    {
        if (a[mid] < a[left])
            return left;
        else if (a[mid] < a[right])
            return mid;
        else
            return right;
    }
    if (a[right] > a[left])
    {
        if (a[mid] > a[right])
            return right;
        else if (a[mid] > a[left])
            return mid;
        else
            return left;
    }
}
int Partition(Datatype* a, int left, int right)
{
    int index = MidInRange(a,start,end);
    Swap(&a[index],&a[right]);
    Datatype key = a[right];
    int begin = left, end = right;
    while (begin < end)
    {
        while (begin < end&&a[begin] <= key)
            begin++;
        while (begin < end&& a[end] >= key)
            end--;
        Swap(&a[begin], &a[end]);
    }
    Swap(&a[begin], &a[right]);
    return begin;
}
//3.小区间优化法,当区间较小时可以采用插入排序,减少函数递归时调用栈帧的时间开销
void QuickSort(Datatype* a, int left, int right)
{
    assert(a);
    if (left >= right)
        return;
    if (right - left <= 10)
        InsertSort(a, right-left+1);
    int div = Partition(a, left, right);
    QuickSort(a, 0, div - 1);
    QuickSort(a, div + 1, right);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值