各种排序算法
测试用的函数
命名空间的名称 SortTestHelper
生成随机数组
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) + rangeL;//生成随机数[l,r]闭区间
}
return arr;
}
生成近乎有序的随机数组
int* generateNearlyRandomArray(int n, int swaptimes)
{
int* ret = new int[n];
for (int i = 0;i < n;i++)
{
ret[i] = i;
}
srand(time(NULL));
for (int i = 0;i < swaptimes;i++)
{
int posx = rand() % n;
int posy = rand() % n;
swap<int>(posx, posy);
}
return ret;
}
打印数组
void printArray(int* array, int n)
{
for (int i = 0;i < n;i++)
{
cout << array[i] << " ";
}
cout << endl;
}
判断是否完成
template<typename T>
bool isSorted(T array[], int n)
{
bool sorted = true;
for (int i = 0;i < n-1;i++)
{
if (array[i] > array[i + 1])return false;
}
return sorted;
}
测试时间
template<typename T>
void testSortTime(string Sortname, void(*sort)(T array[], int n), T array[], int n)
{
clock_t startTime = clock();
sort(array, n);
clock_t endTime = clock();
assert(isSorted<T>(array, n));
cout << Sortname << " " << double(endTime - startTime)/CLOCKS_PER_SEC<<"S" << endl;
}
复制数组
int* copyArray(int a[], int n)
{
int* arr = new int[n];
copy(a, a + n, arr);
return arr;
}
定义不同的数据类型进行测试
定义学生类型并为了方便比较进行操作符重载
struct Student {
string name;
int score;
bool operator<(const Student& other)
{
return(score < other.score);
return(score = other.score ? name < other.name : score < other.score);//自定义的排序方式 根据名字和分数排序
}
friend ostream& operator<<(ostream& out, const Student& student)//返回类型为引用
{
out << "student"<<" "<<student.name << " " << student.score << endl;
return out;
}
};
测试函数 主函数
int main()
{
int n = 50000;
//普通排序
cout << "普通排序" << endl;
int* array = generateRandomArray(n,0,n);
int* array1=copyArray(array, n);
int* array2 = copyArray(array, n);
int* array3 = copyArray(array, n);
testSortTime("bubbleSort", bubbleSort<int>, array, n);
testSortTime("selectSort", selectionSort<int>, array1, n);
testSortTime("insertSort", insertSort2<int>, array2, n);
//testSortTime("mergeSort", mergeSort<int>, array1, n);
//testSortTime("quickSort", quickSort<int>, array2, n);
testSortTime("quickSort2", quickSort2<int>, array3, n);
testSortTime("quickSort3", quickSort3<int>, array, n);
//testSortTime("mergeSortBU", mergeSortBU<int>, array2, n);
delete[] array1;//释放内存
delete[] array2;
delete[] array3;//释放内存
delete[] array;
//大量重复
cout << "大量重复" << endl;
array = generateRandomArray(n, 0, 10);
array1 = copyArray(array, n);
array2 = copyArray(array, n);
array3 = copyArray(array, n);
testSortTime("mergeSort", mergeSort<int>, array1, n);
testSortTime("quickSort", quickSort<int>, array2, n);
testSortTime("quickSort2", quickSort2<int>, array3, n);
testSortTime("quickSort3", quickSort3<int>, array, n);
delete[] array1;//释放内存
delete[] array2;
delete[] array3;//释放内存
delete[] array;
//近乎排序完成
cout << "近乎排序完成" << endl;
array = generateNearlyRandomArray(n, 10);
array1 = copyArray(array, n);
array2 = copyArray(array, n);
array3 = copyArray(array, n);
testSortTime("mergeSort", mergeSort<int>, array1, n);
testSortTime("quickSort", quickSort<int>, array2, n);
testSortTime("quickSort2", quickSort2<int>, array3, n);
testSortTime("quickSort3", quickSort3<int>, array, n);
delete[] array1;//释放内存
delete[] array2;
delete[] array3;//释放内存
delete[] array;
//selectionSort<int>(array1, n);
//printArray(array1, n);
//int array[10] = { 5,8,6,2,10,1,3,7,9,4 };
//Student d[4] = { {"D",90},{"C",70} ,{"A",100} ,{"B",85} };
//selectionSort<Student>(d,4);
//selectionSort<int>(array, 10);
delete[] array1;//释放内存
delete[] array;
}
冒泡排序
如果一次遍历中没有发生交换 说明已经排好序了 可以提前结束
template <typename T>
void bubbleSort(T array[], int n)
{
int already=0;
int i = 0;
while(!already)
{
already = 1;
for (int j = 0;j < n - i-1;j++)
{
if (array[j] > array[j + 1])
sswap(&array[j], &array[j + 1]);
already = 0;
}
i++;
}
}
选择排序
找到每一行的最小数,然后和起始位置进行交换
template <typename T>
void selectionSort(T array[], int n)
{
for (int i = 0;i < n;i++)
{
int minIndex = i;
for (int j = i+1;j < n;j++)
{
if (array[j] < array[minIndex])
{
minIndex = j;
}
}
swap<T>(array[i], array[minIndex]);
}
}
插入排序
原始方式 每次都进行交换
template <typename T>
void insertSort(T array[], int n)
{
for (int i = 1;i < n;i++)
{
//寻找合适的插入位置 一个换一个
for (int j = i;j > 0 && array[j] < array[j-1];j--)
swap<T>(array[j-1], array[j]);
}
}
优化的方式
先把元素后移,找到合适的位置直接插入
template <typename T>
void insertSort2(T array[], int n)
{
for (int i = 1;i < n;i++)
{
T tmp = array[i];
int j=0;
//寻找合适的插入位置 一个换一个
for (j = i;j > 0 && tmp < array[j - 1];j--)
array[j] = array[j - 1];
array[j] = tmp;
}
}
其他函数调用时进行参数修改
快速排序和归并排序在尺寸较小时 可以使用插入排序
template <typename T>
void insertSort2(T array[], int n)
{
for (int i = 1;i < n;i++)
{
T tmp = array[i];
int j=0;
//寻找合适的插入位置 一个换一个
for (j = i;j > 0 && tmp < array[j - 1];j--)
array[j] = array[j - 1];
array[j] = tmp;
}
}
归并排序
原始方式
主函数
template<typename T>
void mergeSort(T array[], int n)
{
__mergeSort(array,0, n - 1);
}
递归调用排序函数
template<typename T>
void __mergeSort(T array[],int left,int right )
{
if (left >= right)
{
return;
}/*
if (right - left <= 15)//优化2 数组比较小的时候使用插入排序然后记得返回
{
insertSort<T>(array, left, right);
return;
}*/
int midle = left + (right - left) / 2;//避免数太大越界
__mergeSort(array, left, midle);
__mergeSort(array, midle + 1, right);
//if(array[midle]>array[midle+1])//优化 当左边最后一个值大于右边第一个值才merge
__merge(array, left, right, midle);
}
merge函数
template<typename T>
void __merge(T array[], int left, int right, int midle)
{
T* tmp = new T[right - left + 1];//新建一个数组
for (int i = 0;i < right - left + 1;i++)
{
tmp[i] = array[left + i];
}
int i = 0;
int j = midle-left+ 1;
int k = 0;
while (k < right - left + 1)
{
if (i > midle-left)
{
array[k + left] = tmp[j];
j++;
}
else if (j > right-left)
{
array[k + left] = tmp[i];
i++;
}
else if (tmp[i] < tmp[j])
{
array[k + left] = tmp[i];
i++;
}
else
{
array[k + left] = tmp[j];
j++;
}
k++;
}
delete[]tmp;
}
优化
优化1 当左边最后一个值大于右边第一个值才merge
优化2 组比较小的时候使用插入排序
template<typename T>
void __mergeSort(T array[],int left,int right )
{
if (right - left <= 15)//优化2 数组比较小的时候使用插入排序然后记得返回
{
insertSort<T>(array, left, right);
return;
}
int midle = left + (right - left) / 2;//避免数太大越界
__mergeSort(array, left, midle);
__mergeSort(array, midle + 1, right);
if(array[midle]>array[midle+1])//优化 当左边最后一个值大于右边第一个值才merge
__merge(array, left, right, midle);
}
自底向上的方法(从小到大 不用递归)
template<typename T>
void mergeSortBU(T array[], int n)
{
for (int sz = 1;sz < n;sz += sz)
{
for (int i = 0;i+sz < n;i += 2*sz)//i+sz是为了保证有第二部分
{
__mergeSort(array, i, i + 2 * sz - 1<n-1? i + 2 * sz - 1:n-1);//三目运算符保证右边不越界
}
}
}
快速排序
原始方式
主函数
template <typename T>
void quickSort(T array[], int n)
{
srand(time(NULL));
__quickSort(array, 0, n - 1);
}
递归函数
template <typename T>
void __quickSort(T array[], int left, int right)
{
if (left >= right)
{
return;
}
int p = __partition(array, left, right);
__quickSort(array, left, p - 1);
__quickSort(array, p + 1, right);
}
分类函数 partition
template <typename T>
int __partition(T array[], int left, int right)
{
int low = left;//保证从[left+1,low]都是小于tmp的值
T tmp = array[left];
int high;//保证从[low+1,high)都大于tmp的值 high表示当前正在处理的值
for(high=left+1;high<=right;high++)
if (array[high] <= tmp)
{
sswap(&array[high], &array[++low]);//这里是++low
//自带的swap函数运行很慢 所以自己写了swap函数速度正常
}
sswap(&array[left], &array[low]);//low成了
return low;
}
partition优化(针对近乎有序序列)
随机选取p的值,通过生成随机种子,再将p的位置和最左边的位置交换,这样保证尽量左右两侧分布均匀,否则对于近乎有序的序列,每次分都是第一个值,两侧分布不均匀。
template <typename T>
int __partition(T array[], int left, int right)
{
int a = rand() % (right - left + 1) + left;//优化 随机选取一个在左右两边的值
sswap(&array[left],&array[a]);
int low = left;//保证从[left+1,low]都是小于tmp的值
T tmp = array[left];
int high;//保证从[low+1,high)都大于tmp的值 high表示当前正在处理的值
for(high=left+1;high<=right;high++)
if (array[high] <= tmp)
{
sswap(&array[high], &array[++low]);//这里是++low
//自带的swap函数运行很慢 所以自己写了swap函数速度正常
}
sswap(&array[left], &array[low]);
return low;
}
优化快排1(针对大量重复序列)
主函数
template <typename T>
void __quickSort2(T array[], int left, int right)
{
if (left >= right)
{
return;
}
int p = __partition2(array, left, right);
__quickSort2(array, left, p - 1);
__quickSort2(array, p + 1, right);
}
template <typename T>
void quickSort2(T array[], int n)
{
srand(time(NULL));
__quickSort2(array, 0, n - 1);
}
优化的partition函数
从两头开始搜索
template <typename T>
int __partition2(T array[], int left, int right)
{
int a = rand() % (right - left + 1) + left;
swap(array[left], array[a]);
int low = left+1;//[left+1,low)都是小于p的数
int high = right;//(high,right]都是大于p的数
T tmp = array[left];
while (true)
{
while (array[low] < tmp&&low<=right)low++;
while (array[high] > tmp&&high>=left+1)high--;
if (low > high)break;
swap<T>(array[low], array[high]);
low++;
high--;
}
swap<T>(array[left], array[high]);
return high;
}
快排优化3(针对大量重复)
主函数
template <typename T>
void quickSort3(T array[], int n)
{
srand(time(NULL));
__quickSort3(array, 0, n - 1);
}
优化partition函数
因为要返回两个指针值,所以不将partition写成函数了
template <typename T>
void __quickSort3(T array[], int left, int right)
{
if (left >= right)
{
return;
}
//int midle = left;
int a = rand() % (right - left + 1) + left;
sswap(&array[left], &array[a]);
T t = array[left];
int low = left;//[left+1,low]是小于t的
int high = right+1;//[high,right]是大于t的
int tmp = left+1;//[low+1,tmp)是等于t的
while (tmp < high)
{
if (array[tmp] > t)
{
sswap(&array[--high], &array[tmp]);
}
else if(array[tmp] < t)
{
sswap(&array[++low], &array[tmp++]);
}
else
{
tmp++;
}
}
sswap(&array[left], & array[low]);
__quickSort3(array, left, low-1);
__quickSort3(array, high, right);
}