迄今为止的排序算法总结

迄今为止的排序算法总结

7.10 迄今为止的排序算法总结

复杂度和稳定性

排序算法 平均情况 最好情况 最坏情况 稳定性 空间复杂度
选择排序 O(n^2) O(n^2) O(n^2) 不稳定(有的书或资料说稳定,以自己的资料为准) O(1)
堆排序 O(nlogn) O(nlogn) O(nlogn) 不稳定 O(1)(新开辟堆来处理数据的堆排序为O(n))
直接插入排序 O(n^2) O(n) O(n^2) 稳定 O(1)
希尔排序 O(nlogn)~O(n^{2}) O(n^{1.3}) O(n^{2}) 不稳定 O(1)
冒泡排序 O(n^2)(这里的所有排序算法里效率最差的) O(n) O(n^2)(这里的所有排序算法里效率最差的) 稳定 O(1)
快速排序 O(nlogn) O(nlogn) O(n^2) 不稳定 O(logn)~O(n)
归并排序 O(nlogn) O(nlogn) O(nlogn) 稳定(主要取决于比较规则,若是数据大小,则要用>=或<=) O(n)
计数排序 O(n+Range)(理论上所有排序算法中最快的,但局限性非常大) O(n+Range)(理论上所有排序算法中最快的,但局限性非常大) O(n+Range)(理论上所有排序算法中最快的,但局限性非常大) 稳定 O(Range)(取决于数据中的极端值)

时间复杂度测试程序

这是一套用于测试在10个数据到100000个数据的情况下,不同排序算法(包括他们的优化版本、未优化版本)的用时的c++代码。计时用的是chrono库的高精度计时,输出的单位是秒。

sortAlgorithm.h

c 复制代码
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>


typedef int Datatype;

//直接选择排序
void selectSort(Datatype a[], int n);

//经优化的选择排序,每次同时选出最小的和最大的
void selectSortplus(Datatype* a, int n);

//堆排序
void heapSort(Datatype* a, int n);

//拷贝方式的堆排序
void heapSortCopy(Datatype* a, int n);

//直接插入排序未优化
void insertSort(Datatype* a, int n);

//直接插入排序小优化
void insertSortPlus(Datatype* a, int n);

//希尔排序,gap=gap/2
void shellSort(Datatype* a, int n);

//希尔排序,gap=gap/3+1
void shellSort2(Datatype* a, int n);

//冒泡排序,因为性能实在太差,只上优化版本
void bubbleSort(Datatype* a, int n);

//快速排序,hoare版本
void quickSortHoare(Datatype* a, int left, int right);

//快速排序,挖坑法
void quickSortHole(Datatype* a, int left, int right);

//快速排序,前后指针
void quickSortDPoint(Datatype* a, int left, int right);

//快速排序,三路划分优化
void quickSortTRoute(int* a, int begin, int end);

//快排的非递归实现,Hoare版本
void quickSortNonRHoare(Datatype* a, int left, int right);

//快排的非递归实现,挖坑法
void quickSortNonRHole(Datatype* a, int left, int right);

//快排的非递归实现,前后指针
void quickSortNonRDPoint(Datatype* a, int left, int right);

//归并排序,无小区间优化
void mergeSort(Datatype* a, int n);

//归并排序,小区间优化
void mergeSortPlus(Datatype* a, int n);

//归并排序,非递归
void mergeSortNonR(Datatype* a, int n);

//计数排序
void countSort(Datatype* a, int n);

sortAlgorithm.cpp

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include"sortAlgorithm.h"

//交换函数
void Swap(Datatype* a, Datatype* b) {
	Datatype t = *a;
	*a = *b;
	*b = t;
}

//直接选择排序
void selectSort(Datatype a[], int n) {
	int i = 0, j = 0, k = 0;
	for (i = 0; i < n; i++) {
		k = i;//每次更新第一个(或最后一个)元素的位置 
		for (j = i + 1; j < n; j++)
			if (a[j] < a[k])//遍历寻找最小值
				k = j;
		if (k != i) {//找到的最小值没有放在第一个位置
			int temp = a[k];
			a[k] = a[i];
			a[i] = temp;
		}
	}
}

