O(n^2)方的排序算法
1. 选择排序(Selection Sort)
- 图解选择排序
- 代码
这里使用模板,可以通过模板来对任何数据类型的数组或自定义类进行排序
template<typename T>
void selectionSort(T arr[], int n)
{
for(int i = 0; i < n; i++)
{
//寻找[i, n)区间中最小元素
int minIndex = i;
for(int j = i + 1; j < n; j++)
{
if(arr[j] < arr[minIndex])
{
minIndex = j;
}
}
//c++标准库函数
swap(arr[i], arr[minIndex]);
}
}
2. 插入排序(Insertion Sort)
- 图解插入排序
- 代码
template<typename T>
static void InsertionSort(T arr[], int n)
{
// i = 1? 因为一个元素不用考虑,本身就有序
for(int i = 1; i < n; i ++)
{
//寻找元素arr[i]合适的插入位置
for(int j = i; j > 0; j--)
{
if(arr[j] < arr[j - 1])
{
swap(arr[j], arr[j - 1]);
}
else
{
break;
}
}
}
}
3. 冒泡排序(Insertion Sort)
- 图解冒泡排序
- 代码
template<typename T>
void bubbleSort(T arr[], int len)
{
for (int i = 0; i < len - 1; i++)
for (int j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1])
{
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
敲黑板,划重点
4. 三种O(n^2)算法性能比较
(1) 创建一个类,里面有各种排序算法的接口
【注意】这些函数接口均定义成静态方法,方便通过类名直接调用
#ifndef SORTTESTHELPER_H
#define SORTTESTHELPER_H
#include <iostream>
#include <ctime>
#include <cassert>
#include <string>
using namespace std;
class SortTestHelper
{
public:
SortTestHelper() {}
virtual ~SortTestHelper() {}
//生成有n个元素的随机数组,每个元素的随即范围为[rangeL, rangeR]
static int *generateRandomArray(int n, int rangeL, int rangeR)
{
assert( rangeL <= rangeR);
int *arr = new int[n];
srand(time(NULL));
for(int i = 0; i < n; i++)
{
arr[i] = rand() % (rangeR - rangeL + 1);
}
return arr;
}
//生成一个几乎有序的随机数组,先生成一个有序数组,将随机交换两个值
static int *generateNearlyOrderedArray(int n, int swapTimes)
{
int *arr = new int[n];
for(int i = 0; i < n; i ++)
{
arr[i] = i;
}
srand(time(NULL));
for(int i = 0; i < swapTimes; i++)
{
int posx = rand() % n;
int posy = rand() % n;
swap(arr[posx], arr[posy]);
}
return arr;
}
//选择排序
template<typename T>
static void selectionSort(T arr[], int n)
{
for(int i = 0; i < n; i++)
{
//寻找[i, n)区间中最小元素
int minIndex = i;
for(int j = i + 1; j < n; j++)
{
if(arr[j] < arr[minIndex])
{
minIndex = j;
}
}
//c++标准库函数
swap(arr[i], arr[minIndex]);
}
}
//插入排序
template<typename T>
static void InsertionSort(T arr[], int n)
{
// i = 1? 因为一个元素不用考虑,本身就有序
for(int i = 1; i < n; i ++)
{
//寻找元素arr[i]合适的插入位置
for(int j = i; j > 0; j--)
{
if(arr[j] < arr[j - 1])
{
swap(arr[j], arr[j - 1]);
}
else
{
break;
}
}
}
}
//冒泡排序
template<typename T>
static void bubbleSort(T arr[], int len)
{
for (int i = 0; i < len - 1; i++)
for (int j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1])
{
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
//数组的打印
template<typename T>
static void printArray(T arr[], int n)
{
for(int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
//测试排序算法的性能
template<typename T>
static void testSort( string sortName, void(* sort)(T[], int), T arr[], int n)
{
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
//endTime - startTime:运行了几个时钟周期
//CLOCKS_PER_SEC:每秒钟运行的时钟周期的个数
cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
return ;
}
//检测数组是否排序成功(默认升序)
template<typename T>
static bool isSorted( T arr[], int n)
{
for(int i = 0; i < n; i++)
{
if(arr[i] > arr[i + 1])
return false;
}
return true;
}
//拷贝数组
template<typename T>
static T* copyArray(T arr[], int n)
{
T* copyArr = new T[n];
copy(arr, arr+n, copyArr);
return arr;
}
};
#endif // SORTTESTHELPER_H
(2) main函数里测试
随机生成拥有10000个元素的随机数组arr,然后拷贝两份arr1,arr2,通过调用testSort接口来比较三种排序算法的性能
#include <iostream>
#include <SortTestHelper.h>
using namespace std;
int main()
{
int n = 10000;
int *arr = SortTestHelper::generateRandomArray(n, 0, n);
int *arr1 = SortTestHelper::copyArray(arr, n);
int *arr2 = SortTestHelper::copyArray(arr, n);
SortTestHelper::testSort("Selection Sort", SortTestHelper::selectionSort, arr, n);
SortTestHelper::testSort("Insertion Sort", SortTestHelper::InsertionSort, arr1, n);
SortTestHelper::testSort("Bubble Sort", SortTestHelper::bubbleSort, arr2, n);
delete[] arr;
delete[] arr1;
delete[] arr2;
return 0;
}
【结果】
【下面让我们测试一个有100000元素的数组】
结果分析
插入排序的性能最好,选择排序和冒泡排序差不多
【问题】因为虽然插入排序可以提前终止循环,但是也不断做着交换,交换比比较更耗时
【解决方案】优化插入排序:用赋值代替交换
(1)原理图
(2)代码
template<typename T>
static void InsertionSort2(T arr[], int n)
{
// i = 1? 因为一个元素不用考虑,本身就有序
for(int i = 1; i < n; i ++)
{
T temp = arr[i];
int j; //保存temp应该插入的位置
for(j = i; j > 0; j--)
{
if(arr[j - 1] > temp)
{
arr[j] = arr[j - 1];
}
else
{
break;
}
}
arr[j] = temp;
}
}
【结论】优化后的插入排序,对相对有序的数组排序效率更高
用以上代码进行测试