经典的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 ...
随机推荐
- JPA_day01
- go系列(3)- go框架beego以及redis的使用
这篇讲讲如何在beego框架使用redis. golang中比较好用的第三方开源redisclient有: go-redis 源码地址:https://github.com/go-redis/redi ...
- 测试 | Mock object
实例: 一个简单的图书管理系统包括三个类: Book提供了书籍的定义 BookService提供了有关书籍借阅和归还的服务 BookMananger查找书籍并把对书籍状态的更改更新到数据库中 目前,B ...
- ArrayList简单学习
类声明: public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomA ...
- echarts 添加Loading 等待。
capturedsDetailsEcharts: function(id) { if (!id) { id = mini.get("chnNameCaptureds").getVa ...
- P1791 线段覆盖
题目描述 已知数轴上0<N<10000条线段.每条线段按照端点Ai和Bi(Ai<>Bi,i=1..N)定义.端点坐标在(-999,999)内,坐标为整数.有些线段可能相交.编程 ...
- uvm_object ——太极
无极生太极——无名天地之始 太极生两仪——有名万物之母 文件: $UVM_HOME/src/base/uvm_object.svh 类: uvm_object The uvm_object cla ...
- HDU 1171 Big Event in HDU 杭电大事件(母函数,有限物品)
题意: 分家问题,对每种家具都估个值,给出同样价值的家具有多少个,要求尽可能平分,打印的第一个数要大于等于第二个数. 思路: 可以用背包做,也可以用母函数.母函数的实现只需要注意一个点,就是每次以一种 ...
- MIPS—冒泡排序
SORT 使用冒泡排序对整数数组进行排序,这种排序虽然不是最快的,但却是最简单的. C语言代码 #include<stdio.h> #include<iostream> usi ...
- 机器学习(3)- 学习建议<误差出现如何解决?>
根据Andrew Ng在斯坦福的<机器学习>视频做笔记,已经通过李航<统计学习方法>获得的知识不赘述,仅列出提纲. 1 学习建议 误差太大,如何改进? 使用更多的训练样本→解决 ...