C++模板实现八大排序算法
最近重新温习了一下十大排序算法,就用C++11模板试着实现了这些排序算法,各种数据类型都能操作,还可以像ST排序算法一般传入自定义排序规则,默认排序规则是less<>{},以下是所写程序:(其中桶排序和计数排序没有给出代码实现)
#pragma once
#include<utility>
/*所有函数调用格式:
sortMehtod(beginIterator, endIterator, compareMethod);
最后的比较方法缺省为less<>{}
*/
/*对输入数据按特定规则分区,符合规则元素移到前面,返回符合规则的元素个数*/
template<typename Ite, class partitionCriterion >//partitionCriterion:分区规则,对特定元素判断是否符合规则
inline size_t myPartition(Ite start, Ite final, partitionCriterion _pCriterion) {
auto L = final - start;
int k = 0;//记录当前符合规则的元素个数
if (L) {
for (auto i = 1; i < L; ++i) {//跳过初始元素
if (_pCriterion(*(start + i))) {//满足入桶规则
++k;
std::swap(*(start + i), *(start + k));
}
}
if (!_pCriterion(*(start))) {//保证最初start开始元素始终在区间开端
std::swap(*(start), *(start + k));
}
else {
++k;
}
}
return k;
}
/*冒泡排序*/
template<typename Ite, class sortCriterion>
void bubbleSort(Ite start,Ite final, sortCriterion sCriterion ) {
auto L= final - start;
for (size_t LL = L; LL > 1;--LL) {
bool issorted{ true };
for (int i=0; i<LL-1; ++i) {
if (sCriterion(*(start + i+1) , *(start + i )) ) {//大泡沫不断沉底
std::swap(*(start + i), *(start +i+ 1));
issorted = false;//若有序则退出排序
}
}
if (issorted) {
break;
}
}
}
template<typename Ite>
void bubbleSort(Ite start, Ite final) {
bubbleSort(start, final, std::less<>{});
}
/*选择排序*/
template<typename Ite, class sortCriterion >
void selectionSort(Ite start, Ite final, sortCriterion sCriterion ) {
auto L = final - start;
for (int i = 0; i < L - 1; ++i) {
auto minT = start + i;
for (int j = i + 1; j < L; ++j) {
if (sCriterion (*(start + j), *(minT))) {
minT = start + j;//记录最小者
}
}
if(minT!= start + i) std::swap(*(start + i), *minT);
}
}
template<typename Ite>
void selectionSort(Ite start, Ite final) {
selectionSort(start, final, std::less<>{});
}
/*插入排序*/
template<typename Ite, class sortCriterion >
void insertionSort(Ite start, Ite final, sortCriterion sCriterion) {
auto L = final - start;
for (int i = 1; i < L ; ++i) {
for (int j =i; j>0&& sCriterion(* (start + j) , *(start + j - 1)); --j) {
std::swap(*(start + j) , *(start + j - 1));//一直向前交换直到前一个元素已经不大于当前元素
}
}
}
template<typename Ite>
void insertionSort(Ite start, Ite final) {
insertionSort(start, final, std::less<>{});
}
/*归并排序*/
template<typename Ite, class sortCriterion >
void merge(Ite start1, Ite start2, Ite final2, sortCriterion sCriterion) {
for (; start2 != final2; ++start2) {
for (auto temp = start2; temp > start1 &&sCriterion(*(temp), *(temp -1));--temp) {//一直向前交换直到前一个元素已经不大于当前元素,思路类似于插入排序
std::swap(*(temp), *(temp - 1));
}
}
}
template<typename Ite, class sortCriterion >
void mergeSort(Ite start, Ite final, sortCriterion sCriterion) {
auto L = final - start;
if (L > 1) {
mergeSort(start, start + L / 2, sCriterion);//假设调用函数后前后两个区域已经各自有序
mergeSort(start + L / 2, final, sCriterion);
merge(start,start + L / 2, final,sCriterion);//合并两个有序区域
}
}
template<typename Ite>
void mergeSort(Ite start, Ite final) {
mergeSort(start, final, std::less<>{});
}
/*快速排序*/
template<typename Ite, class sortCriterion >
void quickSort(Ite start, Ite final, sortCriterion sCriterion) {
if (start!=final) {
size_t num = myPartition(start, final, [&start,&sCriterion](auto ele) ->bool {
return sCriterion(ele,*(start));
});
quickSort(start, start + num, sCriterion);//对不同分区分别排序
quickSort(start + num+1, final, sCriterion);
}
}
template<typename Ite>
void quickSort(Ite start, Ite final) {
quickSort(start, final, std::less<>{});
}
/*堆排序*/
template<typename Ite, class sortCriterion >
void heapSort(Ite start, Ite final, sortCriterion sCriterion) {
for (auto L = final - start; L > 1; --L) {
for (auto i = L; i > 1; --i) {//从叶子节点向根节点开始遍历
if (sCriterion(*(start + i / 2 - 1), *(start + i - 1))) {//如果子节点大于父节点则交换
std::swap(*(start + i / 2 - 1), *(start + i - 1));
}//构建最大堆
}
std::swap(*(start), *(start + L - 1));//将根节点与最后的叶子节点交换
}
}
template<typename Ite>
void heapSort(Ite start, Ite final) {
heapSort(start, final, std::less<>{});
}
/*希尔排序(对增量区间(间隔划分区间)进行插入排序)*/
template<typename Ite, class sortCriterion >
void shellSort(Ite start, Ite final, sortCriterion sCriterion) {
auto L = final - start;
for (auto delta = L / 2; delta >= 1; delta /= 2) {
//插入排序对应与delta为1;
for (auto i = delta; i < L; ++i) {//i = delta表示第一个区间的第二个元素,通过i的递增,保证了各个区间除区间起点以外都被遍历到
for (auto j = i; j >= delta && sCriterion(*(start + j), *(start + j - delta)); j -= delta) {//从i开始向前遍历i所在区间,对该区间插入排序
std::swap(*(start + j - delta), *(start + j));
}
}
}
}
template<typename Ite>
void shellSort(Ite start, Ite final) {
shellSort(start, final, std::less<>{});
}
/*桶排序*/
//此处桶可理解为:符合某准规则的元素集合,多个桶表示有多种规则,需保证待排序元素为所有桶并集的子集
template<typename Ite, class sortCriterion >
void BucketSort(Ite start, Ite final, sortCriterion sCriterion) {
;
}
template<typename Ite>
void BucketSort(Ite start, Ite final) {
BucketSort(start, final, std::less<>{});
}
/*基数排序*/
//特殊的桶排序
template<typename Ite, class sortCriterion>
void RadixSorter(Ite start, Ite final, sortCriterion sCriterion) {
auto L = final - start;
size_t numRadix[10]{};//用于记录每个桶的元素个数
for (int k = 1; numRadix[0] < L; ++k) {
for (int i = 0; i < 10;++i) {//按每一位填入桶
auto pCrite = [&i, &k](const auto& ele) ->bool {//i表示不同桶,k表示个位(1)、十位(2)、百位(3)...
auto temp{ ele };
auto k_{ k };
while ((--k_) && (temp /= 10));
return temp % 10 == i;
};//判断元素ele是否入桶
auto accuNum = i == 0 ? 0 : numRadix[i - 1];
numRadix[i] = myPartition(start + accuNum, final, pCrite) + accuNum;//记录当前入桶元素
//保证桶内元素有序,在基本有序情况下插入排序性能较高(本来基数排序不需要对桶内部排序,但是由于上面调用的分区算法不稳定,所以需要再排列)
shellSort(start + accuNum, final, sCriterion);
if (numRadix[i]>=L) {
break;
}
}
}
}
template<typename Ite>
void RadixSorter(Ite start, Ite final) {
RadixSorter(start, final, std::less<>{});
}
以下是主函数性能测试代码:
int main(int argc, char* argv[])
{
clock_t average{};
for (int j = 0; j < 100; ++j) {
srand((unsigned int)time(nullptr));
vector<int> nums{};
for (int i = 0; i < 1000; ++i) {
nums.push_back(rand());
}
clock_t start, finish;
start = clock();
RadixSorter(nums.begin(), nums.end());
finish = clock();
if (!is_sorted(nums.begin(), nums.end())) {
cout << "算法测试不成功" << endl;
}
average = (average * j + finish - start) / (j + 1);
}
cout<<"算法测试成功,平均时间为:"<< average;
return 0;
}
算法性能验证
最后各个排序算法在1000的数据量下的平均耗时为(重复100次):
选择:122
冒泡:512
插入:336
归并:139
快速:6
堆排序:621
希尔:14
基数:312
(基数排序之所以这么大有可能是因为我在每个桶内排序的原因)
最后问题:
有时候一些递归算法会爆栈。