//经优化的选择排序,每次同时选出最小的和最大的
void selectSortplus(Datatype* a, int n) {
	int begin = 0, end = n - 1;//起标记作用的两个变量
	while (begin < end) {//能不能加等于,取决于交换数据时用的方法
		int maxi = begin, mini = begin;
		for (int i = begin; i <= end; i++) {
			if (a[i] > a[maxi])
				maxi = i;
			if (a[i] < a[mini])
				mini = i;
		}
		Swap(&a[begin], &a[mini]);
		if (begin == maxi)//处理begin和maxi重叠时的情况
			maxi = mini;
		Swap(&a[end], &a[maxi]);
		++begin;//双指针思路
		--end;
	}
}

//大堆向下调整
void adjustMaxDown(Datatype* a, int n, int parent) {
	int child = parent * 2 + 1;
	while (child < n) {
		if (child + 1 < n)
			if (a[child] < a[child + 1])
				++child;
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else break;
	}
}

//堆排序,
void heapSort(Datatype* a, int n) {
	int i = 0;
	//向下调整建堆,从倒数第一个父结点开始
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
		adjustMaxDown(a, n, i);

	int end = n - 1;
	while (end > 0) {
		int tmp = a[0];
		a[0] = a[end];
		a[end] = tmp;
		adjustMaxDown(a, end, 0);
		--end;
	}
}

//拷贝方式的堆排序
void heapSortCopy(Datatype* a, int n) {//2*N*logN
	//创建一个临时的堆
	Datatype* hp = (Datatype*)malloc(sizeof(Datatype) * n);
	if (hp == NULL)
		return;
	int i = 0;
	for (i = 0; i < n; i++)
		hp[i] = a[i];
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
		adjustMaxDown(hp, n, i);
	int cnt = 0, hpCap = n;
	for (i = n - 1; i >= 0; i--) {
		a[i] = hp[0];
		Swap(&hp[0], &hp[hpCap - 1]);
		hpCap--;
		adjustMaxDown(hp, hpCap, 0);
	}
	free(hp);
}

//直接插入排序未优化
void insertSort(Datatype* a, int n) {
	for (int i = 0, j, k; i < n; i++) {
		for (j = i - 1; j >= 0; j--)//这里并没有挪动操作,所以是浪费了部分时间
			if (a[j] < a[i])//修改这里的比较运算符为<=能使算法不稳定
				break;
		if (j != i - 1) {
			Datatype temp = a[i];
			for (k = i - 1; k > j; k--)
				a[k + 1] = a[k];
			a[k + 1] = temp;
		}
	}
}

//直接插入排序小优化
void insertSortPlus(Datatype* a, int n) {
	for (int i = 0; i < n - 1; ++i) {
		// [0, end] 有序,插入tmp依旧有序
		int end = i;//int end;表示单趟,int end=i;和for表示整个过程
		Datatype tmp = a[i + 1];

		while (end >= 0) {
			if (a[end] > tmp) {//修改这里的比较运算符为>=能使算法不稳定
				a[end + 1] = a[end];
				--end;
			}
			else
				break;
		}
		a[end + 1] = tmp;
	}
}

//希尔排序,gap=gap/2
void shellSort(Datatype* a, int n) {
	int gap = n;
	while (gap > 1) {
		gap = gap / 2;//缩小增量
		int i = 0;
		for (i = 0; i < n - gap; ++i) {//插入排序
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0) {
				if (a[end] > tmp) {
					a[end + gap] = a[end];
					end -= gap;
				}
				else break;
			}
			a[end + gap] = tmp;
		}
	}
}

