随机快速排序(内嵌插入排)

本文介绍了一种针对快速排序算法的小数组优化方法,通过在数组长度小于特定阈值时切换至插入排序,有效提升整体排序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.思路
在快速排序算法的递归实现中,存在一种不太好的现象:随着递归层层深入,大量数据被分割成了小数组;快排对于大数组的划分可以迅速地将元素移动到它正确位置的附近,比如说对1024进行一次均等划分,那么某个元素可能会移动数百个单位位置,若进行4次均等划分,元素在正确位置上的概率就从1/1024骤升到1/64,考虑到64与1024的绝对数值,这是相当高的效率;然而对于小数组,快速排序的效率就不那么理想了,对于16个元素的数组,快速排序也要划分4次才能把它移动到正确的位置上,相对于之前几百个位置的移动,小数组排序一次只能移动几个单位的位置。

换句话说,快速排序对少量数据的划分远不如它对大量数据的划分这么划算,当排序进入到小数组阶段后,它将多次因为这些小数组而频繁调用自身,但获得的收益并不大。我姑且把这种现象叫做“小数组的边际效益“

因此,在待排序数组长度小于某一阈值K时(代码中K = 3),用插入排序替代快速排序,取长补短让代码效率达到最高,其中任然选用随即快速排序。

2.完整代码

#include <iostream>
#include <ctime>
using namespace std;

const int K = 3;

void InitArr(int *nArr, int nLen) {     //初始化数组
    //srand(time(NULL));
    for(int i = 0; i < nLen; ++i) {
        //nArr[i] = rand() % 100;
        nArr[i] = i;
    }
}

void PrintArr(int *nArr, int nLen) {
    for(int i = 0; i < nLen; ++i) {
        cout << nArr[i] << " ";
    }
    cout << endl;
}

void Swape(int *p1, int *p2) {
    int nTmp = *p1;
    *p1 = *p2;
    *p2 = nTmp;
}

void RandomSort(int *nArr, int nLen) {
    srand(time(NULL));
    for(int i = 0; i < nLen; ++i) {
        int nIndex = rand() % nLen;
        Swape(&nArr[i], &nArr[nIndex]);
        //Sleep(2000);                       //等待2s,更新随机种子
    }
}



//递增排序
int PartitionUp(int *nArr, int nLeft, int nRight) {
    int nKey = nArr[nRight];
    int i = nLeft - 1;

    for(int j = nLeft; j < nRight; ++j) {
        if(nArr[j] < nKey) {    //反正不稳定,就用<代替≤,省取相等情况下多余的交换运行时间
            i++;
            Swape(&nArr[j], &nArr[i]);     //不稳定排序的原因 eg:1 5 8 8 6 11 7
        }
    }
    Swape(&nArr[i + 1], &nArr[nRight]); //将主元素插入到中间
    return i + 1;
}

//递减排序
int PartitionDown(int *nArr, int nLeft, int nRight) {
    int nKey = nArr[nRight];
    int i = nLeft - 1;

    for(int j = nLeft; j < nRight; ++j) {
        if(nArr[j] > nKey) {
            ++i;
            Swape(&nArr[i], &nArr[j]);          //不稳定排序的原因
        }
    }
    Swape(&nArr[i + 1], &nArr[nRight]);       //将主元素插入到中间
    return i + 1;
}

int RandomPartition(int *nArr, int nLeft, int nRight) {
    srand(time(NULL));
    int nKey = rand() % (nRight - nLeft + 1) + nLeft;   //随机选择【nLeft, nRight】内的某一个数
    Swape(&nArr[nKey], &nArr[nRight]);                    //将选择的数与nArr[nRight]交换,即将选择的数作为Key值进行排序
    return PartitionUp(nArr, nLeft, nRight);
}

void InsertSort(int *nArr, int nLeft, int nRight) {
    for(int i = nLeft + 1; i <= nRight; ++i) {
        int nTmp = nArr[i];
        int j;
        for(j = i - 1; j >= nLeft && nArr[j] > nTmp; --j) {
            nArr[j + 1] = nArr[j];
        }
        nArr[j + 1] = nTmp;
    }
}

void QuickSort(int *nArr, int nLeft, int nRight) {
    if(nLeft < nRight) {
        //分解
        int nTmpPos = PartitionUp(nArr, nLeft, nRight);

        //解决、合并
        QuickSort(nArr, nLeft, nTmpPos - 1);
        QuickSort(nArr, nTmpPos + 1, nRight);
    }
}

void QuickSortRandom(int *nArr, int nLeft, int nRight) {
    if(nLeft < nRight) {
        int nTmpPos = RandomPartition(nArr, nLeft, nRight);
        QuickSortRandom(nArr, nLeft, nTmpPos - 1);
        QuickSortRandom(nArr, nTmpPos + 1, nRight);
    }
}

void QuickInsertSortRandom(int *nArr, int nLeft, int nRight) { //主要代码
    if(nLeft < nRight) {
        if(nRight - nLeft > K) {
            int nTmpPos = RandomPartition(nArr, nLeft, nRight);
            QuickInsertSortRandom(nArr, nLeft, nTmpPos - 1);
            QuickInsertSortRandom(nArr, nTmpPos + 1, nRight);
        } else {
            InsertSort(nArr, nLeft, nRight);  //换用插入排序
        }
    }
}


int main() {
    int Arr[1000];
    int Len = 20;
    InitArr(Arr, Len);
//    PrintArr(Arr, Len);
    RandomSort(Arr, Len);
    PrintArr(Arr, Len);
//    QuickSort(Arr, 0, Len - 1);
    QuickInsertSortRandom(Arr, 0, Len - 1);
//    InsertSort(Arr, 0, Len - 1);
    PrintArr(Arr, Len);
    return 0;
}

3.运行结果与算法效率同前文。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值