几种常见排序算法的C++描述
基本的排序算法有如下特点:
1.几种容易的算法都是以O(N2)排序的
2.Shell排序编程简单,其也是以O(N2)排序的,在实践中用的很多
3.复杂的排序算法往往都是按照O(NlogN)尽心排序的
4.任何通用的排序算法都需要NlogN次比较。
还有两个定理应该记一下,证明略:
1. N个互异数组的平均逆序数是N(N-1)/4
2. 通过交换相邻元素进行排序的任何算法平均都需要O(N^2)的时间
插入排序
首先当然是插入排序啦,算法的时间复杂度其实是O(N^2)的
先来个一般的描述:
- template<typename Comparable>
- void insertSort(vector<Comparable> & a)
- {
- int j;
- for (int p = ; p < a.size(); p++)
- {
- Comparable tmp = a[p];
- for (int j = p - ; j > && tmp < a[j - ]; j--)
- a[j] = a[j - ];
- a[j] = tmp;
- }
- }
再就是stl的描述,主要分为四个部分,比较麻烦
- template <typename Iterator>
- void insertionSort(const Iterator & begin, const Iterator & end)
- {
- if (begin != end)
- insertSortionHelp(begin, end, *begin);//用于解析类型
- }
- template <typename Iterator, typename Object>
- void insertionSortHelp(const Iterator & begin, const Iterator & end, const Object & obj)
- {
- insertionSort(begin, end, less<Object>());
- }
- template <typename Iterator, typename Comparator>
- void insertionSort(const Iterator & begin, const Iterator & end, Comparator lessThan)
- {
- if (begin ! = end)
- insertionSort(begin, end, lessThan, *begin);
- }
- template <typename Iterator, typename Comparator, typename Object>
- void insertionSort(const Iterator & begin, const Iterator & end, Comparator lessThan, const Object & obj)
- {
- Iterator j;
- for (Iterator p = begin + ; p != end; p++)
- {
- Object tmp = *p;
- for (j = p; j != begin && lessThan(tmp, *(j - )); --j)
- *j = *(j - );
- *j = tmp;
- }
- //这整个4个函数组成了一个STL,很麻烦所以一般不用STL界面
- }
可以讨论一下插入排序的排序的时间复杂度:
2+3+4+...+n = O(N^2)
还可以总结出来的一个特点是,如果插入排序的的输入数据预先已经排好序号了,那么内层循环会直接跳出,这样时间复杂度就会被降到O(N).
也就是说,对于输入几乎被排序了的序列来说,插入排序将很快完成。
希尔排序
希尔排序其实相当于插入排序的不同间隔的版本,希尔排序的平均时间复杂度实际很大程度的依赖于增量序列的选择,同样可以证明得知其算法复杂度为O(N^2)的。
- template<typename Comparable>
- void shellSort(vector<Comparable > & a)
- {
- for (gap = a.size() / ; gap > ; gap /= )
- {
- for (int i = gap; i < a.size(); i++)
- {
- Comparable tmp = a[i];
- int j = i;
- for (; j > gap && tmp < a[j - gap]; j -= gap)
- a[j] = a[j - gap];
- a[j] = tmp;
- }
- }
- }
应该注意一点的是,shell排序的最坏情形的复杂度为O(N^2)
堆排序
堆排序的算法思想其实很简单,先是建立一个堆,建堆时的时间复杂度实际上是O(N),可以先按照逆序建堆,然后在一个一个的把堆中最大的元素,放到数组的最后一个位置,这个过程的时间复杂度是logN,后面其实相当于还要建立N次堆。所以总的时间复杂度就是Nlog(N)的。要点就是不停的建立新的堆。
- template <typename Comparable>
- void heapSort(vector<Comparable> & a)
- {
- for (int i = a.size() / ; i > ; i--)
- percDown(a, i, a.size()); //首先是建堆
- for (int j = a.size() - ; j > ; j--)
- { //这一步从堆中删除最大值到vector的末尾,这样完成了排序
- swap(a[], a[j]);
- percDown(a, , j);
- }
- }
- inline int leftChild(int i)
- {
- return * i + ;
- }
- template<typename Comparable>
- void percDown(vector<Comparable> & a, int i, int j)
- {
- int child;
- Comparable tmp;
- for (tmp = a[i], leftChild(i) < n; i = child)
- {
- child = leftChild(i);
- if (child != n - && a[child] < a[child + ])
- child++;
- if (tmp < a[child])
- a[i] = a[child];
- else
- break;
- }
- a[i] = tmp;
- }
归并排序
归并排序主要使用了递归的思想,即分治策略,可以证明其算法时间复杂度为Nlog(N)的。
- template<typename Comparable>
- void mergeSort(vector<Comparable & a>)
- {
- vector<Comparable> tmpArray(a.size());
- mergeSort(a, tmpArray, , a.size() - );
- }
- template <typename Comparable>
- void mergeSort(vector<Comparable> & a, vector<Comparable> & b, int left, int right)
- {
- if (left < right)
- {
- int center = (left + right) / ;
- mergeSort(a, tmpArray, left, center);
- mergeSort(a, tmpArray, center + , right);
- merge(a, tmpArray, left, center + , right);
- }
- }
- template<typename Comparable>
- void merge(vector<Comparable> & a, vector<Comparable> tmpArray, int leftPos, int rightPos, int rightEnd)
- { //这个下面实际上有一个错误,看的时候要纠正一下
- int leftEnd = rightPos - ;
- int tmpPos = leftPos;
- int numElements = rightEnd - leftPos + ;
- while (leftPos < leftEnd && rightPos < rightEnd)
- {
- if (a[leftPos] <= a[rightPos])
- tmpArray[tmpPos++] = a[leftPos++];
- else
- tmpArray[tmpPos++] = a[rightPos++];
- }
- while (leftPos <= leftEnd)
- tmpArray[tmpPos++] = a[leftPos++];
- while (rightPos <= rightEnd)
- tmpArray[tmppos++] = a[rightPos++];
- for (int i = ; i < numElements; i++, rightEnd--)//这一步拷贝的方法值得注意
- a[rightEnd] = tmpArray[rightEnd];
- }
快速排序
其算法复杂度最大不超过O(N^2),平均值为O(Nlog(N))
注意快排枢纽元的选取。
将第一个元素作为枢纽元:
很明显的不太合适,如果待排序内容是随机的,那么这样是没有问题的。但是如果输入是预排序的或者是反序的,那么就很糟糕。对于预排序的,这样可能导致排序时间是二次的,但是实际上没有做什么事情。如果对于反序的,这种枢纽元每次都会产生劣质的分割,应为所有元素不是被分到S1就是都被分到S2.
随机取一个元素作为枢纽元:
这是个不错的做法,这样不会一直产生很坏的分割,但是生成随机数是昂贵的,可能会给算法带来很大的负担。
三数中值分割法:
可以取左侧,中间,右边的值的和的平均值作为枢纽元。这种做法是比较理智的。
- template<typename Comparable>
- void quickSort(vector<Comparable> & a)
- {
- quickSort(a, , a.size() - );
- }
- //首先是枢纽元的选取
- template<typename Comparable>
- const Comparable & median3(vector<Comparable> & a, int left, int right)
- {
- int center = (left + right) / ;
- if (a[center] < a[left])
- swap(a[center], a[left]);
- if (a[right] < a[left]) //将最小的元素放到vector的最左边,符合要求
- swap(a[left], a[right]);
- if (a[right] < a[center]) //将最大的元素放到vector的最右边,这也符合要求
- swap(a[center], a[right]);
- swap(a[center], a[right - ]); //将枢纽元放在最右边左侧一个的位置上,符合要求
- return a[right - ];
- }
- template <typename Comparable>
- void quickSort(vector<Comparable> & a, int left, int right)
- {
- if (left + <= right)
- {
- Comparable pivot = median3(a, left, right);
- int i = left; j = right - ;
- for (;;)
- {
- while (a[++i] < pivot){}
- while (a[--j] > pivot){}
- if (i < j) //如果i,j的前后顺序还没有交换,那么这一轮块排还没有结束
- swap(a[i], a[j])
- else break;
- }
- swap(a[i], a[right - ]);
- quickSort(a, left, i - );
- quickSort(a, i + , right);
- }
- else
- insertionSort(a, left, right);<span style="white-space:pre"> </span>//对于小数组,那么采取插入排序性能较好
- }<pre name="code" class="cpp">template<typename Comparable>
- void quickSort(vector<Comparable> & a)
- {
- quickSort(a, , a.size() - );
- }
- 简单介绍一个快排算法的展开:快速选择其代码段如下所示,其实相当于快速选择找一个固定的数的版本
- template<typename Comparable>
- void quickSort(vector<Comparable> & a)
- {
- quickSort(a, , a.size() - );
- }
- //首先是枢纽元的选取
- template<typename Comparable>
- const Comparable & median3(vector<Comparable> & a, int left, int right)
- {
- int center = (left + right) / ;
- if (a[center] < a[left])
- swap(a[center], a[left]);
- if (a[right] < a[left]) //将最小的元素放到vector的最左边,符合要求
- swap(a[left], a[right]);
- if (a[right] < a[center]) //将最大的元素放到vector的最右边,这也符合要求
- swap(a[center], a[right]);
- swap(a[center], a[right - ]); //将枢纽元放在最右边左侧一个的位置上,符合要求
- return a[right - ];
- }
- template <typename Comparable>
- void quickSelect(vector<Comparable> & a, int left, int right, int k)
- {
- if (left + <= right)
- {
- Comparable pivot = median3(a, left, right);
- int i = left; j = right - ;
- for (;;)
- {
- while (a[++i] < pivot){}
- while (a[--j] > pivot){}
- if (i < j) //如果i,j的前后顺序还没有交换,那么这一轮块排还没有结束
- swap(a[i], a[j])
- else break;
- }
- swap(a[i], a[right - ]);
- if(k <= i)
- quickSelect(a, left, i - , k);
- else if(k > i+)
- quickSelect(a, i + , right, k);
- }
- else
- insertionSort(a, left, right);
- }
6.对于大元素的数组排序,由于不断移动的成本很高,所以一般会使用移动指针的方式代替直接的大元素的移动
- //首先应该声明一个指针类,其中应该包含比较方法,在用这个指针类去调用quicksort就行了
- template<typename Comparable>
- class Pointer
- {
- private:
- Comparable * pointee;
- public:
- Pointer(Comparable * rhs = NULL) : pointee(rhs){}
- bool operator<(const Pointer & rhs)cosnt
- {
- return *pointee < *rhs.pointee;
- }
- operator Comparable*()const<span style="white-space:pre"> </span>//转换符号,使得完成从pointer<Comparable>da使得可以
- {<span style="white-space:pre"> </span>//直接使用*pointer,这里已经有着隐含着的Comparator*了
- <span style="white-space:pre"> </span>{<span style="white-space:pre"> </span>//定义了这个符号之后就可以完成Pointer与Comparable*之间的双向转换!!
- <span style="white-space:pre"> </span>return *pointee;
- <span style="white-space:pre"> </span>}
- };
- template<typename Comparable>
- void largeObjectSort(vector<Comparable> & a)
- {
- vector<Pointer<Comparable>> p(a.size());
- int i, j, next;
- for (i = ; i < a.size(); i++)
- p[i] = &a[i];
- quickSort(p);
- //下面再依靠指针指向的情况来将数组重新来排列,其中使用到了滑动算法
- for (i = ; i < a.size(); i++)
- if (p[i] != a[i])
- {
- Comparable tmp = a[i];
- for (j = i; p[j] != &a[i]; j = nextj)
- {
- nextj = p[j] - &a[]; //这里使用的滑动算法,可以自己画图演示一下
- a[i] = *p[j];
- p[j] = &a[j];
- }
- a[j] = tmp;
- p[j] = &a[j];
- }
- }
//大量元素的排序最好直接使用快排算法,注意不要图省事将第一个元素作为枢纽元
//如果考虑编程的简洁性可以使用希尔排序
//堆排序比希尔排序是要慢的
//插入排序一般只用在小或者是基本上已经排好序的输入数据上
//归并排序较为麻烦而且对于CPU的主存排序性能不一定有快速排序好
几种常见排序算法的C++描述的更多相关文章
- JavaScript版几种常见排序算法
今天发现一篇文章讲“JavaScript版几种常见排序算法”,看着不错,推荐一下原文:http://www.w3cfuns.com/blog-5456021-5404137.html 算法描述: * ...
- 几种常见排序算法之Java实现(插入排序、希尔排序、冒泡排序、快速排序、选择排序、归并排序)
排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列. 稳定度(稳定性)一个排序算法是稳定的,就是当有两个相等记录的关 ...
- Java中几种常见排序算法
日常操作中常见的排序方法有:冒泡排序.快速排序.选择排序.插入排序.希尔排序等. 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数 ...
- 几种常见排序算法的java实现
一.几种常见的排序算法性能比較 排序算法 最好时间 平均时间 最坏时间 辅助内存 稳定性 备注 简单选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定 n小时较好 直接插入排序 O( ...
- Java的几种常见排序算法
一.所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法.排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面. ...
- 几种常见排序算法的基本介绍,性能分析,和c语言实现
本文介绍6种常见的排序算法,以及他们的原理,性能分析和c语言实现: 为了能够条理清楚,本文所有的算法和解释全部按照升序排序进行 首先准备一个元素无序的数组arr[],数组的长度为length,一个交换 ...
- php几种常见排序算法
<?php //从时间上来看,快速排序和归并排序在时间上比较有优势,//但是也比不上sort排序,归并排序比较占用内存! $arr = [4,6,1,2,3,89,56,34,56,23,65] ...
- 【知了堂学习笔记】java 编写几种常见排序算法2
排序的分类: 1.直接选择排序 它的基本思想是:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,....,第i次从R[i-1]~ ...
- 【知了堂学习笔记】java 编写几种常见排序算法
排序的分类: 一.交换排序 所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动. 1.冒泡 ...
随机推荐
- 连接postgresql
# psycopg2 engine=create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')# python 连 ...
- python全栈开发从入门到放弃之元组的内置应用
1.元组的字符类型tuple t=(1,[1,3],'sss',(1,2)) print(type(t)) <class 'tuple'> 2.按索引号取值 t=(1,[1,3],'sss ...
- Spring 之自动化装配 bean 尝试
[Spring之自动化装配bean尝试] 1.添加dependencies如下所示(不是每一个都用得到 <dependencies> <dependency> <grou ...
- rest_framework解析器组件源码流程
rest_framework解析器组件源码流程 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数 ...
- EasyUI:datagrid控件简介
EasyUI:datagrid控件简介 1,水平滚动条属性: //显示滚动条 fitColumns:false //不显示滚动条 fitColumns:true
- CC3中的2D转换
2D转换方法: translate() rotate() scale() skew() matrix() 1.translate()方法,根据左(X轴)和顶部(Y轴)位置给定的参数,从当前元素位置移动 ...
- Refseq,accssion #,gi ,Ensembl的关系
accession编号的分子类型代号: Ensembl是2000年就开始开发的基因组自动注释软件,起初是只对真核生物基因组,2009年后开始对植物,细菌等开放.既然要注释,就要有注释对象(基因,转录本 ...
- windows 安装python3.5启动报错:api-ms-win-crt-runtime-l1-1-0.dll丢失
下载: api-ms-win-crt-runtime就是MFC的运行时环境的库,python在windows上编译也是用微软的visual studio C++编译的,底层也会用到微软提供的C++库和 ...
- java实验一报告
北京电子科技学院(BESTI) 实 验 报 告 课程:Java 班级: 1352 姓名:黄晓妍 学号:20135227 成绩: 指导教师:娄嘉 ...
- c语言网络通信杂笔记
1.sin_addr.s_addr = INADDR_ANY;设置成本地IP 2.pthread_create();线程生成函数 3.在linux下,sleep(1)是睡眠1s