快速排序quicksort算法细节优化(一次申请内存/无额外内存排序)

版权声明:转载请注明出处,如有侵权地方请联系删除 https://blog.csdn.net/qq_21201267/article/details/81516569

对链接中快速排序进行代码优化

https://blog.csdn.net/qq_21201267/article/details/80993672#t6

1.只申请一次内存,避免多次递归调用时反复的申请和释放内存,提高程序运行效率

/*
 * 6-1-opti1.快速排序(best version)(三数取中基准+希尔排序+基准群)(opti1,只申请一次内存)
 * 对数组找出一个中间大小的合适哨兵,把小于哨兵的放左边,大于哨兵的放右边,中间是等于哨兵的
 * 分别对左右递归调用快排
 */

void partion1_opti1(int *arr, size_t left, size_t right, size_t &lessPnum, size_t &largePnum, 
						int *temp)//数据分段
{
    selectmedianofthree1(arr,left,right);  //找出中间大小的哨兵,让分段尽量均匀,提高效率
    int pval = arr[left];  //中间大小的数赋值给哨兵
    int tempLindex=0, tempRindex = right-left;  //临时数组的首末位下标
    for(int i = left+1; i <= right; ++i)
    {
        if(pval > arr[i]) //比哨兵小的放在左边,从左边首位往中间写入,记录下比哨兵小的有多少个
        {
            temp[tempLindex++] = arr[i];
            ++lessPnum;
        }
        if(pval < arr[i])  ////比哨兵大的放在右边,从右边末位中间写入,记录下比哨兵大的有多少个
        {
            temp[tempRindex--] = arr[i];
            largePnum++;
        }
    }
    for( ; tempLindex <= tempRindex; ++tempLindex)
    //中间还未被写入的位置,写入哨兵(哨兵可能是多个相同的值)
    {
        temp[tempLindex] = pval;
    }
    for(int i = left, j=0; i <= right; ++i)
    {
        arr[i] = temp[j++]; //把分好段的数组写回原数组{[小于哨兵的],[等于哨兵的],[大于哨兵的]}
    }
}
void qsort1_opti1(int *arr, size_t left, size_t right, int deep, int *temp)
{
    if(left >= right)
    {
        return;
    }
    else if(right-left == 1)
    //只有两个数直接比较交换(也可以设置长度小于X(比如10),调用其他排序,如归并,减少不必要的调用快排)
    {
        if(arr[left]>arr[right])
        {
            swap(arr[left], arr[right]);
        }
    }
    else if(right-left > 1 && right-left < 20)  //数组长度较小时,调用希尔排序,减少调用快排
    {
        size_t len = right - left + 1;
        shellsort(len, &arr[left]); //数组首地址为&arr[left]
    }
    else
    {
        size_t lessPnum = 0, largePnum=0;
        partion1_opti1(arr,left,right,lessPnum,largePnum,temp);  
        //数据分段,{[小于哨兵],[等于哨兵],[大于哨兵]}
        size_t pl_index = left + lessPnum;  //首位哨兵的下标
        size_t pr_index = right - largePnum;  //末位哨兵的下标
        if(pr_index == right && pl_index != left)  //哨兵群位于数组最右边,且左边还有数据
        {
            qsort1_opti1(arr,left,pl_index-1,deep,temp); //只对左边非哨兵数据快排
        }
        else if(pl_index == left && pr_index != right)  //哨兵群位于数组最左边,且右边还有数据
        {
            qsort1_opti1(arr,pr_index+1,right,deep,temp);  //只对右边非哨兵数据快排
        }
        else if(pl_index == left && pr_index == right) //全部是哨兵,两侧无数据,退出
        {
            return;
        }
        else  //两侧都有非哨兵数据,对两侧调用快排
        {
            qsort1_opti1(arr,left,pl_index-1,deep,temp);
            qsort1_opti1(arr,pr_index+1,right,deep,temp);
        }
    }
}
void quicksort1_opti1(size_t dsize, int *arr)
{
    if(dsize <= 1)  //预防特殊情况下后面代码失效
    {
        return;
    }
    size_t left = 0, right = dsize-1;
    int deep = 0;  //可以打印显示出调用的层数
    int *temp = new int [dsize];  //一次性开辟堆空间存放临时数组
    qsort1_opti1(arr,left,right,deep,temp);
    delete [] temp; //释放临时数组
    temp = NULL;  //指针置空
}

运行比较: 优化1效率提升
这里写图片描述

2.不申请内存,在原数组上直接排序

