经典的7种排序算法 原理C++实现
排序是编程过程中经常遇到的操作,它在很大程度上影响了程序的执行效率。
7种常见的排序算法大致可以分为两类:第一类是低级排序算法,有选择排序、冒泡排序、插入排序;第二类是高级排序算法,有堆排序、排序树、归并排序、快速排序。
一、低级排序算法
1. 选择排序
排序过程:给定一个数值集合,循环遍历集合,每次遍历从集合中选择出最小或最大的放入集合的开头或结尾的位置,下次循环从剩余的元素集合中遍历找出最小的并如上操作,最后直至所有原集合元素都遍历完毕,排序结束。
实现代码:
//选择排序法
template
void Sort::SelectSort(T* array, int size)
{
int minIndex;
for(int i = ; i < size; i++)
{
minIndex = i;
for(int j = i + ; j < size; j++)
{
if(array[minIndex] > array[j])
{
minIndex = j;
}
}
if(minIndex != i)
{
Swap(array, i, minIndex);
}
}
}
分析总结:选择排序时间复杂度比较高,达到了O(n^2),每次选择都要遍历一遍无序区间。选择排序对一类重要的元素序列具有较好的效率,就是元素规模很大,而排序码却比较小的序列。另外要说明的是选择排序是一种不稳定的排序方法。
2. 冒泡排序
排序过程:冒泡排序的过程形如其名,就是依次比较相邻两个元素,优先级高(或大或小)的元素向后移动,直至到达序列末尾,无序区间就会相应地缩小。下一次再从无序区间进行冒泡操作,依此循环直至无序区间为1,排序结束。
实现代码:
//冒泡排序法
template
void Sort::BubbleSort(T* array, int size)
{
for(int i = ; i < size; i++)
{
for(int j = ; j < size - i; j++)
{
if(array[j] < array[j - ])
{
Swap(array, j, j - );
}
}
}
}
分析总结:冒泡排序的时间复杂度也比较高,达到O(n^2),每次遍历无序区间都将优先级高的元素移动到无序区间的末尾。冒泡排序是一种稳定的排序方式。
3. 插入排序
排序过程:将前面的区间(初始区间为1,包含第一个元素)视作有序区间,然后将有序区间的后一元素插入到前面有序区间的适当位置。直至有有序区间扩展到原区间的大小,排序结束。
实现代码:
//插入排序
template
void Sort::InsertSort(T* array, int size)
{
for(int i = ; i < size; i++)
{
for(int j = i; j > ; j--)
{
if(array[j] < array[j - ])
{
Swap(array, j, j-);
}
}
}
}
分析总结:插入排序的时间复杂度达到O(n^2),排序的运行时间和待排序元素的原始排列顺序密切相关。插入排序是一种稳定的排序方法。
二、高级排序算法
1. 快速排序
排序过程:快速排序应该是应用最广泛的排序算法,它是采用了分治的思想(这种思想很重要)。其基本的思想就是任取待排序序列中的某个元素(元素的选取方式在一定程序上会影响实现过程和排序效率)作为标杆,将待排序序列划分为左右两个子序列,左侧元素小于标杆元素,右侧元素大于标杆元素,标杆元素则排在这两个子序列的中间,然后再对这两个子序列重复上述的方法,直至排序结束。
实现代码:
//快速排序
template
void Sort::QuickSort(T *array, int left, int right)
{
if(left < right)
{
int i = left -, j = right + ;
T mid = array[(left + right) / ];
while(true)
{
while(array[++i] < mid);
while(array[--j] > mid);
if(i >= j)
{
break;
}
Swap(array, i, j);
}
QuickSort(array, left, i - );
QuickSort(array, j + , right);
}
}
分析总结:快速排序的时间复杂度为O(nlogn),是一种不稳定的排序算法。
2. 归并排序
排序过程:归并排序的原理比较简单,也是基于分治思想的。它将待排序的元素序列分成两个长度相等的子序列,然后为每一个子序列排序,然后再将它们合并成一个序列。
实现代码:
//归并排序
template
void Sort::MergeSort(T* array, int left, int right)
{
if(left < right)
{
int mid = (left + right) / ;
MergeSort(array, left, mid);
MergeSort(array, mid + , right);
Merge(array, left, mid, right);
}
}
//合并两个已排好序的子链
template
void Sort::Merge(T* array, int left, int mid, int right)
{
T* temp = new T[right - left + ];
int i = left, j = mid + , m = ;
while(i <= mid && j <= right)
{
if(array[i] < array[j])
{
temp[m++] = array[i++];
}
else
{
temp[m++] = array[j++];
}
}
while(i <= mid)
{
temp[m++] = array[i++];
}
while(j <= right)
{
temp[m++] = array[j++]; }
for(int n = left, m = ; n <= right; n++, m++)
{
array[n] = temp[m];
}
delete temp;
}
分析总结:归并排序最好、最差和平均时间复杂度都是O(nlogn),是一种稳定的排序算法。
3. 堆排序
排序过程:堆排序的过程分为两个步骤,第一步是根据初始输入数据,建立一个初始堆;第二步是将堆顶元素与当前无序区间的最后一个元素进行交换,然后再从堆顶元素开始对堆进行调整。
实现代码:
//堆排序
template
void Sort::HeapSort(T* array, int size)
{
int lastP = size / ;
//从最后一个有孩子的结点开始建初始堆
for(int i = lastP - ; i >= ; i--)
{
HeapAjust(array, i, size);
}
int j = size;
//将堆顶元素和无序区间的最后一个元素交换,再调整堆
while(j > )
{
Swap(array, , j - );
j--;
HeapAjust(array, , j);
}
}
//调整堆
template
void Sort::HeapAjust(T *array, int toAjust, int size)
{
int pos = toAjust;
while((pos * + ) < size)
{
int lChild = pos * + ;
if(array[lChild] > array[pos])
{
pos = lChild;//左孩子大
}
int rChild = lChild + ;
if(rChild < size && array[rChild] > array[pos])
{
pos = rChild;//右孩子更大
}
if(pos != toAjust) //父结点比其中一个孩子小
{
Swap(array, toAjust, pos);
toAjust = pos;
}
else
{
break;
}
}
}
分析总结:堆排序的算法时间复杂度为O(nlogn),它是一种不稳定的排序算法。
4. 排序树
排序过程:排序树算法应用了AVL树的原理,只不过排序树不是平衡的,它的特点就是父结点元素总是比左孩子元素要大却比右孩子元素要小。根据这个特点,可以将原数组元素组织成排序树,然后在对排序树进行中序遍历,中序遍历的结果就是排好序的序列。在算法的实现中采用的数组的形式来存储排序树,并采用的三个辅助数组来表示元素与元素之间在树中的关系,这三个辅助数组分别是父结点索引表、左孩子索引个、右孩子索引表。最后采用了递归的方法对排序树进行了中序遍历。
实现代码:
template
void Sort::TreeSort(T* array, int size)
{
int *parent = new int[size];//父结点子针
int *lChild = new int[size];//左孩子子针
int *rChild = new int[size];//右孩子子针
//将各结点左右子结点指针均置为-1,表示没有左右子结点
for(int i = ; i < size; i++)
{
lChild[i] = -;
rChild[i] = -;
} parent[] = -; //将第一个元素作为根结点,其父结点置为-1
//从第2个数开始构造树
for(int i = ; i < size; i++)
{
int last = ;
while(true)
{
int compare = array[i] - array[last];
if(compare > ) //比当前值大,进入右子树
{
if(rChild[last] == -)
{
rChild[last] = i;
parent[i] = last;
break;
}
else
{
last = rChild[last];
}
}
else //比当前值小,进入左子树
{
if(lChild[last] == -)
{
lChild[last] = i;
parent[i] = last;
break;
}
else
{
last = lChild[last];
}
}
}
}
//保存排序树的中序遍历结果
T* midRetrival = new T[size];
TreeMidRetrival(array, midRetrival, , lChild, rChild);
//将排好序的中序数组复制到原数组
for(int i = ; i < size; i++)
{
array[i] = midRetrival[i];
}
}
//中序遍历
template
void Sort::TreeMidRetrival(T *array, T* temp, int pos, T* lChild, T* rChild)
{
static int i = ;
if(pos != -)
{
TreeMidRetrival(array, temp, lChild[pos], lChild, rChild);//遍历左子树
temp[i++] = array[pos];//保存当前值
TreeMidRetrival(array, temp, rChild[pos], lChild, rChild);//遍历右子树
}
else
{
return;
}
}
分析总结:算法中排序树建立的时间复杂度是O(nlogn),中序遍历的时间复杂度是O(n),故排序树排序的时间复杂度为O(nlogn)。排序树的空间复杂度比较高,因为它使用了几个额外的存储空间的存储排序树元素之间的关系。
模板类定义如下所示:
template
class Sort
{
public:
void SelectSort(T* array, int size);
void InsertSort(T* array, int size);
void BubbleSort(T* array, int size);
void MergeSort(T* array, int left, int right);
void Merge(T* array, int left, int mid, int right);
void HeapSort(T *array, int size);
void HeapAjust(T *array, int toAjust, int size);
void QuickSort(T *array, int left, int right);
void TreeSort(T *array, int size);
void TreeMidRetrival(T* array, T* temp, int pos, T* lChild, T* rChild);
void Swap(T* array, int x, int y);
}; template //交换两个元素
void Sort::Swap(T* array, int x, int y)
{
T temp = array[x];
array[x] = array[y];
array[y] = temp;
}
更多代码请进入: https://github.com/tomatoschool
经典的7种排序算法 原理C++实现的更多相关文章
- 八种排序算法原理及Java实现
原文链接:http://ju.outofmemory.cn/entry/372908
- C#常用8种排序算法实现以及原理简介
public static class SortExtention { #region 冒泡排序 /* * 已知一组无序数据a[1].a[2].--a[n],需将其按升序排列.首先比较a[1]与a[2 ...
- 几种排序算法的学习,利用Python和C实现
之前学过的都忘了,也没好好做过总结,现在总结一下. 时间复杂度和空间复杂度的概念: 1.空间复杂度:是程序运行所以需要的额外消耗存储空间,一般的递归算法就要有o(n)的空间复杂度了,简单说就是递归集算 ...
- 数据结构(三) 用java实现七种排序算法。
很多时候,听别人在讨论快速排序,选择排序,冒泡排序等,都觉得很牛逼,心想,卧槽,排序也分那么多种,就觉得别人很牛逼呀,其实不然,当我们自己去了解学习后发现,并没有想象中那么难,今天就一起总结一下各种排 ...
- 排序—时间复杂度为O(n2)的三种排序算法
1 如何评价.分析一个排序算法? 很多语言.数据库都已经封装了关于排序算法的实现代码.所以我们学习排序算法目的更多的不是为了去实现这些代码,而是灵活的应用这些算法和解决更为复杂的问题,所以更重要的是学 ...
- 最全排序算法原理解析、java代码实现以及总结归纳
算法分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过 ...
- Python实现的选择排序算法原理与用法实例分析
Python实现的选择排序算法原理与用法实例分析 这篇文章主要介绍了Python实现的选择排序算法,简单描述了选择排序的原理,并结合实例形式分析了Python实现与应用选择排序的具体操作技巧,需要的朋 ...
- 秒杀9种排序算法(JavaScript版)
一:你必须知道的 1> JS原型 2> 排序中的有序区和无序区 3> 二叉树的基本知识 如果你不知道上面三个东西,还是去复习一下吧,否则,看下面的东西有点吃力. 二:封装丑陋的原型方 ...
- PHP的几种排序算法的比较
这里列出了几种PHP的排序算法的时间比较的结果,,希望对大家有所帮助 /* * php 四种排序算法的时间与内置的sort排序比较 * 3000个元素,四种算法的排序所用的时间比较 * 冒泡排序 85 ...
随机推荐
- IT兄弟连 JavaWeb教程 JSTL常用标签
1.条件标签 条件标签能够实现Java语言中的if语句以及if-else语句的功能,它包括以下几种: <c:if>:用于实现Java语言中的if语句的功能. <c:choose> ...
- IT兄弟连 JavaWeb教程 Servlet会话跟踪 经典面试题
1.描述Cookie的作用 Cookie是网站保存在浏览器客户端的信息,也就是说保存在访客的机器里的变量,一般随着HTTP头发送到客户端.在Cookie生效之后及失效之前,客户每次发出页面请求的时候, ...
- valgrind 使用入门
近期在学习使用valgrind 来定位性能问题,记录一下整个操作过程以及涉及到的工具 一般在机器上都会预装valgrind 工具 使用callgrind 工具检查程序的调用情况,例如使用valgrin ...
- Maven多模块构建实例
创建coffee-parent项目 New->Maven Project 创建coffee-web项目 右键coffee-parent项目->New->Project... 注意:需 ...
- RDS 导出Mysqlbinlog_二进制日志
1:首先进入RDS后台查看Mysqlbin_log日志,可以通过以下命令进行查看: show master logs; 或 show binary logs; 2:比如我们获取的是:mysql-bin ...
- G. Of Zorcs and Axes 二分 + 贪心 —— STL的用法
http://codeforces.com/gym/101149/problem/G 一开始还以为要用二分图去做,但是复杂度也太高了,O(n * m)的话直接爆炸. 考虑贪心,考虑第i个东西优先选一个 ...
- java 并发容器一之BoundedConcurrentHashMap(基于JDK1.8)
最近开始学习java并发容器,以补充自己在并发方面的知识,从源码上进行.如有不正确之处,还请各位大神批评指正. 前言: 本人个人理解,看一个类的源码要先从构造器入手,然后再看方法.下面看Bounded ...
- 模拟一次CSRF(跨站请求伪造)例子,适合新手
GET请求伪造 有一个论坛网站,网站有一个可以关注用户的接口,但是必须登录的用户才可以关注其他用户. 这个网站的网站是www.a.com 有一天有一个程序员想提高自己的知名度,决定利用CSRF让大家关 ...
- Android属性系统简介
1.简介 在android 系统中,为统一管理系统的属性,设计了一个统一的属性系统.每个属性都有一个名称和值,他们都是字符串格式.属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息 ...
- freebsd安装ports
/etc/portsnap.conf 里面更改 SERVERNAME=portsnap.hshh.org portsnap的命令比较少 fetch 获取数据 extract 释放全部ports upd ...