//希尔排序,gap=gap/3+1
void shellSort2(Datatype* a, int n) {
	int gap = n;
	while (gap > 1) {
		gap = gap / 3 + 1;//+1可以保证最后一次一定是1
		int i = 0;
		for (i = 0; i < n - gap; ++i) {//插入排序
			int end = i;
			Datatype tmp = a[end + gap];
			while (end >= 0) {
				if (a[end] > tmp) {
					a[end + gap] = a[end];
					end -= gap;
				}
				else break;
			}
			a[end + gap] = tmp;
		}
	}
}

//冒泡排序,因为性能实在太差,只上优化版本
void bubbleSort(Datatype* a, int n) {
	int i = 0, j = 0;
	for (i = 0; i < n; i++) {//表示这是第i+1轮排序
		int judg = 0;
		for (j = 1; j < n - i; j++)//表示正在比较的数据
			if (a[j - 1] > a[j]) {
				Swap(&a[j - 1], &a[j]);
				judg = 1;
			}
		if (judg == 0) break;
	}
}


int partSortHoare(Datatype* a, int left, int right) {
	int keyi = left;//keyi记录下标, key记录值。
	while (left < right) {//相遇时结束循环
		// 右边找小
		while (left < right && a[right] >= a[keyi])//利用c语言的短路特性去判断
			--right;
		// 左边找大
		while (left < right && a[left] <= a[keyi])
			++left;
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);//相遇了就交换
	return left;
}

//快速排序,hoare版本
void quickSortHoare(Datatype* a, int left, int right) {
	if (right - left <= 0)//区间只有1个值或区间不存在时没必要继续分
		return;

	// 按照基准值对array数组的 [left, right)区间中的元素进行划分
	int keyi = partSortHoare(a, left, right);

	// 划分成功后以keyi为边界形成了左右两部分 [left, keyi) 和 [keyi+1, right)
	// 递归排[left, div)
	quickSortHoare(a, left, keyi - 1);

	// 递归排[div+1, right)
	quickSortHoare(a, keyi + 1, right);
}

