在算法训练中,快排应该是基础中的基础了,直接使用前面介绍的快排,无论是单向循环还是双向循环方式,在特定的数据序列下,都有可能出现TLE(Time Limit Exceeded)超时,这篇文章对原因和基准值的优化进行验证和总结。

目录

  • 快排的效率的情况
  • 基准值选择优化
  • 双向循环 vs 单向循环
    • 方式1: 折半方式
    • 方式2: 随机方式
    • 折半方式 vs 随机方式
  • 总结
  • 附录
    • 附录1:缺省单向循环方式
    • 附录2: 缺省的双向循环方式
    • 附录3: 基准值折半方式的双向循环方式
    • 附录4: 基准值随机方式的双向循环方式

快排的效率的情况

快排本身不是一种稳定的算法,在前面的文章中也提到过,缺省的实现中一般直接选取最左或者最右进行,而这种方式之下,如果待排序的序列是已经有序的情况之下,快排的效率直接会恶化到N平方的时间复杂度,相当于排序核心的基准值分割算法几乎没有能起到作用,所以这种情况下出现超时也就可以理解了。这种情况之下,最简单的方式就是首先进行如下基准值选择的优化,这种情况就可以直接解决本身已经有序的大量带排序的算法的排序问题。


基准值选择优化

基准值最为简单的优化方式是可以不改变之前的逻辑,在进行排序之前首先按照某种方式选取一个,放至基准值选取的位置,与之进行交换,相当于打个补丁,之前的代码几乎不必修改。


双向循环 vs 单向循环

使用前文给出的实现示例的情况下,单向循环较为简略,在大量数据的情况下,前者的替换效率整体一般会高一些,但有时也不一定,对于不同的测试用例,单跑一次的效果可能也会不同,可以多试,测测人品。

  • 缺省的单向循环
    参看附录1的示例代码,执行时对于有序数列的效率如下所示,可以看到对于10万个有序数列,时间消耗已经达到10s级别的程度了
100
Time Used: 0.000014s
10000
Time Used: 0.105468s
100000
Time Used: 10.499863s
  • 缺省的双向循环
    参看附录2的示例代码,对于10万有序数列,不做任何操作也已经到10s级别了
100
Time Used: 0.000019s
10000
Time Used: 0.130322s
100000
Time Used: 12.846300s

方式1: 折半方式

选取待排序列的左右的中间值作为基准点,比如示例代码可能如下

    int key = (left+right)/2;int tmp=arr[left]; arr[left]=arr[key]; arr[key]=tmp;

使用附录3示例代码,运行结果如下所示,可以看到提升2个数量级到千万级别的有序序列,运行时间也仍然在毫秒级别0.665毫秒:

100
Time Used: 0.000005s
10000
Time Used: 0.000442s
100000
Time Used: 0.005531s
1000000
Time Used: 0.059314s
10000000
Time Used: 0.665215s

方式2: 随机方式

选取待排序列的左右的中间值作为基准点,比如示例代码可能如下

    int key = rand()%(right - left) + left;int tmp=arr[left]; arr[left]=arr[key]; arr[key]=tmp;

使用附录4的示例代码,执行结果如下所示

100
Time Used: 0.000015s
10000
Time Used: 0.000635s
100000
Time Used: 0.007567s
1000000
Time Used: 0.082657s
10000000
Time Used: 1.008981s

折半方式 vs 随机方式

一般来说,平均而言随机方式比折半方式效果要好一点。当然针对本文示例中已经完全有序的情况,显然折半方式更加接近于标准答案,但是不同的测试用例,平均效果而言,应该来说随机方式还是有其优势的,不过对于应试这种无聊的思路而言,果断换效果比较好的方式就可以了。


总结

使用双向循环+基准值的优化+平台整体优化选项的使用,一般来说可以解决TLE的问题


附录

附录1:缺省单向循环方式

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>int partition(int* arr, int start, int end) {if (start >= end ) return end;int pivot = arr[start];int index = start;for (int i=start+1; i<=end; i++) {if (arr[i] <= pivot) {index++;int tmp=arr[i]; arr[i]=arr[index]; arr[index]=tmp;}}arr[start]=arr[index];arr[index]=pivot;return index;
}void quick_sort(int* arr, int start, int end){if (start >= end) return;int pivot_index = partition(arr,start,end);quick_sort(arr,start,pivot_index-1);quick_sort(arr,pivot_index+1,end);
}int main() {int n = 0;while (scanf("%d",&n) != EOF) {int* array = (int *)malloc(sizeof(int)*n);memset(array,0,sizeof(int)*n);for (int i=0; i<n; i++) array[i]=i+1;time_t start_time, end_time;start_time=clock();quick_sort(array,0,n-1);end_time=clock();printf("Time Used: %fs\n",(double)(end_time-start_time)/CLOCKS_PER_SEC);free(array); array= NULL;}
}

