c语言数据结构 排序(二)

4.b

紧接上篇博客,对hoare版本的快排再进行优化。

1.三数取中:在数组的第一个位置、最后一个位置和最中间的位置选出中间值,作为key

解决:1.数组有序时快排效率慢的问题

           2.不会出现随机值数组中恰好挑选出最大或者最小值,影响快排的效率

2.小区间优化:最后三层要使用递归的子区间,均使用插入排序。

解决:1.递归太深会引发栈溢出

           2.当最后三层递归的子区间数据更接近有序性,快排的效率反而会低于插入排序。

为什么选择插入排序而不选择冒泡排序?

插入排序适用性强,可局部排序;而冒泡排序需要全部排序即数组整体有序才停止,条件性强。

呈上代码:

//三数取中
int GetMidIndex(int* a, int left, int right)
{
    int mid = left + (right - left) / 2;
    if (left < mid)
    {
        if (mid < right)
            return mid;
        else if (left > right)
            return left;
        else
            return right;
    }
    else
    {
        if (mid > right)
            return mid;
        else if (left < right)
            return left;
        else
            return right;
    }

}

// 快速排序前后指针法
int PartSort3(int* a, int left, int right);
void QuickSort(int* a, int left, int right)
{
    if (left >= right)
        return;
    int keyi = PartSort2(a, left, right);
    if (right - left <= 8)
    {
        InsertSort(a + left, right - left + 1);
    }
    else
    {
      QuickSort(a, left, keyi - 1);
      QuickSort(a, keyi + 1, right);
    }

}

4.c 挖坑法快排

思路:三数取中求得中间值的下标,再将其位置数据与数组左边第一个数据进行交换。数组左边第一个数据作为key,其位置为坑。Right在数组中从后往前找比key小的数,找到就直接将其赋值给坑位。坑的位置换到Right的位置。Left在数组中从前往后找比key大的数,找到就直接将其赋值给坑位。坑的位置换到Left的位置。最后再将key赋值给Right与Left相遇的坑位。此时找到key在数组中的真实位置。由此划分两个子区间,此过程反复进行确定某一数值在数组中的具体位置。

代码如下:

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
    int mid = GetMidIndex(a, left, right);
    Swap(&a[mid], &a[left]);

    int key = a[left];
    int hole = left;
    while (left < right)
    {
        while (left<right&&a[right] >= key)
        {
            right--;
        }
        a[hole] = a[right];
        hole = right;
        while (left<right&&a[left] <= key)
        {
            left++;
        }
        a[hole] = a[left];
        hole = left;
    }
    a[hole] = key;
    return hole;
}

4.d 前后指针快排法

思路:prev是数组左边第一个元素,cur是prev之后的元素。三数取中求得中间值的下标,再将其位置数据与数组左边第一个数据进行交换。数组左边第一个数据作为key。当初始时,cur++后找到的是连续不断的比key小的值,prev、cur++,之后cur继续找比key小的数(与之前连续不断的比key小的值间隔n个比key大的数),找到后prev再++,prev与cur所处位置的数据进行交换,直到cur指向空。这里一个重要的想法是分cur找到比key小的数时与prev的间隔,如果相邻两者同时++;如果不是,prev++后再进行数据的交换。

代码如下所示:

}
// 快速排序前后指针法--复杂版
//int PartSort3(int* a, int left, int right , int n)
//{
//    优化一:三数取中求keyi
//    int keyi = GetMidIndex(a, left, right);
//    Swap(&a[keyi], &a[left]);
//    keyi = left;
//    int prev = left;
//    int cur = prev + 1;
//    while (cur<left+n)
//    {
//        cur找到大数
//        if (a[cur] >= a[keyi])
//            cur++;
//        cur找到小数
//        else
//        {
//            
//            if (prev + 1 != cur)
//            {
//                prev++;
//                Swap(&a[prev], &a[cur]);
//                cur++;
//            }
//            else
//            {
//                prev++;
//                cur++;
//            }
//
//        }
//    }
//    Swap(&a[left], &a[prev]);
//    return prev;
//}
//
//---简便版
int PartSort3(int* a, int left, int right)
{
    
    int keyi = GetMidIndex(a, left, right);
    Swap(&a[keyi], &a[left]);
    keyi = left;

    int prev = left;
    int cur = prev + 1;
    while (cur <= right)
    {
        if (a[cur] < a[keyi] && ++prev != cur)
            Swap(&a[cur], &a[prev]);
        ++cur;
    }
    Swap(&a[prev], &a[keyi]);
    return prev;
}
 


4.e 非递归实现快排

思路:要想了解非递归实现快排,就要充分了解递归实现快排的内在核心。递归实现快排需要创建函数栈帧,而函数栈帧中存放的是区间。为模拟实现递归快排,必须要解决区间问题。

快排的区间类似于一个二叉树的结构。那么可以利用栈的先进后出的性质,模拟实现类似于二叉树的连续调用左子区间的形式。

代码实现如下:

需要引用栈的头文件和源文件

6.堆排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值