int partSortHole(Datatype* a, int left, int right) {
	int key = a[left];
	int hole = left;//坑
	while (left < right) {
		// 右边找小
		while (left < right && a[right] >= key)
			--right;

		a[hole] = a[right];
		hole = right;//找到数据后就交换,形成新坑

		// 左边找大
		while (left < right && a[left] <= key)
			++left;

		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;//相遇位置即为最后一个坑的位置
	return hole;
}

//快速排序,挖坑法
void quickSortHole(Datatype* a, int left, int right) {
	if (right - left <= 0)//区间只有1个值或区间不存在时没必要继续分
		return;

	// 按照基准值对array数组的 [left, right)区间中的元素进行划分
	int keyi = partSortHole(a, left, right);

	// 划分成功后以keyi为边界形成了左右两部分 [left, keyi) 和 [keyi+1, right)
	// 递归排[left, div)
	quickSortHole(a, left, keyi - 1);

	// 递归排[div+1, right)
	quickSortHole(a, keyi + 1, right);
}

int partSortDPoint(Datatype* a, int left, int right) {
	int prev = left;
	int cur = left + 1;
	int keyi = left;
	while (cur <= right) {
		if (a[cur] < a[keyi] && ++prev != cur)//prev和cur相等时无意义
			Swap(&a[prev], &a[cur]);
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	//keyi = prev; return keyi;
	return prev;
}

//快速排序,前后指针
void quickSortDPoint(Datatype* a, int left, int right) {
	if (right - left <= 0)//区间只有1个值或区间不存在时没必要继续分
		return;

	// 按照基准值对array数组的 [left, right)区间中的元素进行划分
	int keyi = partSortDPoint(a, left, right);

	// 划分成功后以keyi为边界形成了左右两部分 [left, keyi) 和 [keyi+1, right)
	// 递归排[left, div)
	quickSortDPoint(a, left, keyi - 1);

	// 递归排[div+1, right)
	quickSortDPoint(a, keyi + 1, right);
}

//快排的三路划分再优化方案。
void quickSortTRoute(Datatype* a, int begin, int end) {
	if (begin >= end)
		return;

	int left = begin;
	int right = end;
	int cur = left + 1;

	Datatype key = a[left];

	while (cur <= right) {
		if (a[cur] < key) {
			Swap(&a[cur], &a[left]);
			++left;
			++cur;
		}
		else if (a[cur] > key) {
			Swap(&a[cur], &a[right]);
			--right;
		}
		else {
			++cur;
		}
	}
	quickSortTRoute(a, begin, left - 1);
	quickSortTRoute(a, right + 1, end);
}

//快排的非递归实现,Hoare版本
void quickSortNonRHoare(Datatype* a, int left, int right) {
	int* st = (int*)malloc(
		sizeof(int) * 100 * 10//floor(log(right - left + 1) / log(2)) * 3
	);
	//栈开100个空间后就能处理平时99%的数据排列
	//开1000个是因为极端情况下越界严重
	if (st == NULL) {
		printf("栈空间申请失败\n");
		return;
	}
	int top = 0;//一个栈顶变量和一个数组构成的简易栈
	st[top++] = right;
	st[top++] = left;

	while (top != 0) {
		int l = st[top - 1];
		--top;
		int r = st[top - 1];
		--top;
		int keyi = partSortHoare(a, l, r);//单趟排序
		if (keyi + 1 < r) {
			st[top++] = r;//右边先入栈
			st[top++] = keyi + 1;
		}
		if (l < keyi - 1) {
			st[top++] = keyi - 1;//左边后入栈
			st[top++] = l;
		}
	}
	free(st);
}

//快排的非递归实现,挖坑法
void quickSortNonRHole(Datatype* a, int left, int right) {
	int* st = (int*)malloc(
		sizeof(int) * 100 * 10//floor(log(right - left + 1) / log(2)) * 3
	);
	//栈开100个空间后就能处理平时99%的数据排列
	//开1000个是因为极端情况下越界严重
	if (st == NULL) {
		printf("栈空间申请失败\n");
		return;
	}
	int top = 0;//一个栈顶变量和一个数组构成的简易栈
	st[top++] = right;
	st[top++] = left;

	while (top != 0) {
		int l = st[top - 1];
		--top;
		int r = st[top - 1];
		--top;
		int keyi = partSortHole(a, l, r);//单趟排序
		if (keyi + 1 < r) {
			st[top++] = r;//右边先入栈
			st[top++] = keyi + 1;
		}
		if (l < keyi - 1) {
			st[top++] = keyi - 1;//左边后入栈
			st[top++] = l;
		}
	}
	free(st);
}

//快排的非递归实现,前后指针
void quickSortNonRDPoint(Datatype* a, int left, int right) {
	int* st = (int*)malloc(
		sizeof(int) * 100 * 10//floor(log(right - left + 1) / log(2)) * 3
	);
	//栈开100个空间后就能处理平时99%的数据排列
	//开1000个是因为极端情况下越界严重
	if (st == NULL) {
		printf("栈空间申请失败\n");
		return;
	}
	int top = 0;//一个栈顶变量和一个数组构成的简易栈
	st[top++] = right;
	st[top++] = left;

	while (top != 0) {
		int l = st[top - 1];
		--top;
		int r = st[top - 1];
		--top;
		int keyi = partSortDPoint(a, l, r);//单趟排序
		if (keyi + 1 < r) {
			st[top++] = r;//右边先入栈
			st[top++] = keyi + 1;
		}
		if (l < keyi - 1) {
			st[top++] = keyi - 1;//左边后入栈
			st[top++] = l;
		}
	}
	free(st);
}

void _mergeSort(Datatype* a, int begin, int end, Datatype* tmp) {
	if (begin >= end)
		return;
	//不可能出现begin>end的情况
	//除非c语言的除法运算是向上取整

	 小区间优化,优化效率在10%~20%
	//if (end - begin + 1 < 10) {//区间选择[8,15]最佳
	//	insertSort(a + begin, end - begin + 1);
	//	return;
	//}

	int mid = (begin + end) / 2;
	//将这组数据分成两个区间: [begin, mid] [mid+1, end]
	_mergeSort(a, begin, mid, tmp);
	_mergeSort(a, mid + 1, end, tmp);

	// 归并两个区间
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2) {//合并区间
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
		else
			tmp[i++] = a[begin2++];
	}

	//将剩余的数接在临时数组的尾部
	//理论上下面这两个while循环只有一个能进行
	while (begin1 <= end1)
		tmp[i++] = a[begin1++];

	while (begin2 <= end2)
		tmp[i++] = a[begin2++];

	for (i = begin; i <= end; i++)
		a[i] = tmp[i];
}

//归并排序,无小区间优化
void mergeSort(Datatype* a, int n) {
	Datatype* tmp = (Datatype*)malloc(sizeof(Datatype) * n);

	_mergeSort(a, 0, n - 1, tmp);

	free(tmp);
}

void _mergeSortPlus(Datatype* a, int begin, int end, Datatype* tmp) {
	if (begin >= end)
		return;
	//不可能出现begin>end的情况
	//除非c语言的除法运算是向上取整

	// 小区间优化,优化效率在10%~20%
	if (end - begin + 1 < 10) {//区间选择[8,15]最佳
		insertSort(a + begin, end - begin + 1);
		return;
	}

	int mid = (begin + end) / 2;
	//将这组数据分成两个区间: [begin, mid] [mid+1, end]
	_mergeSortPlus(a, begin, mid, tmp);
	_mergeSortPlus(a, mid + 1, end, tmp);

	// 归并两个区间
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2) {//合并区间
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
		else
			tmp[i++] = a[begin2++];
	}

	//将剩余的数接在临时数组的尾部
	//理论上下面这两个while循环只有一个能进行
	while (begin1 <= end1)
		tmp[i++] = a[begin1++];

	while (begin2 <= end2)
		tmp[i++] = a[begin2++];

	for (i = begin; i <= end; i++)
		a[i] = tmp[i];
}