附录2: 缺省的双向循环方式

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>int partition(int* arr, int start, int end) {int left=start, right=end;if (left >= right ) return right;int pivot = arr[left];while (left != right) {while (left < right && arr[right] > pivot) right--;while (left < right && arr[left] <= pivot) left++;if (left < right) {int tmp = arr[left]; arr[left]=arr[right]; arr[right]=tmp;}}arr[start]=arr[left];arr[left]=pivot;return right;
}void quick_sort(int* arr, int start, int end){if (start >= end) return;int pivot_index = partition(arr,start,end);quick_sort(arr,start,pivot_index-1);quick_sort(arr,pivot_index+1,end);
}int main() {int n = 0;while (scanf("%d",&n) != EOF) {int* array = (int *)malloc(sizeof(int)*n);memset(array,0,sizeof(int)*n);for (int i=0; i<n; i++) array[i]=i+1;time_t start_time, end_time;start_time=clock();quick_sort(array,0,n-1);end_time=clock();printf("Time Used: %fs\n",(double)(end_time-start_time)/CLOCKS_PER_SEC);free(array); array= NULL;}
}

附录3: 基准值折半方式的双向循环方式

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>int partition(int* arr, int start, int end) {int left=start, right=end;if (left >= right ) return right;int key = (left+right)/2;int tmp=arr[left]; arr[left]=arr[key]; arr[key]=tmp;int pivot = arr[left];while (left != right) {while (left < right && arr[right] > pivot) right--;while (left < right && arr[left] <= pivot) left++;if (left < right) {int tmp = arr[left]; arr[left]=arr[right]; arr[right]=tmp;}}arr[start]=arr[left];arr[left]=pivot;return right;
}void quick_sort(int* arr, int start, int end){if (start >= end) return;int pivot_index = partition(arr,start,end);quick_sort(arr,start,pivot_index-1);quick_sort(arr,pivot_index+1,end);
}int main() {int n = 0;while (scanf("%d",&n) != EOF) {int* array = (int *)malloc(sizeof(int)*n);memset(array,0,sizeof(int)*n);for (int i=0; i<n; i++) array[i]=i+1;time_t start_time, end_time;start_time=clock();quick_sort(array,0,n-1);end_time=clock();printf("Time Used: %fs\n",(double)(end_time-start_time)/CLOCKS_PER_SEC);free(array); array= NULL;}
}

附录4: 基准值随机方式的双向循环方式

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>int partition(int* arr, int start, int end) {int left=start, right=end;if (left >= right ) return right;int key = rand()%(right - left) + left;int tmp=arr[left]; arr[left]=arr[key]; arr[key]=tmp;int pivot = arr[left];while (left != right) {while (left < right && arr[right] > pivot) right--;while (left < right && arr[left] <= pivot) left++;if (left < right) {int tmp = arr[left]; arr[left]=arr[right]; arr[right]=tmp;}}arr[start]=arr[left];arr[left]=pivot;return right;
}void quick_sort(int* arr, int start, int end){if (start >= end) return;int pivot_index = partition(arr,start,end);quick_sort(arr,start,pivot_index-1);quick_sort(arr,pivot_index+1,end);
}int main() {int n = 0;while (scanf("%d",&n) != EOF) {int* array = (int *)malloc(sizeof(int)*n);memset(array,0,sizeof(int)*n);for (int i=0; i<n; i++) array[i]=i+1;time_t start_time, end_time;start_time=clock();quick_sort(array,0,n-1);end_time=clock();printf("Time Used: %fs\n",(double)(end_time-start_time)/CLOCKS_PER_SEC);free(array); array= NULL;}
}

