欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > 快速排序的性能优化

快速排序的性能优化

2025/2/23 14:24:57 来源:https://blog.csdn.net/LVZHUO_2022/article/details/143648151  浏览:    关键词:快速排序的性能优化

目录

引入

小优化——三数取中

 三路划分

 终极大招——自省排序


引入

        在此篇文章中已经介绍了快速排序的多种实现方式:快速排序的多种实现方式-CSDN博客

        毋庸置疑的是快速排序是一种非常优秀的排序方式,因此被广泛的使用也凸显出快速排序的重要地位。但是快速排序的普通实现方式在面对一些特殊场景使就会显得非常笨重,比如待排序的数组为有序的情况下,每次划分只得到一个比上一次少一个记录的子序列,时间复杂度就变为:O( N* N )

小优化——三数取中

“ 三数取中 ” 规则:

        比较当前数组中第一个记录、最后一个记录和中间一个记录的关键字,取三个关键字中大小为中间的记录作为枢轴记录,并将其与第一个记录的位置进行交换

         图解:

         代码实现:

//快速排序——双指针
void QuickSort(int* arr, int begin, int end)
{if (begin >= end){return;}//三数取中int pos = begin;int mid = (begin + end) / 2;if (arr[pos] < arr[mid]){pos = mid;}if (arr[pos] > arr[end]){pos = end;}swap(&arr[begin], &arr[pos]);//基准位置int key = pos;//始终指向比基准小的位置,后续与 cur 交换int prev = begin;//探路,找比基准值小的数int cur = prev + 1;while (cur <= end){//找到比基准值小的数,与 prev 先加一在交换if (arr[cur] <= arr[key]){//慢指针先加一prev++;swap(&arr[cur], &arr[prev]);}cur++;}//基准与prev交换swap(&arr[key], &arr[prev]);//二分QuickSort(arr, begin, prev - 1);QuickSort(arr, prev + 1, end);
}

         三数取中理论上可以说在平均情况下,快速排序的时间复杂度为O( NlogN ),但是在面对有重复大量的数组时,快速排序的时间复杂度还是O( N*N ),比如:

 三路划分

        当面对数组中有大量重复元素时,三路划分核心思想有点类似于【博客快速排序的多种实现方式-CSDN博客】当中讲的【hoare法】和【快慢指针法】的结合。其主要思想就是将数组中的数分为三个部分【比基准小的值】【与基准相等的值】【比基准大的值】,因此被称为三路划分算法

算法思路:

  1. key 默认为 left 位置 
  2. left 指向数组的最左边,right 指向数组的最右边,cur 指向 left + 1 的位置
  3. cur 遇到比 key 小的值后跟 left 位置进行交换,随后 left++,cur++
  4. cur 遇到比 key 大的值后跟 right 位置进行交换,随后 right--
  5. cur 遇到与 key 相等的值时 cur++
  6. 直到 cur > right 结束

        图解: 

 

        代码实现: 

//交换两个数的值
void swap(int* num1, int* num2)
{int tmp = *num1;*num1 = *num2;*num2 = tmp;
}
//快速排序——三路划分
void QuickSort(int* arr, int begin, int end)
{if (begin >= end){return;}//三数取中int pos = begin;int mid = (begin + end) / 2;if (arr[pos] < arr[mid]){pos = mid;}if (arr[pos] > arr[end]){pos = end;}swap(&arr[begin], &arr[pos]);//left 指向数组的最左边//right 指向数组的最右边int left = begin;int right = end;//key 默认为 left 位置 int key = arr[begin];//cur 指向 left + 1 的位置int cur = left + 1;while (cur <= right){//三路划分if (arr[cur] == key){//cur 遇到与 key 相等的值时 cur++cur++;}else if (arr[cur] < key){//cur 遇到比 key 小的值后跟 left 位置进行交换,随后 left++,cur++swap(&arr[left], &arr[cur]);left++;cur++;}else{//cur 遇到比 key 大的值后跟 right 位置进行交换,随后 right--swap(&arr[right], &arr[cur]);right--;}}//二分QuickSort(arr, begin, left - 1);QuickSort(arr, right + 1, end);
}

 终极大招——自省排序

        这个排序方式也是 C++ 标准库中排序方法所采用的方式

算法思路        

        自省排序它的思路就是进行自我侦察和反省,当快速排序递归的深度太深时( C++ STL 中使用的是深度为2倍排序元素数量的对数值)就说明在当前子序列中,选择基准位置出现了问题,性能在快速退化。那就不要进行快速排序分割递归了,该换为堆排序进行排序

        代码实现: 

         比较复杂,需要一点理解力

//向下调整算法(大根堆)
void AdjustDown(int* arr, int size, int pos)
{//左孩子位置int child = pos * 2 + 1;//向下调整算法,直到左孩子位置大于数组个数if (child < size){//选出左右孩子中最大的那个孩子if (child + 1 < size && arr[child] < arr[child + 1]){child++;}//与当前位置进行比较//如果左右孩子中最大数大于当前位置的数,就进行交换,并且继续向下调整//如果左右孩子中最大数小于当前位置的数,表示堆已经调整好了if (arr[child] > arr[pos]){//交换两个数的位置swap(&arr[pos], &arr[child]);//继续向下调整AdjustDown(arr, size, child);}}
}//堆排序——升序
void HeapSort(int* arr, int size)
{//从后往前依次调整建堆//先让节点的左右子树为堆,然后整体为堆for (int i = size - 1; i >= 0; i--){//向下调整建堆AdjustDown(arr, size, i);}//堆排序//排升序要建小堆//排降序要建大堆for (int i = 0; i < size; i++){//堆顶与最后一个有效元素交换位置swap(&arr[0], &arr[size - 1 - i]);//向下调整,保持堆的结构AdjustDown(arr, size - i - 1, 0);}
}
//快速排序——自省排序
void _QuickSort(int* arr, int begin, int end, int deep, int maxdeep)
{if (begin >= end){return;}//当到达指定的最大深度时,改变使用堆排序if (deep > maxdeep){HeapSort(arr + begin, end - begin + 1);return;}//三数取中int pos = begin;int mid = (begin + end) / 2;if (arr[pos] < arr[mid]){pos = mid;}if (arr[pos] > arr[end]){pos = end;}swap(&arr[begin], &arr[pos]);//基准位置int key = begin;//始终指向比基准小的位置,后续与 cur 交换int prev = begin;//探路,找比基准值小的数int cur = prev + 1;while (cur <= end){//找到比基准值小的数,与 prev 先加一在交换if (arr[cur] <= arr[key]){//慢指针先加一prev++;swap(&arr[cur], &arr[prev]);}cur++;}//基准与prev交换swap(&arr[key], &arr[prev]);//二分_QuickSort(arr, begin, prev - 1, deep + 1, maxdeep);_QuickSort(arr, prev + 1, end, deep + 1, maxdeep);
}
void QuickSort(int* arr, int begin, int end)
{int maxdeep = 0;//计算数组个数int n = end - begin + 1;//确定最大深度for (int i = 1; i < n; i *= 2){maxdeep++;}//自省排序,C++ STL 中使用的是深度为2倍排序元素数量的对数值_QuickSort(arr, begin, end, 0, maxdeep * 2);
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词