//归并排序,小区间优化
void mergeSortPlus(Datatype* a, int n) {
	Datatype* tmp = (Datatype*)malloc(sizeof(Datatype) * n);

	_mergeSortPlus(a, 0, n - 1, tmp);

	free(tmp);
}

//归并排序非递归实现
void mergeSortNonR(Datatype* a, int n) {
	Datatype* tmp = (Datatype*)malloc(sizeof(Datatype) * n);
	if (tmp == NULL)
		return;

	//通过控制每组要合并的数据个数控制排序本体
	//gap是每组的数据个数,可以是1,2,4,....,n/2
	int gap = 1;
	while (gap < n) {
		int j = 0, i = 0;

		//每次循环跳过两个区间
		for (i = 0; i < n; i += 2 * gap) {
			int begin1 = i, end1 = i + gap - 1;//这样一个区间的数据个数为gap个
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//int j = i;//若j放在for循环内,则要初始化为i。也就是最左边区间的左边界

			// 修正奇数分组归并时区间1囊括所有数据的情况
			if (end1 >= n || begin2 >= n)
				break;

			//修正奇数分组归并时区间2的组数不够导致右区间越界的情况
			if (end2 >= n)
				end2 = n - 1;

			//之后就是正常的归并排序
			while (begin1 <= end1 && begin2 <= end2)
				if (a[begin1] < a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];

			while (begin1 <= end1)
				tmp[j++] = a[begin1++];

			while (begin2 <= end2)
				tmp[j++] = a[begin2++];

			// 归并一组,拷贝一组
			//将所有参与归并的数据进行拷贝
			// 参与归并的区间为[i,end2]
			//memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));

			//这里j的最终的值和end2是一样的,即使经过这一轮拷贝
			for (j = i; j <= end2; j++)
				a[j] = tmp[j];
		}
		//memcpy(a, tmp, sizeof(int) * n);
		//有用break阻止后续归并,一次性拷贝一层无法处理越界的情况,

		gap *= 2;
	}
	free(tmp);
}