算法基础:快排优化:为什么快排都会TLE相关推荐

  1. 【人工智能算法】算法基础之离散优化[旅行商问题 背包问题]

    本文重点: 离散VS连续 旅行商问题 背包问题 旅行商问题 旅行商问题(Traveling Salesman Problem, TSP)是一个难以用传统迭代算法求解的NP困难问题,因此经常用模拟退火算 ...

  2. L-BFGS算法基础_matlab

    L-BFGS算法基础_matlab L-BFGS 优化基础 考虑利用递归展开的方式进行求解 第一个循环: 第二个循环: L-BFGS 算法_matlab 初始化和迭代准备 迭代主循环 L-BFGS 双 ...

  3. 八大排序算法之快速排序(上篇)(未经优化的快排)

    目录 一.关于快速排序的总体算法思想 1.冒泡排序(交换排序) (以排升序为例) 2.快速排序的总体思想简介(以排升序为例) 二.快速排序单趟排序的算法接口设计(以排升序为例) 单趟排序实现的方法一: ...

  4. 数据结构之排序【归并排序和快排的顶级优化和快排的三种原理的实现及分析】 内含动态演示图

    文章目录 引言: 1.归并排序(MergeSort) 2.快速排序的优化(顶级优化) 3.快速排序的三种思路的代码实现及分析 4.归并排序和快排第3原理的测试 引言: 刚刚去回顾了一下递归实现的几个小 ...

  5. 快排三种基本解法以及两种快排优化

    /*  快速排序  基本思想    选定每次排序的基准数据 在剩下的位置将小于基准值的数据放在基准值得左边,大于基准值的数据放到基准值的右边    一次划分之后 如果此基准值的左右两边仍存在大于两个数 ...

  6. c++ 快排优化(三数取中法)

    快排优化(三数取中法) 文章目录 快排优化(三数取中法) 前言 一.三数取中法 二.递归思想 三.程序实现过程(代码) 1.取基准数(三数取中) 2.快速排序(递归) 总结 前言 作为刚刚入门c和c+ ...

  7. 归并排序、快速排序、二路快排、三路快排python实现

    源代码: https://github.com/lzneu/Algrithm_python O(n*logn)级别的排序: |-归并排序     分成log(n)个层级 每个层级进行O(n)排序   ...

  8. 数学之美番外篇:快排为什么那样快

    目录 0. 前言 1. 猜数字 2. 称球 3. 排序 3.1 为什么堆排比快排慢 3.2 为什么快排其实也不是那么快 3.3 基排又为什么那么快呢 4. 信息论!信息论? 5. 小结 0. 前言 知 ...

  9. 分治法:快速排序,3种划分方式,随机化快排,快排快,还是归并排序快?

    快速排序不同于之前了解的分治,他是通过一系列操作划分得到子问题,不同之前的划分子问题很简单,划分子问题的过程也是解决问题的过程 我们通常划分子问题尽量保持均衡,而快排缺无法保持均衡 快排第一种划分子问 ...

  10. 【LeetCode笔记】215. 数组中的第K个最大元素(Java、快排、堆排、并发快排)

    文章目录 题目描述 思路 & 代码 快排 基于 Fork / Join 的并发快排 针对 topK 的快排优化 堆排 基本堆排 结合题目的堆排 二刷 题目描述 大名鼎鼎的TOP K,主要考察排 ...

最新文章

  1. 用链表生成前序二叉树
  2. [Z]应试生涯结束后,美国亚裔的那群超优生都过得如何?来源: 李璐瑶的日志...
  3. centos 上传jar 命令_centos上快速将一个jar使用docker部署启动
  4. 课时18:函数:灵活即强大
  5. MySQL出现慢日志超过2秒_MySQL慢日志功能分析及优化增强
  6. tcp 三次握手与四次挥手_TCP三次握手与四次挥手详解
  7. liunx的百度输入法如何卸载_测试面试题集锦liunx与网络
  8. Android深入四大组件(一)应用程序启动过程
  9. Linux 查看日志命令tail的用法
  10. 微软背叛wintel联盟,Intel似乎正陷入四面楚歌之中
  11. Tushare介绍和入门级实践(2)——使用tushare接口获取沪深300成分股的财报数据并输出到本地
  12. JavaScript设计模式与开发实践
  13. WindowsCMD配置代理
  14. 泛泛而谈的菜鸟学习记录(四)—— Vorley噪声生成原理及噪声边缘提取
  15. 台式计算机能装蓝牙吗,台式电脑没有蓝牙功能怎么安装
  16. 利用自带命令 手动杀毒
  17. -webkit-tap-highlight-color
  18. Matlab 计算显色指数(Ra,R1-R15)、CCT、duv、Lux、XYZ三刺激值、CIE1931x、CIE1931y
  19. 使用electron-builder --win --x64 命令 app-builder-bin\win\x64\app-builder.exe 报错
  20. win7桌面便笺:启动时提示windows7部分便笺的元数据已被损坏

热门文章

  1. 红米4x android7.0,效率太惊人 红米Note 4X也迎来Android 7.0升级
  2. 函数原型中int *arr和int arr[]
  3. 流氓软件强夺用户数据,马斯克截胡扎克伯格!
  4. 如何查询主机IP地址
  5. XXXXXXXXXXX学校“专家问诊课”活动方案
  6. 处理器协同机制其三C++内存顺序与栅栏(及依赖性读屏障)
  7. Standard EVB硬件开发指南(1)——LCD接口电路
  8. 基于SkeyeVSS综合安防监控视频云平台实现智能视频监控系统解决方案
  9. leaflet实现风场流动
  10. 明日书苑:书法用笔挫笔、疾涩、接笔、搭锋、裹锋、平铺、筑锋、过