算法就是这么一回事(排序)(第三部分)

六、快速排序

  快速排序是通过一种把集合中的元素按照第一个元素(这个是动态过程变化)作为标杆来分为两部分,前面一部分比他小(或等),后面一部分比它大。然后就是通过适当的程序来递归这个过程,当最后没有交换说明需要退出递归。

  上图

  

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

步骤为:

  1. 从数列中挑出一个元素,称为 "基准"(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

代码:

  1 #include <iostream>
  2 #include <iterator>
  3 #include <vector>
  4 #include <ctime>
  5 #include <random>
  6 #include <functional>
  7 #include <algorithm>
  8 using namespace std;
  9 int intSwap(int& a,int& b)
 10 {
 11     int intswaptemp=a;
 12     a=b;
 13     b=intswaptemp;
 14     return 0;
 15 }
 16 /*----------------------------------------------------
 17 -----------------快速排序(STL版本)---------------------
 18 参数:迭代器、cmp
 19 cmp可以为less、greater等函数
 20   template<typename _Tp>
 21     struct less_equal : public binary_function<_Tp, _Tp, bool>
 22     {
 23       bool
 24       operator()(const _Tp& __x, const _Tp& __y) const
 25       { return __x <= __y; }
 26     };
 27 这个东西第一次学需要自己拿笔算写,不需要想,因为太费劲了。
 28 ----------------------------------------------------*/
 29 template<typename Conditerator,typename Compare>
 30 int quickSortIter(Conditerator begin,Conditerator end,Compare cmp)
 31 {
 32     if(begin!=end)//递归终止条件
 33     {
 34         Conditerator left=begin;//左
 35         Conditerator right=end;//右
 36         Conditerator pivot=left++;//用于作为参考相对大小的数子
 37         while(left!=right)
 38         {
 39             if(cmp(*left,*pivot))//从begin开始下一个比较是否小于begin,    left<begin(pivot)
 40                 ++left;//如果成立,left移向下一个未和begin比较的值
 41             else
 42             {
 43                 while((left!=right)&&cmp(*pivot,*right))//begin(pivot)<right
 44                     right--;
 45                 iter_swap(left,right);
 46             }
 47         }
 48         if(cmp(*pivot,*left))//这里就是为了防止left和right重合
 49             --left;//因为在上面程序中,最后会导致left和right重合,需要分离left
 50         iter_swap(begin,left);//保留了pivot,通过交换到前面一组中的最后一位
 51         quickSortIter(begin,left,cmp);
 52         quickSortIter(right,end,cmp);
 53     }
 54     return 0;
 55 }
 56 template<typename T>
 57 inline int quickSort(T begin,T end)
 58 {
 59     quickSortIter(begin,end,
 60                   less_equal<typename iterator_traits<T>::value_type>());
 61     return 0;
 62 }
 63 inline int QuickSortVector(vector<int> &ivec)
 64 {
 65     quickSort(ivec.begin(),ivec.end());
 66     return 0;
 67 }
 68 /*----------------------------------------------------
 69 -----------------快速排序(vector版本)------------------
 70 参数:vector<int>
 71 关键信息:通过合适的交换来实现,以第一个begin值为临时交换的参考值
 72 解释同上
 73 注意:一定要搞清楚>=和<=的逻辑关系,否则error或者死循环
 74 ----------------------------------------------------*/
 75 int quicksort_vector(vector<int>& ivec,int begin,int end)
 76 {
 77     if(begin!=end)
 78     {
 79         int left=begin;
 80         int right=end;
 81         int pivot=left++;//设置参考值(用于比较)
 82         while(left!=right)
 83         {
 84             if(ivec[pivot]>=ivec[left])
 85                 ++left;
 86             else
 87             {
 88                 while((left!=right)&&(ivec[pivot]<=ivec[right]))
 89                     --right;
 90                 intSwap(ivec[left],ivec[right]);
 91             }
 92         }
 93         if(ivec[pivot]<=ivec[left])
 94             left--;
 95         intSwap(ivec[begin],ivec[left]);
 96         quicksort_vector(ivec,begin,left);
 97         quicksort_vector(ivec,right,end);
 98     }
 99     return 0;
100 }
101 inline int quicksort1(vector<int> &ivec)
102 {
103     quicksort_vector(ivec,0,ivec.size()-1);
104     return 0;
105 }
106 int main()
107 {
108     clock_t start,end;
109     vector<int> ivec,copyivec;
110     srand(14);
111     for(int i=0;i<10000;i++)//10k
112         ivec.push_back((int)rand());
113     copyivec=ivec;
114     start=clock();
115     QuickSortVector(ivec);
116     end=clock();
117     for(int i=0;i<10000;i+=500)
118         cout<<ivec[i]<<‘\t‘;
119     cout<<endl;
120     cout<<"the time  of 1 is "<<end-start<<endl;
121     start=clock();
122     quicksort1(copyivec);
123     end=clock();
124     for(int i=0;i<10000;i+=500)
125         cout<<ivec[i]<<‘\t‘;
126     cout<<endl;
127     cout<<"the time  of 2 is "<<end-start<<endl;
128
129     return 0;
130 }

  乱数快速排序有一个值得注意的特性,在任意输入数据的状况下,它只需要O(n log n)的期望时间。是什么让随机的基准变成一个好的选择?

  假设我们排序一个数列,然后把它分为四个部份。在中央的两个部份将会包含最好的基准值;他们的每一个至少都会比25%的元素大,且至少比25%的元素小。如果我们可以一致地从这两个中央的部份选出一个元素,在到达大小为1的数列前,我们可能最多仅需要把数列分区2log2 n次,产生一个 O(nlogn)算法。

  不幸地,乱数选择只有一半的时间会从中间的部份选择。出人意外的事实是这样就已经足够好了。想像你正在翻转一枚硬币,一直翻转一直到有 k 次人头那面出现。尽管这需要很长的时间,平均来说只需要 2k 次翻动。且在 100k 次翻动中得不到 k 次人头那面的机会,是像天文数字一样的非常小。借由同样的论证,快速排序的递归平均只要2(2log2 n)的调用深度就会终止。但是如果它的平均调用深度是O(log n)且每一阶的调用树状过程最多有 n 个元素,则全部完成的工作量平均上是乘积,也就是 O(n log n)。

数据结构 不定
最差时间复杂度
最优时间复杂度
平均时间复杂度
最差空间复杂度 根据实现的方式不同而不同
时间: 2024-10-09 14:33:12

算法就是这么一回事(排序)(第三部分)的相关文章

算法 排序lowB三人组 冒泡排序 选择排序 插入排序

参考博客:基于python的七种经典排序算法   [经典排序算法][集锦]     经典排序算法及python实现 首先明确,算法的实质 是 列表排序.具体就是操作的列表,将无序列表变成有序列表! 一.排序的基本概念和分类 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法. 排序的稳定性: 经过某种排序后,如果两个记录序号同等,且两者在原无序记录中的先后秩序依然保持不变,则称所使用的排序方法是稳定的,反之是不稳定

算法就是这么一回事(排序)(第二部分)

上次说到冒泡排序 一共可以产生4种方式来产生,因为两个for循环皆可从小到大,也可从大到小 分类         排序算法数据结构     vector.数组最差时间复杂度     O(n^2)最优时间复杂度     O(n)平均时间复杂度     O(n^2)最差空间复杂度     总共O(n),需要辅助空间O(1)//用于交换所需临时变量 冒泡排序算法的运作如下: 1.   比较相邻的元素.如果第一个比第二个大,就交换他们两个. 2.   对每一对相邻元素作同样的工作,从开始第一对到结尾的最

算法一之简单选择排序

一.  选择排序的思想 选择排序的基本思想是:每一趟在n-i+1(i=1,2,-n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录.基于此思想的算法主要有简单选择排序.树型选择排序和堆排序. 简单选择排序的基本思想:第1趟,在待排序记录r[1]~r[n]中选出最小的记录,将它与r[1]交换:第2趟,在待排序记录r[2]~r[n]中选出最小的记录,将它与r[2]交换:以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕.

【数据结构】——排序算法——3.1、选择排序

      [数据结构]--排序算法--3.1.选择排序 一.先上维基的图: 分类 排序算法 数据结构 数组 最差时间复杂度 О(n2) 最优时间复杂度 О(n2) 平均时间复杂度 О(n2) 最差空间复杂度 О(n) total, O(1)auxiliary 二.描述: 选择算法算是最直观的一个了.每次在队列里抽取一个极大(或极小)值进行排列.每次都需要遍历未被抽取的元素队列. 三.Java程序: static void selection_sort(int[] unsorted) { for

算法系列(四)排序算法下篇--如何超越排序算法下界

概述 在算法系列(四)排序算法中篇--归并排序和快速排序一文中,我们介绍了归并排序和快速排序,最坏的情况下,最快的排序算法的时间复杂度是O(nlogn),是否有更好的算法呢?到目前为止,没有特殊的规则,O(nlogn)已经是最好的排序算法了,也就是说通用排序算法的时间复杂度下界就是O(nlogn).如果限定一些规则,是可以打破这个下界的.下面说一下尽在O(n)时间内就能实现对数组排序的算法. 基于排序的规则 基于什么样的规则才能突破排序的下界呢?我们需要分析一下排序消耗的时间.排序需要遍历,比较

排序算法之一--冒泡排序,选择排序,插入排序

一.排序算法定义 1.排序算法定义 排序算法是一种能将一串数据依照特定顺序进行排列的一种算法 2.六种排序算法理解方式 想象小时候老师给我们按照身高进行排队时用到的方法,脑子里面要浮现老师排身高的场面   以从矮到高进行排序为例 3.稳定性的定义 一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前. 二.三种基本排序算法 1.冒泡排序:"移" 把最高的移到最右边 第一次循环,找到最高的那个人放到最右边       方法

程序员的算法课(2)-排序算法

原文链接:https://www.jianshu.com/p/47170b1ced23 术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面: 内排序:所有排序操作都在内存中完成: 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行: 一.排序算法总结 n: 数据规模 k: “桶”的个数 In-place: 占用常数内存,不占用额外内存 Out-place: 占用额外内存

数据结构与算法系列十三(选择排序)

1.引子 1.1.为什么要学习数据结构与算法? 有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀! 有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗? 于是问题来了:为什么还要学习数据结构与算法呢? #理由一: 面试的时候,千万不要被数据结构与算法拖了后腿 #理由二: 你真的愿意做一辈子CRUD Boy吗 #理由三: 不想写出开源框架,中间件的工程师,不是好厨子 1.2.如何系统化学习数据结构与算法?

算法大神之路----排序(冒泡排序法)

冒泡排序法 冒泡排序法又称为交换排序法,是由观察水中冒泡变化构思而成,气泡随着水深压力而改变.气泡在水底时,水压最大,气泡最小,而气泡慢慢浮上水面时,气泡所受压力最小,体积慢慢变大. 冒泡排序比较方式是从第一个元素开始,比较相邻的元素大小,如果大小顺序有误,则对调后进行下一个元素比较.直到所有元素满足关系为止. 冒泡排序法分析 冒泡排序法平均情况下,需要比较(n-1)/2次,时间复杂度为O(n2),最好的情况只需要扫描一次,不用操作,即作n-1次比较,时间复杂度为O(n). 由于冒泡排序为相邻两

七月算法--12月机器学习在线班-第三次课笔记—矩阵和线性代数

七月算法--12月机器学习在线班-第三次课笔记—矩阵和线性代数 七月算法(julyedu.com)12月机器学习在线班学习笔记 http://www.julyedu.com