插入排序
插入排序由N-1次排序组成,对于i = 1到N-1趟,插入排序保证从0到i位置上的元素是已排序状态。
插入排序在第i次排序的时候,把第i个位置上的元素向左移动到[ 0 , i ]范围内的正确的位置上。
算法实现
/*
插入排序
将整个数组分为前后两部分,i(不包括i)之前的部分视为已排序部分,i及i之后的部分视为未排序部分;
令temp = a[i],将a[i]暂时提取出来;
然后把i之前的数依次向后移,直到某个位置j的数比temp小的时候,或已经全部移动完时,结束后移;
把temp插入当前j的位置。
*/
template<typename T>
void insertionSort(vector<T> & a){
int j; //指向已排序的部分
for (int i = 1; i < a.size(); i++){
T temp = a[i]; //提取未排序的第一个数
每次须比较a[j - 1]和a[j],所以j-1 >= 0,所以 j >= 1
for (j = i; j>=1 && temp < a[j - 1]; j--){ //已排序的数依次后移
//直到全部移动完或找到temp的插入位置时结束
a[j] = a[j - 1];
}
a[j] = temp; //插入temp
}
}
希尔排序
希尔排序通过比较相距一定间隔的元素来工作,各趟比较所用的距离gap随算法进行而减小, 直到gap==1
只比较相邻元素的最后一趟排序为止。实际上是通过gap变量把一个数组分为gap个小组,对每个小组进行插入排序。
比较所用的距离可以排成一个序列,叫做增量序列,刚开始令gap = v.size() / 2
,之后每次排序完成后都令gap = gap / 2
,最终构成一个大于零的增量序列。
算法实现
/*
gap是增量,通过gap把数组分为若干组,组内下标间隔为gap;
同时,gap的值也是第一组的第二个数的位置;
i依次扫描第一组的第二个,第二组的第二个,第三组的第二个……第gap组的第二个;第一组的第三个,第二组的第三个……直到第gap组的最后一个;
j依次扫描每一组的已排序部分,每次前移的量为gap。
*/
template<typename T>
void shellSort(vector<T> & a){
int gap = a.size() / 2; //增量
for (; gap > 0; gap /= 2){
//类似插入排序,每个小组下标间隔为gap
for (int i = gap; i < a.size(); i++){
int j;
T temp = a[i];
//每次须比较a[j - gap]和a[j],所以j-gap >= 0,所以 j >= gap
for (j = i; j >= gap && temp < a[j - gap]; j -= gap){
a[j] = a[j - gap];
}
a[j] = temp;
}
}
}
堆排序
优先队列可以用于以O(NlogN)的时间进行排序,是一种最佳的排序方法。
不同于优先队列的构造方法,堆排序并不重新建立一个新的数组创建堆,而是直接在原数组的基础上建立一个堆。因为数组是从下标0开始,而二叉堆是从下标1开始,所以在进行下滤操作的时候,数组的下标应在堆操作的基础上加以转换:
(如果对于下滤和上滤有疑惑可以查看《二叉堆中上滤和下滤的问题》)
节点 | 堆中节点 | 数组中节点 |
---|---|---|
节点i的下标 | i | i-1 |
节点i的左孩子节点下标 | i*2 | i*2+1 ((i+1)*2-1 ) |
节点i的左孩子节点下标 | i*2+1 | i*2+2 |
节点i不是叶子节点(节点i是分支节点)(左孩子的下标小于总个数) | i*2 < n | i*2+1 |
节点i有两个孩子(左孩子下标不等于总节点数) | i*2 != n | i*2+1 != n-1 |
节点i只有一个孩子(节点i只有一个左孩子)(左孩子下标等于总结点数) | i*2 == n | i*2+1 == n-1 |
因为在每次deleteMin()之后,堆中元素的个数都缩小了1,因此可以把堆的最后一个单元存放刚刚删除的堆顶。这样对于转化成最大堆的数组,每次都会把当前堆的最大值放到当前的最后一位,最后形成一个递增的序列。所以堆排序有这样一个规则:构建最大堆产生递增序列,构建最小堆产生递减序列。
堆排序算法的实现过程分为以下几步:
1. 将乱序数组建立成一个最大堆或最小堆。
2. 交换堆顶和堆末的元素
3. 堆的大小减1,重整新的堆,和步骤1的堆序一样
4. 重复步骤2,直到堆的大小为1为止
在别人的博客上发现了一个动态图,很形象地描绘了堆排序的过程,在这里分享一下
来源https://www.2cto.com/kf/201609/549335.html
算法实现
template<typename T>
void heapsort(vector<T> & a){
//建立堆
for (int i = a.size() / 2; i >= 0; i--){
percDown(a, i, a.size());
printVector(a);
}
//deleteMax
for (int j = a.size() - 1; j > 0; j--){
swap(a[0], a[j]); //交换堆顶和堆末
percDown(a, 0, j);
}
}
//返回左孩子的下标
inline int leftChild(int i){
//return (i + 1) * 2 - 1;
//即
return i * 2 + 1;
}
/*
下滤
i是下滤的位置
n是堆的大小
*/
template<typename T>
void percDown(vector<T> & a, int i, int n){
int child;
T temp = a[i];
for (; leftChild(i) < n; i = child){
child = leftChild(i);
//n-1为当前二叉堆的最大的编号(该二叉堆从0开始)
//如果左孩子的编号等于n-1,那么父节点没有右孩子
if (child != n - 1 && a[child] < a[child + 1]){
child++;
}
if (temp < a[child]){
a[i] = a[child];
}
else{
break;
}
}
a[i] = temp;
}
归并排序
归并排序是一个以O(NlogN)最坏情况下运行的算法,也是一个非常好的排序算法。
归并排序的基本操作是合并两个已经排序的数组。对于一个无序数组,如果N=1,那么相当于有序数组;否则,递归对把数组的前半部分和后半部分进行归并排序,得到前后两部分有序数组,再对整个数组进行合并。
比如:
无序数组: 24|13|26|1|2|27|38|15 -> 1、2、3、13、15、24、26、27
↓ ↑
第一次递归: 24、13、26、1 | 2、27、38、15 -> 1、13、24、26|2、3、15、27
↓ ↑
第二次递归: 24、13 | 26、1 | 2、27 | 38、15 -> 13、24|1、26|2、27|15、3
算法实现
template<typename T>
void mergesort(vector<T> & a){
vector<T> tmpArray(a.size()); //辅助数组
MergeSort(a, tmpArray, 0, a.size() - 1);
}
template<typename T>
void MergeSort(vector<T> &a, vector<T> &tmpArray, int left, int right){
if (left < right){
int center = (left + right) / 2;
MergeSort(a, tmpArray, left, center);
MergeSort(a, tmpArray, center + 1, right);
merge(a, tmpArray, left, center + 1, right); //合并
}
}
//合并数组
//a是主数组
//tmpArrray是辅助数组,用来暂时存储排好序的数列
template<typename T>
void merge(vector<T> & a, vector<T> & tmpArray, int leftPos, int rightPos, int rightEnd){
int elementsNum = rightEnd - leftPos + 1;
int leftEnd = rightPos - 1;
int insertPos = leftPos;
while (leftPos <= leftEnd && rightPos <= rightEnd){
if (a[leftPos] < a[rightPos]){
tmpArray[insertPos] = a[leftPos];
leftPos++;
insertPos++;
}
else{
tmpArray[insertPos] = a[rightPos];
rightPos++;
insertPos++;
}
}
while (leftPos <= leftEnd){
tmpArray[insertPos] = a[leftPos];
leftPos++;
insertPos++;
}
while (rightPos <= rightEnd){
tmpArray[insertPos] = a[rightPos];
rightPos++;
insertPos++;
}
for (int i = 0; i < elementsNum; i++){
a[rightEnd] = tmpArray[rightEnd];
rightEnd--;
}
}