以下代码是个人学习排序算法的一些实践,实现了大部分排序算法的升序版本,并且对每一种算法进行了简要的介绍和复杂度分析。
涉及的算法如下:
- 插入排序:直接插入排序、折半插入排序、希尔排序
- 交换排序:冒泡排序、快速排序
- 选择排序:简单选择排序、堆排序
- 其他类型:归并排序、基数排序
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 以下排序均为升序算法
void printArray(vector<int> A){
for(int i = 0; i < A.size(); i++){
cout << A[i] << " ";
}
cout << endl << endl;
}
/************************************
插入排序
1. 直接插入排序
2. 折半插入排序
3. 希尔排序(shell)
*************************************/
vector<int> simpleInsertSort(vector<int> A){
/**
-- 简单插入排序的思想
从数组的第二个元素开始,将其选为目标待排序
认定元素左边的序列为有序,而右边的序列为无序
将目标元素和左边序列从后向前进行比较,找到插入有序的位置t之后,
先将该位置之后的元素往后移动1位,然后将目标元素插入到位置t
空间效率:O(1)
时间效率:O(n^2)
最好情况 顺序,O(n)
最坏情况:逆序,比较次数达到最大,移动次数达到最大
稳定的排序算法:不会改变相同关键字元素的相对位置。
适用于顺序存储和链式存储的线性表,适合基本有序和数据量不大的情况
**/
if(A.size() <= 1) return A;
for(int i = 1; i < A.size(); i++){
if(A[i] < A[i - 1]){
int tmp = A[i];
int j = 0;
for(j = i - 1; j >= 0 && tmp < A[j]; j--)
A[j + 1] = A[j];
A[j + 1] = tmp;
}
}
return A;
}
vector<int> binaryInsertSort(vector<int> A){
/**
折半插入在简单插入排序的基础上进行了改进
在向前查找插入位置时使用了二分查找的方法,减少了比较的次数
折半插入减少了比较的次数,约为 O(nlog2), 元素移动次数没有发生改变O(n^2)
**/
int i, j, low, high, mid;
for(i = 1; i < A.size(); i++){
int tmp = A[i];
low = 0;
high = i - 1;
while(low <= high){
mid = (low + high) / 2;
if(A[mid] > tmp) high = mid-1;
else low = mid + 1;
}
for(j = i - 1; j >= high + 1; j--)
A[j + 1] = A[j];
A[high + 1] = tmp;
}
return A;
}
vector<int> shellSort(vector<int> A){
/****
希尔排序,又称缩小增量排序,由于简单插入排序很适合基本有序的序列,
那么可以对一些较乱的序列进行处理,使之变为基本有序的序列,
然后在进行一次直接插入排序。
思想如下:
先将数组分割成若干子数组,如[i, i+d, i+2d, i+kd]。
d为步长,小于,数组长度。这样整个数组就可以分成d个子数组。
分别对这d个子数组进行直接插入排序。
进行下一轮,取更小的步长e,重复上述动作,知道步长等于1,在进行一次直接插入排序完成。
希尔排序是不稳定的,仅适用于顺序存储的线性表
时间复杂度为O(n^2),空间复杂度为O(1)
*****/
for(int d = A.size() / 2; d > 0; d /= 2){
for(int i = d; i < A.size(); i++){
int tmp = A[i];
int j = 0;
for(j = i; j >= d && A[j - d] > tmp; j -= d)
A[j] = A[j-d];
A[j]