这里介绍三个O(n^2)的排序算法即插入排序、选择排序和冒泡排序,这三个排序算法应该是所有排序中最简单的。
插入排序:在已经排好序的序列中找一个合适的位置,将那个未被排序的数插入那个位置即可。
选择排序:在未排序序列找到一个最小值放到已排序的序列后面即可。
冒泡排序:每循环一次,通过逐位交换的方式将最大值往未排序的序列的最后一位推即可。
下面的代码针对不同的测试数据对上面三种算法的时间性能进行了相应测试
1.首先我们实现一个用于测试排序算法时间效率的类
#ifndef SORTTESTHELPER_H
#define SORTTESTHELPER_H
#include <bits/stdc++.h>
using namespace std;
namespace SortTestHelper {
int* generateRandomArray(int n, int rangeL, int rangeR) {
// 生成长度为n 且数值范围在rangeL 到 rangeR 之间的随机数组
int* arr = new int[n];
srand(time(0));
for (int i = 0; i < n; i++) {
arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
}
return arr;
}
int* generateNearlyOrderedArray(int n, int swapTimes) { // 生成长度为n,且几乎有序的数组
int* arr = new int[n];
iota(arr, arr + n, 1); // 按顺序生成1--n
srand(time(0));
while (swapTimes--) {
int posx = rand() % n;
int posy = rand() % n;
swap(arr[posx], arr[posy]);
}
return arr;
}
int* copyIntArray(int a[], int n) {
int* arr = new int[n];
copy(a, a + n, arr);
return arr;
}
bool isSorted(int n, int arr[]) {
for (int i = 0; i < n - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
void testSort(string sortName, void(*sort)(int[], int), int arr[], int n) { // 计算算法的执行的时间
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(n, arr));
cout << sortName << ":";
printf("%.7f s\n", double(endTime - startTime) / CLOCKS_PER_SEC);
}
}
#endif // SORTTESTHELPER_H
2.我们再实现上面提到的3个算法。
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int tmp = arr[i];
int j;
for (j = i - 1; j >= 0; j--) {
if (tmp < arr[j]) {
arr[j + 1] = arr[j];
} else {
break;
}
}
arr[j + 1] = tmp;
}
}
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
swap(arr[minIndex], arr[i]);
}
}
void bubbleionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
bool flag = false;
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
flag = true;
swap(arr[j], arr[j + 1]);
}
}
if (!flag) {
break;
}
}
}
void test0() { // 对随机的n个数进行测试
int n = 10000;
int* arr1 = SortTestHelper::generateRandomArray(n, 1, n);
int* arr2 = SortTestHelper::copyIntArray(arr1, n);
int* arr3 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("insertionSort", insertionSort, arr2, n);
SortTestHelper::testSort("selectionSort", selectionSort, arr1, n);
SortTestHelper::testSort("bubbleionSort", bubbleionSort, arr3, n);
delete[] arr1;
delete[] arr2;
delete[] arr3;
}
void test1() { // 对几乎有序的n个数进行测试
int n = 100000;
int* arr1 = SortTestHelper::generateNearlyOrderedArray(n, 100);
int* arr2 = SortTestHelper::copyIntArray(arr1, n);
int* arr3 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("insertionSort", insertionSort, arr1, n);
SortTestHelper::testSort("selectionSort", selectionSort, arr2, n);
SortTestHelper::testSort("bubbleionSort", bubbleionSort, arr3, n);
delete[] arr1;
delete[] arr2;
delete[] arr3;
}
当n=10000时且为随机数的情况:
我们可以看出插入排序是最快的
当n=10000时且近乎有序的情况:
还是插入排序最快,且选择排序的时间几乎没变,说明选择排序几乎不受原有数据的顺序性的影响
当n=100000时且为随机数的情况:
插入排序还是最快的,且三个排序的时间大于为n=10000时所消耗时间的100倍,所以数据量和时间是一个平方的关系
当n=100000时且近乎有序的情况:
可以看出选择排序的时间和n=100000时且为随机数几乎一样,这跟进一步说明了选择排序不受原有数据顺序性的影响,而冒泡却快了很多这是因为我们的代码中进行了一步优化。