/*
 * 6-1-opti2.快速排序(best version)(三数取中基准+希尔排序+基准群)(不申请内存)
 * 对数组找出一个中间大小的合适哨兵,把小于哨兵的放左边,大于哨兵的放右边,中间是等于哨兵的
 * 分别对左右递归调用快排
 */
void partion1_opti2(int *arr, size_t left, size_t right, size_t &pl_index, size_t &pr_index)
	//数据分段
{
    selectmedianofthree1(arr,left,right);  //找出中间大小的哨兵,让分段尽量均匀,提高效率
    int pval = arr[left];  //中间大小的数赋值给哨兵
    size_t i = left, j = right;
    while(i < j)
    {
        while(i < j && pval <= arr[j]) //把<=改成<,则哨兵群都在左边,下面相应代码可减少
            --j;
        swap(arr[i],arr[j]);
        while(i < j && pval >= arr[i])// =号至少有一个才行,一个等号,忽略下面半边集合哨兵代码是最高效的
            ++i;
        swap(arr[i],arr[j]);
    }
    size_t pindex = i;
    size_t leftpnum = 0, rightpnum = 0; //左右跟哨兵相等的元素个数
    pl_index = pindex;//记得初始化!!!之前没有写,假如进不去for,没有初始化,就越界了
    pr_index = pindex;
    if(pindex != 0)//!!!如果pindex = 0,下面 i = pindex - 1 越界
    {
        for(i = pindex-1; i >= left; --i)//左边哨兵群向中间集合,哨兵都在右边,即可忽略以下代码
        {
            if(arr[i] == pval)
            {
                ++leftpnum;
                pl_index = pindex - leftpnum;
                swap(arr[i],arr[pl_index]);
            }
            if(i == left)    //size_t 做减法要小心越界
                break;
        }
    }
    for(i = pindex+1; i <= right; ++i)//右边哨兵群向中间集合,哨兵都在左边边,即可忽略以下代码
    {
        if(arr[i] == pval)
        {
            ++rightpnum;
            pr_index = pindex + rightpnum;
            swap(arr[i],arr[pr_index]);
        }
    }
}
void qsort1_opti2(int *arr, size_t left, size_t right, int deep)
{
    if(left >= right)
    {
        return;
    }
    else if(right-left == 1)
        //只有两个数直接比较交换(也可以设置长度小于X(比如10),调用其他排序,如归并,减少不必要的调用快排)
    {
        if(arr[left]>arr[right])
        {
            swap(arr[left], arr[right]);
        }
    }
    else if(right-left > 1 && right-left < 20)  //数组长度较小时,调用希尔排序,减少调用快排
    {
        size_t len = right - left + 1;
        shellsort(len, &arr[left]); //数组首地址为&arr[left]
    }
    else
    {
        size_t pl_index;  //首位哨兵的下标
        size_t pr_index;  //末位哨兵的下标
        partion1_opti2(arr,left,right,pl_index,pr_index); //数据分段{[小于哨兵][等于哨兵][大于哨兵]}
        if(pr_index == right && pl_index != left)  //哨兵群位于数组最右边,且左边还有数据
        {
            qsort1_opti2(arr,left,pl_index-1,deep); //只对左边非哨兵数据快排
        }
        else if(pl_index == left && pr_index != right)  //哨兵群位于数组最左边,且右边还有数据
        {
            qsort1_opti2(arr,pr_index+1,right,deep);  //只对右边非哨兵数据快排
        }
        else if(pl_index == left && pr_index == right) //全部是哨兵,两侧无数据,退出
        {
            return;
        }
        else  //两侧都有非哨兵数据,对两侧调用快排
        {
            qsort1_opti2(arr,left,pl_index-1,deep);
            qsort1_opti2(arr,pr_index+1,right,deep);
        }
    }
}
void quicksort1_opti2(size_t dsize, int *arr)
{
    if(dsize <= 1)  //预防特殊情况下后面代码失效
    {
        return;
    }
    size_t left = 0, right = dsize-1;
    int deep = 0;  //可以打印显示出调用的层数
    qsort1_opti2(arr,left,right,deep);
}

运行效率:
这里写图片描述
这里写图片描述

优化比较总结

以下数据为5次运行的平均数据
这里写图片描述
windows下效率提升:optimization1 -----6%----- optimization2 -----14%-----
linux 下效率提升:optimization1 -----2%----- optimization2 -----20%-----

测试程序运行时间shell脚本
https://blog.csdn.net/qq_21201267/article/details/81840299

最后特别感谢阿福同学的帮忙调试找BUG!!!

猜你喜欢

转载自blog.csdn.net/qq_21201267/article/details/81516569