//计数排序
void countSort(Datatype* a, int n) {
	Datatype min = a[0], max = a[0];
	int i;
	for (i = 0; i < n; i++) {
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}
	int range = max - min + 1;//求数据的范围
	Datatype* countA = (Datatype*)malloc(sizeof(Datatype) * range);
	if (countA == NULL)
		return;
	memset(countA, 0, sizeof(Datatype) * range);//计数数组

	// 统计次数
	for (i = 0; i < n; i++)
		countA[a[i] - min]++;

	// 排序
	int k = 0, j = 0;
	for (j = 0; j < range; j++)
		while (countA[j]--)
			a[k++] = j + min;
	free(countA);
}

test.cpp

c 复制代码
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif
/*

*/
#include"sortAlgorithm.h"
#include<chrono>
#include<iostream>
using namespace std;

int sortJudg(Datatype* a, int n) {//判断数据是否升序
	int i = 0;
	for (i = 0; i < n - 1; i++)
		if (a[i] > a[i + 1])
			break;
	return i == n - 1;
}

void f0() {//测试排序算法的可行性
	srand((size_t)time(0));
#define NUM 100
	Datatype* a = (Datatype*)malloc(sizeof(Datatype) * NUM),
		* b = (Datatype*)malloc(sizeof(Datatype) * NUM);
	if (a == NULL) return;
	if (b == NULL) return;
	for (int i = 0; i < NUM; i++) {
		a[i] = rand() + 1;
		b[i] = a[i];
	}

	//函数指针数组
	void (*f[13])(Datatype*, int) = {
		selectSort,selectSortplus,
		heapSort,heapSortCopy,
		insertSort,insertSortPlus,
		shellSort,shellSort2,
		bubbleSort,
		mergeSort,mergeSortPlus,mergeSortNonR,
		countSort
	};
	void (*f2[7])(Datatype*, int, int) = {
		quickSortHoare,quickSortHole,quickSortDPoint,quickSortTRoute,
		quickSortNonRHoare,quickSortNonRHole,quickSortNonRDPoint
	};
	for (int i = 0; i < 13; i++) {
		f[i](a, NUM);
		printf("%d\n", sortJudg(a, NUM));
		for (int i = 0; i < NUM; i++) a[i] = b[i];
	}
	for (int i = 0; i < 7; i++) {
		f2[i](a, 0, NUM - 1);
		printf("%d\n", sortJudg(a, NUM));
		for (int i = 0; i < NUM; i++) a[i] = b[i];
	}

	free(a);
	free(b);
#undef NUM
}

void _f1(Datatype* a, int n,
	void (*f)(Datatype*, int), const char* st) {
	auto begin = std::chrono::high_resolution_clock::now();
	f(a, n);
	auto end = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> time1 = end - begin;
	std::cout << st << ":" << time1.count() << endl;
}

//c++的函数重载
void _f1(Datatype* a, int n,
	void (*f)(Datatype*, int, int), const char* st) {
	auto begin = std::chrono::high_resolution_clock::now();
	f(a, 0, n - 1);
	auto end = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> time1 = end - begin;
	std::cout << st << ":" << time1.count() << endl;
}

