(三)几种排序算法的学习总结(快速排序)
来源:互联网 发布:威盾软件 编辑:程序博客网 时间:2024/05/29 08:16
快速排序:“二十世纪最伟大的算法”
核心思想:每次从数组中选择一个元素作为基点,之后把该元素移动到排好序时应该所处的位置,使得基点之前的元素都小于它、之后的元素都大于它。之后对小于它、大于它的子数组分别递归进行快速排序
将元素移动到合适位置的函数:Partition
通常使用第一个元素作为分界的标志点:索引——l,元素值——v
大于v与小于v的分界点索引:j
当前访问的元素索引:i,元素值:e
最终目的,使得:
arr[l+1…j]< v
arr[j+1….i-1]> v
分为两种情况:
1.e > v
2.e < v
再交换l与j位置的元素,使得基点在合适的位置上
//对arr[l...r]部分进行partition操作 //返回p,使得arr[l...p-1]<arr[p];arr[p+1...r]>arr[p] template<typename T>int __partition(T arr[],int l,int r){ //优化1:随机选取一个基数,而非最左端的元素 //swap(arr[rand()%(r-l+1)+l],arr[l]); //基数、标准 T v=arr[l]; //i是当前考虑的位置 //arr[l+1...j]<v;arr[j+1...i)>v int j=l; //使得两个子数组初始都为空 for(int i=l+1;i<=r;i++){ if(arr[i]<v){ swap(arr[++j],arr[i]); //j++; } } swap(arr[l],arr[j]); return j;} template<typename T>void __quickSort(T arr[],int l,int r){ //索引非法处理 if(l>=r) return; int p=__partition(arr,l,r); __quickSort(arr,l,p-1); __quickSort(arr,p+1,r);}template<typename T>void quickSort(T arr[],int n){ //优化1:设置随机种子 //srand(time(NULL)); __quickSort(arr,0,n-1);}
缺点1:对于近乎有序的数组排序效率低下,运行过程中形成的递归树平衡度较差,对于完全有序的数组,算法复杂度直接退化为O(n*n)。
优化1:随机选取排序的基准元素
缺点2:对于有大量重复键值的数组排序效率低下,同样可能造成算法复杂度退化为O(n^2)。
优化2:
//双路快速排序:template<typename T> int __partition2(T arr[],int l,int r){ swap(arr[rand()%(r-l+1)+l],arr[l]); T v=arr[l]; //arr[l+1...i]<=v;arr[j...r]>=v int i=l+1,j=r; while(true){ while(i<=r&&arr[i]<v)i++; while(j>=l+1&&arr[j]>v)j--; if(i>j)break; swap(arr[i],arr[j]); //索引移动到下一个要考察的位置 i++; j--; } //循环结束时三个索引: //i: 从前向后看第一个大于等于v的位置 //j: 从后向前看第一个(数组中最后一个)小于等于v的元素位置 //v: 小于等于v一端 swap(arr[l],arr[j]); return j; }
优化3:考虑到可能会有很多“等于v”的键值,还可以进一步优化出三路快速排序。增设一个存放“等于v”元素的子数组,直接省去这部分递归。
索引含义如下:
lt——指向“小于v”部分的最后一个元素,使得arr[l+1…lt] < v
gt——指向已经处理过的后面的第一个“大于v”部分的元素,使得arr[gt…r] > v
则arr[lt+1…i-1]==v
分情况讨论索引为i的元素:
1. e == v:该元素直接纳入“等于v”部分,i++
2. e < v :将该位置元素和“等于v”部分的第一个元素交换,lt++
3. e > v :将该位置元素直接与“gt-1”位置元素进行交换,gt–,i索引无需进行维护,依然指向了一个没有经过处理的元素
整个处理过程结束后,lt、gt分别指向了“小于v”部分和“大于v”部分的最后一个(第一个)元素,i与gt重合,此时交换l位置元素与lt位置元素即可
最后结果:
“小于v”部分:arr[l…lt-1] < v;
“等于v”部分:arr[lt…gt-1] ==v;
“大于v”部分:arr[gt…r] > v;
三路快速排序实现:
//三路快速排序处理:arr[l...r]//将arr[l...r]分为<v,==v,>v三个部分 //之后递归对<v,>v 两部分继续进行三路快速排序 template<typename T>void __quickSort3Ways(T arr[],int l,int r){ //递归结束判断 if(l>=r) return; //partition swap(arr[rand()%(r-l+1)+l],arr[l]); T v=arr[l]; //注意初始值的设定,必须满足三个子数组开始都为空 int lt=l; //arr[l+1...lt]<v int gt=r+1; //arr[gt...r]>v int i=l+1; //arr[lt+1...i)==v while(i<gt){ if(arr[i]<v){ swap(arr[i],arr[lt+1]); lt++; i++; } else if(arr[i]>v){ swap(arr[i],arr[gt-1]); gt--; //i索引无需处理,依然指向了一个未经过处理的元素 } else{//arr[i]==v i++; } } swap(arr[l],arr[lt]); __quickSort3Ways(arr,l,lt-1); __quickSort3Ways(arr,gt,r);}template<typename T>void quickSort3Ways(T arr[],int n){ srand(time(NULL)); __quickSort3Ways(arr,0,n-1);}
对于快速排序就介绍到这里了,更基础的选择、插入排序详见前两篇文章:http://blog.csdn.net/draper__qyt/article/details/77980407
http://blog.csdn.net/draper__qyt/article/details/77984775
注:本文的内容来自慕课网的课程《c++,java算法与数据结构》,主讲人为刘宇波老师~极力推荐!
这里附上课程链接:http://coding.imooc.com/class/71.html
- (三)几种排序算法的学习总结(快速排序)
- 算法学习(排序三)快速排序
- (一)几种排序算法的学习总结(选择排序与插入排序)
- 几种排序算法总结(冒泡、选择、插入、快速)
- 几种排序算法(冒泡排序算法,选择排序算法,快速排序算法,插入排序)
- 几种改良的排序,堆排序,希尔排序,快速排序--堆排序篇(改良的选择排序算法)
- 几种常用的排序算法(快速排序,希尔排序,堆排序,选择排序,冒泡排序)
- (二)几种排序算法的学习总结(归并排序)
- 几种快速的排序算法
- 快速排序算法的几种实现
- 三种快速排序算法以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- C语言常用的几种排序算法代码(选择排序,冒泡排序,插入排序,快速排序)
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 3532:最大上升子序列和(2.6基本算法之动态规划)
- Winform(XtraReport)实现打印方法(转载)
- 比特率与波特率的区别与联系
- hdu-5924-Mr. Frog’s Problem
- CVTE前端面试
- (三)几种排序算法的学习总结(快速排序)
- Candies SPFA + 栈
- text-justification
- 【ESP32视频分享3】Ubuntu使用 Eclipse开发ESP32程序
- Eclipse控制台log4j日志级不同颜色显示
- 微信小程序学习笔记 2.3.1 视图与渲染
- NPOI之Excel——合并单元格、设置样式、输入公式
- C语言读写mysql
- Servlet工作原理解析