这里开始整理常见的排序算法.
本文包含 [ 选择排序, 插入排序, 归并排序 ]
type right by Thomas Alan 光风霁月023 .XDU
准备测试用例
准备main.cpp文件, 这个文件就是测试用例的入口, 写完算法以后在这里添加内容.
另附, 各种排序算法的时间复杂度. https://blog.csdn.net/weixin_43207025/article/details/114902065
/*
* @Description:
* @Version: 1.0
* @Autor: Thomas
* @Date: 2022-07-14 18:00:49
* @LastEditors: Thomas
* @LastEditTime: 2022-12-29 19:08:39
*/
// 这里include了全局的定义文件和helper文件
#include "define/common_include.h"
#include "helper/sort_test_helper.h"
// 这里include各种排序算法的算法实现文件, 根据个人的目录安排来include
#include "sort/selection_sort.h"
#include "sort/insertion_sort.h"
#include "sort/merge_sort.h"
#include "sort/quick_sort.h"
#include "sort/heap_sort.h"
int main()
{
int num = 500000;
int *arr = SortTestHelper::generateRandomArray(num, 0, num);
// int *arr = SortTestHelper::generateNearlyOrderedArray(num, 100);
// sort examples
if (1) // 因为main函数我已经加了不少东西, 所以把测试用例部分放到一个单独的代码块中
{
int *arr2 = SortTestHelper::copyIntArray(arr, num);
int *arr3 = SortTestHelper::copyIntArray(arr, num);
int *arr4 = SortTestHelper::copyIntArray(arr, num);
int *arr5 = SortTestHelper::copyIntArray(arr, num);
int *arr6 = SortTestHelper::copyIntArray(arr, num);
int *arr7 = SortTestHelper::copyIntArray(arr, num);
int *arr8 = SortTestHelper::copyIntArray(arr, num);
int *arr9 = SortTestHelper::copyIntArray(arr, num);
int *arr10 = SortTestHelper::copyIntArray(arr, num);
// ortTestHelper::testSort("selection sort", selectionSort, arr, num);
// SortTestHelper::testSort("insertion sort", insertionSort, arr2, num);
SortTestHelper::testSort("merge sort", mergeSort, arr3, num);
SortTestHelper::testSort("merge sort bu", mergeSortBU, arr4, num);
SortTestHelper::testSort("quick sort", quickSort, arr5, num);
SortTestHelper::testSort("quick sort 2", quickSort2, arr6, num);
SortTestHelper::testSort("quick sort 3 ways", quickSort3Ways, arr7, num);
SortTestHelper::testSort("heap sort", heapSort1, arr8, num);
SortTestHelper::testSort("heap sort2", heapSort2, arr9, num);
SortTestHelper::testSort("heap sort3", heapSort, arr10, num);
delete[] arr3;
delete[] arr4;
delete[] arr5;
delete[] arr6;
delete[] arr7;
delete[] arr8;
delete[] arr9;
delete[] arr10;
}
delete[] arr;
system("pause");
return 0;
}
一、选择排序
从头遍历数组, 找出后面最小的元素, 与开头交换位置.
template
void selectionSort(T arr[], TINT32 num)
{
for (TINT32 idx = 0; idx < num; idx++)
{
// 寻找[idx,n)中的的最小值的索引, 将其与idx位置的元素交换位置
TINT32 min_idx = idx;
for (TINT32 idj = idx + 1; idj < num; idj++)
{
if (arr[idj] < arr[min_idx])
{
min_idx = idj;
}
}
swap(arr[idx], arr[min_idx]);
}
}
二、插入排序
遍历整个数组, 将新遍历的数插入到之前已经排序好的数组之中.
template
void insertionSort(T arr[], TINT32 num)
{
for (TINT32 idx = 1; idx < num; idx++)
{
T key = arr[idx];
TINT32 idj = idx; // idj就是元素最后插入的位置, 后面的过程就是把已经排序好的, 大的元素向后移一位, 直到key找到合适的位置.
for (idj = idx; idj > 0 && arr[idj - 1] > key; idj--)
{
arr[idj] = arr[idj - 1];
}
arr[idj] = key;
}
}
对数组指定范围进行插入排序
template
void insertionSort(T arr[], TINT32 l, TINT32 r)
{
for (TINT32 idx = l + 1; idx <= r; idx++)
{
T key = arr[idx];
TINT32 idj = idx;
for (idj = idx; idj > l && arr[idj - 1] > key; idj--)
{
arr[idj] = arr[idj - 1];
}
arr[idj] = key;
}
}
三、归并排序
1. 自顶向下的归并
递归将整个数组不断分割两部分, 分割到只有一个元素时合并.
// 1. 归并操作
template
// 对arr的[l...mid]部分和arr的[mid+1...r]部分进行归并
void __merge(T arr[], TINT32 l, TINT32 mid, TINT32 r)
{
T aux[r - l + 1];
for (TINT32 idx = l; idx <= r; idx++)
{
aux[idx - l] = arr[idx];
}
TINT32 idx = l;
TINT32 idj = mid + 1;
for (TINT32 k = l; k <= r; k++)
{
if (idx > mid)
{
// 左边的组都归并完毕
arr[k] = aux[idj - l];
idj++;
}
else if (idj > r)
{
// 右边的组都归并完毕
arr[k] = aux[idx - l];
idx++;
}
else if (aux[idx - l] < aux[idj - l])
{
arr[k] = aux[idx - l];
idx++;
}
else
{
arr[k] = aux[idj - l];
idj++;
}
}
}
// 2. 递归对数组arr的[l...r]范围元素进行排序
void __mergeSort(T arr[], TINT32 l, TINT32 r)
{
// if (l >= r)
// {
// return;
// }
if (r - l <= 15)
{
// 长度小的时候用插入排序效率高
insertionSort(arr, l, r);
return;
}
TINT32 mid = (l + r) / 2; // l + r 可能发生数据溢出, 暂时不考虑
__mergeSort(arr, l, mid); // 对 arr的[l...mid]范围进行归并排序
__mergeSort(arr, mid + 1, r); // 对 arr的[mid+1...r]范围进行归并排序
if (arr[mid] <= arr[mid + 1])
{
// 如果arr的[l...mid]最大的数小于或等于arr的[mid+1...r]最小的数, 那自然不用再归并
return;
}
__merge(arr, l, mid, r); // 对arr的[l...mid]部分和arr的[mid+1...r]部分进行归并
}
// 3. 入口
template
void mergeSort(T arr[], TINT32 num)
{
__mergeSort(arr, 0, num - 1);
}
2. 自底向上的归并
循环着从一个元素开始, 将数组归并.
template
void mergeSortBU(T arr[], TINT32 num)
{
for (TINT32 sz = 1; sz <= num; sz += sz)
{
if (sz <= 15)
{
// 长度小的时候用插入排序效率高
for (TINT32 idx = 0; idx + sz < num; idx += sz + sz)
{
insertionSort(arr, idx, min(idx + sz + sz - 1, num - 1));
}
}
else
{
for (TINT32 idx = 0; idx + sz < num; idx += sz + sz)
{
__merge(arr, idx, idx + sz - 1, min(idx + sz + sz - 1, num - 1));
}
}
}
}
自底向上的归并算法, 需要明确将数组的分组情况, 尤其需要注意索引越界的问题. idx + sz + sz - 1有可能超过num - 1, 这里需要取两者的较小值.