void f1() {
	srand((size_t)time(0));
	int n = 10;
	int i = 0;
	while (n <= 100000) {
		Datatype* a = (Datatype*)malloc(sizeof(Datatype) * n),
			* b = (Datatype*)malloc(sizeof(Datatype) * n);
		if (a == NULL)
			return;
		if (b == NULL)
			return;
		for (i = 0; i < n; i++) {
			a[i] = (rand()*rand())%n + 1;
			b[i] = a[i];
		}
		printf("%d个数据的情况:\n", n);

		_f1(a, n, selectSort, "selectSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, selectSortplus, "selectSortplus");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, heapSort, "heapSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, heapSortCopy, "heapSortCopy");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, insertSort, "insertSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, insertSortPlus, "insertSortPlus");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, shellSort, "shellSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, shellSort2, "shellSort2");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, bubbleSort, "bubbleSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortHoare, "quickSortHoare");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortHole, "quickSortHole");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortDPoint, "quickSortDPoint");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortTRoute, "quickSortTRoute");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortNonRHoare, "quickSortNonRHoare");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortNonRHole, "quickSortNonRHole");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, quickSortNonRDPoint, "quickSortNonRDPoint");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, mergeSort, "mergeSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, mergeSortPlus, "mergeSortPlus");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, mergeSortNonR, "mergeSortNonR");
		for (i = 0; i < n; i++) a[i] = b[i];

		_f1(a, n, countSort, "countSort");
		for (i = 0; i < n; i++) a[i] = b[i];

		free(a);
		free(b);
		n *= 10;
		printf("\n");
	}
}

int main() {
	//f0();//排序算法可行性分析
	f1();//测试排序耗时
	return 0;
}

这里只敢放10万个数据,因为个别时间复杂度为 O ( n 2 ) O(n^2) O(n2)的排序算法实在太慢了。后面会对数据进行微调来测试不用的情况。

时间复杂度测试结果

其中最具有代表性的是10个数据的情况和大量数据的情况。受到设备的影响,结果可能有差异,所以这里只举其中一个样例。这里的浮点数例如1.4e-05表示的是 1.4 × 1 0 − 5 1.4\times10^{-5} 1.4×10−5,单位是秒

c 复制代码
10个数据的情况:
selectSort:1.04e-05
selectSortplus:3e-07
heapSort:3e-07
heapSortCopy:1.6e-06
insertSort:4e-07
insertSortPlus:1e-07
shellSort:3e-07
shellSort2:3e-07
bubbleSort:2e-07
quickSortHoare:4e-07
quickSortHole:4e-07
quickSortDPoint:4e-07
quickSortTRoute:4e-07
quickSortNonRHoare:1.8e-06
quickSortNonRHole:1.4e-06
quickSortNonRDPoint:1.7e-06
mergeSort:1.8e-06
mergeSortPlus:1.6e-06
mergeSortNonR:1.4e-06
countSort:7e-07

可以看出,插入排序在这种小数据的情况下性能是最优的,这也是为什么小区间优化会选择插入排序的原因。

接下来是10w个数据的情况。可以看到时间复杂度为 O ( n 2 ) O(n^2) O(n2)的排序已经吃不消了,冒泡排序直接蚌埠住了。

c 复制代码
100000个数据的情况:
selectSort:2.45815
selectSortplus:5.77021
heapSort:0.0047019
heapSortCopy:0.0050789
insertSort:1.43777
insertSortPlus:0.87611
shellSort:0.0112118
shellSort2:0.0087459
bubbleSort:12.4946
quickSortHoare:0.0050874
quickSortHole:0.0050904
quickSortDPoint:0.0054573
quickSortTRoute:0.005225
quickSortNonRHoare:0.0053119
quickSortNonRHole:0.004995
quickSortNonRDPoint:0.0051825
mergeSort:0.0092598
mergeSortPlus:0.0087718
mergeSortNonR:0.0075344
countSort:0.0006727

之后将 O ( n 2 ) O(n^2) O(n2)的算法给注释掉,测试极端情况下各个算法排序的性能。

100w是自己的设备在运行带有递归的排序算法时的极限,再扩大10倍就会栈溢出。

