经典的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 ...
随机推荐
- “我要点爆”微信小程序云开发实例
使用云开发进行微信小程序“我要点爆”的制作 下一章:“我要点爆”微信小程序云开发之项目建立与我的页面功能实现 接下来我将对“我要点爆”微信小程序进行完整的开源介绍 小程序名称: 我要点爆 查看方式:从 ...
- 用EnumSet代替位域
用EnumSet代替位域 如果一个枚举类型的元素主要用在集合中,一般使用int枚举模式,将2的不同倍数赋予每个常量: // Bit field enumeration constants - OB ...
- D. Pair Of Lines( Educational Codeforces Round 41 (Rated for Div. 2))
#include <vector> #include <iostream> #include <algorithm> using namespace std; ty ...
- cmd - 使用curl命令的注意点
前言 最近在cmd中使用curl命令来测试rest api,发现有不少问题,这里记录一下. 在cmd中使用curl命令的注意事项 json不能由单引号包括起来 json数据里的双引号要用反斜杠\转义 ...
- pycharm 中切换虚拟环境
在pycharm上创建虚拟环境,网上的资料非常多. 如果pycharm上有多个项目,如何切换每个项目的虚拟环境? cmd 命令进入虚拟环境所在的文件夹(Pycharm在每创建一个新项目时就会创建一个虚 ...
- pip 参数
pip 自带参数 pip --help pip install 自带参数 pip install --help
- 浅谈H5技术
1.什么是H5:HTML5将成为HTML.XHTML以及HTML DOM的新标准.目前仍处于完善之中.然而,大部分现代浏览器已经具备了某些HTML5支持. 2.背景:HTML5 是 W3C 与 WH ...
- windows安装redis和PHP redis扩展
1.安装Redis (1)这里选择在github官网上下载Redis,地址:Redis下载地址 下载压缩包(如下图),并解压到本地目录,我放在D:\wamp\redis\redis-windows ( ...
- VMware每次联网都需要还原默认设置解决办法
参考:https://zhidao.baidu.com/question/553464573715382812.html
- Elasticsearch之探索集群信息
REST API 启动并运行了节点和集群,下一步是了解如何与它进行通信.elasticsearch提供了一个非常全面和强大的REST API,可以使用它与集群进行交付. 使用API完成以下: • 检查 ...