c 复制代码
1000000个数据的情况:
heapSort:0.407758
heapSortCopy:0.468729
shellSort:0.217771
shellSort2:0.171042
quickSortHoare:0.185388
quickSortHole:0.133911
quickSortDPoint:0.218722
quickSortTRoute:0.251578
quickSortNonRHoare:0.153858
quickSortNonRHole:0.120381
quickSortNonRDPoint:0.204297
mergeSort:0.147725
mergeSortPlus:0.12035
mergeSortNonR:0.134911
countSort:0.0056529

再将所有带有递归的排序算法注释掉。

c 复制代码
1000000个数据的情况:
heapSort:0.386531
heapSortCopy:0.408616
shellSort:0.202543
shellSort2:0.167401
quickSortNonRHoare:0.154687
quickSortNonRHole:0.120555
quickSortNonRDPoint:0.221774
mergeSortNonR:0.129261
countSort:0.0057139

10000000个数据的情况:
heapSort:5.64962
heapSortCopy:5.85666
shellSort:2.93551
shellSort2:2.6005
quickSortNonRHoare:2.75501
quickSortNonRHole:3.13497
quickSortNonRDPoint:3.75697
mergeSortNonR:1.463
countSort:0.0653788

100000000个数据的情况:
heapSort:89.2091
heapSortCopy:89.406
shellSort:41.1887
shellSort2:27.0528
quickSortNonRHoare:146.442
quickSortNonRHole:172.822
quickSortNonRDPoint:179.666
mergeSortNonR:15.9719
countSort:0.66968

快排的特点是重复越多,效率越慢,而rand函数返回的最大值为32767,于是将数组初始化为rand()*rand()+1以减少重复后的测试结果:

c 复制代码
1000000个数据的情况:
heapSort:0.360787
heapSortCopy:0.368785
shellSort:0.240912
shellSort2:0.169701
quickSortNonRHoare:0.158003
quickSortNonRHole:0.116451
quickSortNonRDPoint:0.241888
mergeSortNonR:0.139041
countSort:0.0012818

10000000个数据的情况:
heapSort:6.07863
heapSortCopy:6.02169
shellSort:3.52872
shellSort2:2.82663
quickSortNonRHoare:1.79774
quickSortNonRHole:1.33191
quickSortNonRDPoint:2.63835
mergeSortNonR:1.62083
countSort:0.0128354

100000000个数据的情况:
heapSort:89.2351
heapSortCopy:95.0854
shellSort:51.2792
shellSort2:36.6936
quickSortNonRHoare:20.3174
quickSortNonRHole:15.1538
quickSortNonRDPoint:30.2327
mergeSortNonR:18.416
countSort:0.130009

到这个应该用外排序来操作的数据量,这些算法能兼职足够说明它们都很优秀。我们能做的是根据平时的情况选择合适的排序算法来进行数据排序。

相关推荐
田梓燊3 分钟前
湘潭大学软件工程算法设计与分析考试复习笔记(五)
笔记·算法·软件工程
为什么每天的风都这么大9 分钟前
编译faiss的C++ API
开发语言·c++·faiss
zybishe14 分钟前
计算机毕业设计原创定制(免费送源码)Java+SpringBoot+MySQL SpringBoot物流配送后台系统
java·css·c++·spring boot·spark·django·课程设计
BIM云平台开发16 分钟前
关于return,yield 和 yield return
java·开发语言·数据结构·c#
小柯J桑_16 分钟前
C++:用红黑树封装map与set-2
开发语言·c++·set·map·红黑树
m0_7482329226 分钟前
JVM的内存区域划分
java·jvm·算法
꧁༺△再临ཊ࿈ཏTSC△༻꧂41 分钟前
K1 升班测试卷(C++ 4道题)
算法
硕风和炜1 小时前
【LeetCode: 743. 网络延迟时间 + Dijkstra】
java·算法·leetcode·面试·dijkstra·最短路径
好好学习++1 小时前
【HF设计模式】01-策略模式
java·c++·设计模式·策略模式
码农飞飞1 小时前
详解Rust字符串用法
开发语言·算法·rust·string·所有权·字符串用法