目录
插入排序
基本思想
可以将一组混乱无序的数字看作两个类别,一个是有序的序列,一个是无序的序列。排序开始前,有序的序列为该数组的第一个,其余的数字均属于无序的序列。接下来插入排序的核心思想就是:将无序序列中的数字,逐个拿出,插入有序序列中,直至无序序列没有数字,即为排序完成。
时间复杂度为:
程序实现(InsertSort)
void InsertSort(int* arr, int len)
{
int i = 0;
while (i < len - 1)
{
//i表示的是有序序列中数字的个数
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
//[0,end]为有序序列,[end + 1,len - 1]为无序序列,将arr[end + 1]插入,使得[0,end + 1]有序
if (arr[end] > tmp)
{
//将arr[end]向后移动一个位置
arr[end + 1] = arr[end];
end--;
}
if (arr[end] <= tmp)
{
arr[end + 1] = tmp;
break;
}
}
i++;
}
}
希尔排序
基本思路
希尔排序是插入排序的优化,希尔排序的基本思路为:
1.先进行预排序,使数组接近于有序
2.再进行插入排序
gap 的作用为:进行多次预排序,使数组尽可能趋于有序。gap越大,数字可以移动的幅度越大;gap越小,则说明数组趋于有序,当gap==1,即为插入排序。
时间复杂度: ~
程序实现(ShellSort)
//希尔排序是插入排序的优化
//1.先进行预排序,使数组接近于有序
//2.再进行插入排序
void ShellSort(int* arr, int len)
{
int gap = len;
//gap == 1 插入排序
//gap > 1 预排序
while (gap > 1)
{
gap = gap / 3 + 1;
int i = 0;
//间隔为gap的多趟数据同时排序
for (i = 0; i < len - gap; i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (arr[end] >= tmp)
{
arr[end + gap] = arr[end];
end -= gap;
}
if (arr[end] <= tmp)
{
arr[end + end] = tmp;
break;
}
}
}
}
}
堆排序
基本思路
堆的逻辑结构是一棵完全二叉树,物理结构是一个数组。我们可以通过计算数组下标从而判断其父子关系。
parent * 2 + 1 = leftchild
parent * 2 + 2 = rightchild
parent = (child - 1) / 2
堆可以分为两类,最大堆和最小堆。
最大堆 (大堆) | 最大堆中所有的父亲都大于等于孩子 | 根节点储存的值是最大值 |
最小堆 (小堆) | 最小堆中所有的父亲都小于等于孩子 | 根节点储存的值是最大值 |
接下来,我们以数组arr[] = {1,18,5,9,3,8,12,2}为例,进行分析。
如图,堆的物理结构是arr这个数组,其逻辑结构如图。
我们堆排序的前提是建堆(时间复杂度:),这里我们要将数组arr变成一个小堆。这里我们用到的算法是向下调整算法。
向下调整算法:前提是所给的根节点 root 的左右子树都是小堆,利用这个算法可以将 root 为根节点的二叉树转换为一个小堆。具体思路如图。
如图,根节点的左子树和右子树都是小堆,符合向下调整算法的前提。
基本步骤为:
接下来,我们对于数组arr[] = {1,18,5,9,3,8,12,2},它的根节点的左右子树并不满足是小堆的前提。但是我们可以知道,当一棵二叉树深度为2时,这个二叉树的左右子树必然是小堆(以为根节点下就是叶子节点,叶子节点一定是小堆)。所以我们可以从倒数一个非叶子节点的子树开始向下调整,这样子自下而上,就可以保证整个二叉树都变成小堆了。如图:
堆排序
升序 | 建大堆,找最大值 |
降序 | 建小堆,找最小值 |
还是以数组arr[] = {1,18,5,9,3,8,12,2}为例,我们需要升序,所以应该建大堆。
由于最大堆的特性,根节点的值是数组中的最大值,所以我们可以利用这个特性,进行以下操作。
时间复杂度:
程序实现(HeapSort)
void AdjustDown(int* arr, int len, int root)
{
//向下调整算法
//前提:左右子树均为大堆
//取 min(leftchild, rightchild),和 parent 比较
//如果 parent < min(leftchild, rightchild), 则交换位置
int parent = root;
//左孩子的下标
int child = parent * 2 + 1;
//左孩子和右孩子比大小
while (child < len)
{
if (arr[child] < arr[child + 1] && child + 1 < len)
{
child += 1;
}
if (arr[parent] < arr[child])
{
Swap(&arr[parent], &arr[child]);
}
//因为左右子树是大堆,父节点比左右子树的根节点还要大,所以父节点一定是最大的
else
{
break;
}
//迭代条件
parent = child;
child = parent * 2 + 1;
}
}
void HeapSort(int* arr, int len)
{
//建堆
//向下调整算法的前提是左右子树均为大堆
//所以我们可以从倒数一个非叶子节点的子树开始向下调整
//因为叶子节点没有孩子节点,所以一定满足左右子树是大堆
int i;
for (i = (len - 2) / 2; i >= 0; i--)
{
AdjustDown(arr, len, i);
}
int end = len - 1;
for (end = len - 1; end > 0; end--)
{
Swap(&arr[end], &arr[0]);
AdjustDown(arr, end, 0);
}
}
选择排序
基本思路
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全
部待排序的数据元素排完 。
如下动图所示
时间复杂度:
程序实现(SelectSort)
void SelectSort(int* arr, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
int end = i;
int min = end;
for (end = i; end < len; end++)
{
if (arr[end] < arr[min])
{
min = end;
}
}
Swap(&arr[i], &arr[min]);
}
}
冒泡排序
基本思路
将键值较大的记录向序列的尾部移动,或者将键值较小的记录向序列的前部移动。
如下图所示
时间复杂度:
程序实现(BubbleSort)
void BubbleSort(int* arr, int len)
{
int i = 0;
int end = len - 1;
for (end = len - 1; end > 0; end--)
{
int flag = 0;
for (i = 1; i <= end; i++)
{
if (arr[i - 1] > arr[i])
{
Swap(&arr[i - 1], &arr[i]);
flag = 1;
}
}
//如果某一趟并未进行一次交换,则说明该趟已经有序,不需要继续循环
if (flag == 0)
{
break;
}
}
快速排序
基本思路
流程如图所示
一.选取基准值
一般来说,我们选取数组中第一个为基准值。但是,如果要排序的数组是有序的,时间复杂度为 ,我们可以采取三数取中的方法避免该情况:,即取数组中第一个数,最后一个数,和中间一个数的中间大小值作为基准值。
//优化:三数取中
int GetMidIndex(int* arr, int left, int right)
{
int mid = (left + right) / 2;
if (arr[left] > arr[right])
{
if (arr[mid] > arr[left])
{
return left;
}
else if (arr[mid] < arr[right])
{
return right;
}
else
{
return mid;
}
}
else
{
if (arr[mid] < arr[left])
{
return left;
}
else if (arr[mid] > arr[right])
{
return right;
}
else
{
return mid;
}
}
}
二.单趟排序
1.挖坑法
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
如图所示,先进行第一趟的快速排序。
此时我们可以发现,基准值6将原序列分为两个部分,左子列的所有的数都小于基准值,右子列的所有数都大于基准值,且基准值已经放在了正确的位置。
int PartSort1(int* arr, int left, int right)
{
int index = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[index]);
//将arr[begin]放置在正确的位置
int begin = left;
int end = right;
int key = arr[begin];
int pivot = begin;
while (begin < end)
{
//end向前遍历,找小于基准数的值
while (arr[end] >= key && begin < end)
{
end--;
}
arr[begin] = arr[end];
pivot = end;
//begin向后遍历,找大于基准数的值
while (arr[begin] <= key && begin < end)
{
begin++;
}
arr[end] = arr[begin];
pivot = begin;
}
arr[end] = key;
return pivot;
}
2.左右指针法
如下图所示,还是以第一个数确立为基准值,begin,end指针同时移动:begin指向的值大于基准值时,停止;end指向的值小于基准值时,停止。交换arr[begin],arr[end]。直至begin,end相遇,停止循环,将此时二者指向的值和基准值(数组第一个数)交换位置。
int PartSort2(int* arr, int left, int right)
{
int index = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[index]);
//将arr[begin]放置在正确的位置
int begin = left;
int end = right;
int pivot = begin;
while (begin < end)
{
while (arr[end] >= arr[pivot] && begin < end)
{
end--;
}
while (arr[begin] <= arr[pivot] && begin < end)
{
begin++;
}
Swap(&arr[begin], &arr[end]);
}
Swap(&arr[begin], &arr[pivot]);
pivot = begin;
return pivot;
}
3.前后指针法(快慢指针法)
cur向后遍历,每当arr[cur]小于基准值时,先将prev向后遍历一位,arr[cur]和arr[prev]交换位置。(注意先后顺序)
int PartSort3(int* arr, int left, int right)
{
int index = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[index]);
int cur = left + 1;
int prev = left;
int pivot = left;
while (cur <= right)
{
if (arr[cur] < arr[pivot])
{
prev++;
Swap(&arr[cur], &arr[prev]);
}
cur++;
}
Swap(&arr[pivot], &arr[prev]);
pivot = prev;
return pivot;
}
三.分治递归
接下来,我们只需要将[ 0, pivot - 1]和[ pivot + 1, len - 1]这两个子序列排序即可。
分治递归:只要左边界等于于右边界,则说明子序列中只有一个数,则排序完成(递归终止条件)
时间复杂度:
时间复杂度:(有序的情况下时间最长)
小区间优化:当数组被分割到一个比较小的数量时,可以采取直接插入排序,减少递归调用的次数,以缩短运行时间。
if (pivot - left < 10)
{
InsertSort(arr + left, pivot - left);
}
else
{
_QuickSort(arr, left, pivot - 1);
}
if (right - pivot < 10)
{
InsertSort(arr + pivot + 1, right - pivot);
}
else
{
_QuickSort(arr, pivot + 1, right);
}
程序实现(QuickSort)
void QuickSort(int* arr, int len)
{
_QuickSort(arr, 0, len - 1);
}
void _QuickSort(int* arr, int left, int right)
{
if (right <= left)
{
return;
}
//int pivot = PartSort1(arr, left, right);
//int pivot = PartSort2(arr, left, right);
int pivot = PartSort3(arr, left, right);
//_QuickSort(arr, left, pivot - 1);
//_QuickSort(arr, pivot + 1, right);
//小区间优化
if (pivot - left < 10)
{
InsertSort(arr + left, pivot - left);
}
else
{
_QuickSort(arr, left, pivot - 1);
}
if (right - pivot < 10)
{
InsertSort(arr + pivot + 1, right - pivot);
}
else
{
_QuickSort(arr, pivot + 1, right);
}
}
//优化:三数取中
int GetMidIndex(int* arr, int left, int right)
{
int mid = (left + right) / 2;
if (arr[left] > arr[right])
{
if (arr[mid] > arr[left])
{
return left;
}
else if (arr[mid] < arr[right])
{
return right;
}
else
{
return mid;
}
}
else
{
if (arr[mid] < arr[left])
{
return left;
}
else if (arr[mid] > arr[right])
{
return right;
}
else
{
return mid;
}
}
}
int PartSort1(int* arr, int left, int right)
{
int index = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[index]);
//将arr[begin]放置在正确的位置
int begin = left;
int end = right;
int key = arr[begin];
int pivot = begin;
while (begin < end)
{
//end向前遍历,找小于基准数的值
while (arr[end] >= key && begin < end)
{
end--;
}
arr[begin] = arr[end];
pivot = end;
//begin向后遍历,找大于基准数的值
while (arr[begin] <= key && begin < end)
{
begin++;
}
arr[end] = arr[begin];
pivot = begin;
}
arr[end] = key;
return pivot;
}
int PartSort2(int* arr, int left, int right)
{
int index = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[index]);
//将arr[begin]放置在正确的位置
int begin = left;
int end = right;
int pivot = begin;
while (begin < end)
{
while (arr[end] >= arr[pivot] && begin < end)
{
end--;
}
while (arr[begin] <= arr[pivot] && begin < end)
{
begin++;
}
Swap(&arr[begin], &arr[end]);
}
Swap(&arr[begin], &arr[pivot]);
pivot = begin;
return pivot;
}
int PartSort3(int* arr, int left, int right)
{
int index = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[index]);
int cur = left + 1;
int prev = left;
int pivot = left;
while (cur <= right)
{
if (arr[cur] < arr[pivot])
{
prev++;
Swap(&arr[cur], &arr[prev]);
}
cur++;
}
Swap(&arr[pivot], &arr[prev]);
pivot = prev;
return pivot;
}
非递归实现快速排序
递归存在一定的缺陷,我们以下面的程序为例
我们写一个函数 f(n),用于计算小于n的所有正整数的和。该函数就是使用了递归算法。
int f(int n)
{
return n <= 1 ? 1 : f(n - 1) + 1;
}
int main()
{
printf("%d\n", f(10000));
return 0;
}
但是当我们计算f(10000)时,无法得出结果。
我们可以画出递归图理解:
所以,递归有一个致命缺陷,就是如果递归深度太深,可能会导致栈空间不够用,从而导致栈溢出。
在这里,我们将会借助数据结构的栈模拟递归过程,以增加可调用的空间。
void _QuickSort(int* arr, int left, int right)
{
if (right <= left)
{
return;
}
int pivot = PartSort1(arr, left, right);
_QuickSort(arr, left, pivot - 1);
_QuickSort(arr, pivot + 1, right);
}
这是未进行三数取中优化的快速排序,接下来我们将会通过画出递归展开图的方式,理解快速排序的逻辑,并通过数据结构的栈模拟递归调用的过程。
程序实现:
我们将左区间和右区间放入一个结构体 r 中,而栈st存储该结构体。
struct range
{
int left;
int right;
};
- 栈中存储的是无序的序列。
- 我们每次取出栈顶的元素,进行单趟排序,即可将原来一个无序序列 [r.left, r.right] 分为 [r.left, pivot - 1] 和 [pivot + 1, r.right]两个无序序列
- 如果新的无序序列只有一个值,则已经有序,不需要压入栈中;
- 如果新的无序序列还有多个值,则说明无序,需要重新压入栈中。
- 当栈中没有元素时,即完成排序操作。
//非递归实现快排:借助数据结构的栈模拟递归过程
void QuickSortNonR(int* arr, int len)
{
ST st;
StackInit(&st);
struct range r;
r.left = 0;
r.right = len - 1;
StackPush(&st, r);
//如果栈不为空,则说明需要排序
while (!StackEmpty(&st))
{
//取栈顶元素
r = StackTop(&st);
StackPop(&st);
//单趟排序
//将一个无序序列 [r.left, r.right] 分为 [r.left, pivot - 1] 和 [pivot + 1, r.right]两个无序序列
int pivot = PartSort1(arr, r.left, r.right);
struct range tmp;
tmp.left = 0;
tmp.right = 0;
if (pivot + 1 < r.right)
{
tmp.left = pivot + 1;
tmp.right = r.right;
StackPush(&st, tmp);
}
if (r.left < pivot - 1)
{
tmp.left = r.left;
tmp.right = pivot - 1;
StackPush(&st, tmp);
}
}
StackDestory(&st);
}
归并排序
基本思路
如图所示,将原序列依次二分,分到只有一个数时,进行层层有序合并。
单趟排序:
前提:将两个有序的数组,合并成一个有序的数组。
具体步骤:当两个区间有序,同时遍历这两个序列,取二者中较小的值插入临时的数组tmp中,如果这两个序列有剩余,则将剩余的值直接插入tmp中。然后将tmp中的值复制给原数组。
详见:10.30链表进阶_zhangyuaizhuzhu的博客-CSDN博客
//归并:两个有序序列归并到一个有序序列中
int cur = left;
int prev = mid + 1;
int i = left;
while (cur <= mid && prev <= right)
{
if (arr[cur] < arr[prev])
{
tmp[i++] = arr[cur++];
}
else
{
tmp[i++] = arr[prev++];
}
}
while (cur <= mid)
{
tmp[i++] = arr[cur++];
}
while (prev <= right)
{
tmp[i++] = arr[prev++];
}
时间复杂度:
空间复杂度:
程序实现(MergeSort)
void MergeSort(int* arr, int len)
{
//开辟一个临时数组
int* tmp = (int*)malloc(len * sizeof(int));
int i = 0;
//调用子函数,防止多次开辟数组造成内存浪费
_MergeSort(arr, 0, len - 1, tmp);
free(tmp);
}
void _MergeSort(int* arr, int left, int right, int* tmp)
{
if (left >= right)
return;
//二分:[left, mid] 和 [mid + 1, right]
//分割到区间内只有一个数,则有序,则可以归并了
int mid = (left + right) >> 1;
_MergeSort(arr, left, mid, tmp);
_MergeSort(arr, mid + 1, right, tmp);
PartMergeSort(arr, left, right, tmp);
}
void PartMergeSort(int* arr, int left, int right, int* tmp)
{
//归并:两个有序序列归并到一个有序序列中
int mid = (left + right) >> 1;
int cur = left;
int prev = mid + 1;
int i = left;
while (cur <= mid && prev <= right)
{
if (arr[cur] < arr[prev])
{
tmp[i++] = arr[cur++];
}
else
{
tmp[i++] = arr[prev++];
}
}
while (cur <= mid)
{
tmp[i++] = arr[cur++];
}
while (prev <= right)
{
tmp[i++] = arr[prev++];
}
//将tmp中的数据复制到arr数组中
for (i = left; i <= right; i++)
{
arr[i] = tmp[i];
}
}
以测试数组arr[] = { 10,6,7,1,3,9,4,2 }为例:
进行归并排序时,由递归展开图可知:
该程序的思路类似于二叉树的后序遍历,后序遍历是对二叉树的叶子节点进行打印输出;而归并排序也类似,每次遍历到最小的区间,再进行排序操作。
非递归实现归并排序
除了可以用数据结构的栈实现模拟递归,我们还可以通过循环代替递归。
例如,函数 f (n) 需要实现求出前n项的和,除了递归,我们可以用for循环替代递归。
int f(int n)
{
int i = 0;
int sum = 0;
for (i = 0; i <= n; i++)
{
sum += i;
}
return sum;
}
int main()
{
printf("%d\n", f(10000));
return 0;
}
类似地,我们也可以用循环实现归并排序。
在之前的内容中,我们可以知道,归并排序的实质是先分再合。
我们先将一个无序的序列分解到最小的序列,即只有一个数的序列,此时该序列毫无疑问有序。
然后就是合并,我们单趟归并排序就可以将两个有序的序列合并成一个有序的序列。
依据这一特点,我们可以进行如下操作,模拟归并排序的过程。
程序实现
void MergeSortNonR(int* arr, int len)
{
int gap = 1;
int i = 0;
int* tmp = (int*)malloc(len * sizeof(int));
while (gap < len / 2)
{
for (i = 0; i + 2 * gap <= len; i += gap * 2)
{
if (i + gap > len)
{
break;
}
PartMergeSort(arr, i, i + gap * 2 - 1, tmp);
}
gap *= 2;
}
}
计数排序
基本思路
1.绝对映射位置的计数排序
将arr[] = {5, 6, 6, 7, 1, 0, 3, 0}排序:
我们先找到该数组的最大值,然后以最大值为数组的大小开辟一个空间。count [max]
在 arr[] 数组中, max = 7,则开辟一个长度为8的计数数组。
计数数组 | count[0] | count[1] | count[2] | count[3] | count[4] | count[5] | count[6] | count[7] | |
统计每个数出现的个数 | 2 | 1 | 0 | 1 | 0 | 1 | 2 | 1 |
然后我们就可以根据计数数组,进行排序。
arr | arr[0] | arr[1] | arr[2] | arr[3] | arr[4] | arr[5] | arr[6] | arr[7] |
值 | 0 | 0 | 1 | 3 | 5 | 6 | 6 | 7 |
绝对位置映射有一个缺陷:
当最大值很大时,且最小值不为零时,需要的辅助空间count[]将会很大,会造成内存空间的浪费。
例如arr[] = {105, 106, 106, 107, 101, 100, 103, 100}
则需要一个长度为107的数组,但是我们arr[]数组长度仅为8,会造成内存的浪费。
2.相对映射位置的计数排序
与绝对映射位置不同的是,我们需要找到数组中的最小值min。
计数数组的下标为 arr[n] - min
需要开辟数组的大小为 max - min
还是以arr[] = {105, 106, 106, 107, 101, 100, 103, 100}为例
max = 107 min = 100
计数数组 | count[0] | count[1] | count[2] | count[3] | count[4] | count[5] | count[6] | count[7] | count[107] | |
统计每个数出现的个数 | 2 | 1 | 0 | 1 | 0 | 1 | 2 | 1 | 1 |
如此一来,可以减小辅助空间的大小。
特点:没有进行比较运算即可完成排序,适用于范围集中的整形数据排序。
时间复杂度:
空间复杂度:
程序实现(CountSort)
void CountSort(int* arr, int len)
{
//1.找出原数组max和min
int max = arr[0], min = arr[0];
int i = 0;
for (i = 0; i < len; i++)
{
if (arr[i] > max)
{
max = arr[i];
}
if (arr[i] < min)
{
min = arr[i];
}
}
int range = max - min + 1;
//2.建立计数数组
int* count = (int*)malloc(sizeof(int) * (range));
//将count数组置零
memset(count, 0, sizeof(int) * range);
for (i = 0; i < len; i++)
{
count[arr[i] - min]++;
}
//3.根据计数数组进行排序
//i用于遍历arr原数组,j用于遍历计数数组
i = 0;
int j = 0;
for (j = 0; j < range; j++)
{
while (count[j] > 0)
{
arr[i++] = j + min;
count[j]--;
}
}
free(count);
}
排序综述
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记
录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
各个排序算法的优劣如下表格:
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
冒泡排序 | 稳定 | ||||
选择排序 | 不稳定 | ||||
插入排序 | 稳定 | ||||
希尔排序 | ~ | 不稳定 | |||
堆排序 | 不稳定 | ||||
归并排序 | 稳定 | ||||
快速排序 | ~ | 不稳定 | |||
计数排序 | 不稳定 |
这里我们可以通过TestOP这个函数,测试各个排序的性能。
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
#include"Sort.h"
// 测试排序的性能对比
void TestOP()
{
srand(time(0));
const int N = 100000;
int* a1 = (int*)malloc(sizeof(int) * N);
int* a2 = (int*)malloc(sizeof(int) * N);
int* a3 = (int*)malloc(sizeof(int) * N);
int* a4 = (int*)malloc(sizeof(int) * N);
int* a5 = (int*)malloc(sizeof(int) * N);
int* a6 = (int*)malloc(sizeof(int) * N);
int* a7 = (int*)malloc(sizeof(int) * N);
int* a8 = (int*)malloc(sizeof(int) * N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
a7[i] = a1[i];
a8[i] = a1[i];
}
int begin1 = clock();
InsertSort(a1, N);
int end1 = clock();
int begin2 = clock();
ShellSort(a2, N);
int end2 = clock();
int begin3 = clock();
SelectSort(a3, N);
int end3 = clock();
int begin4 = clock();
HeapSort(a4, N);
int end4 = clock();
int begin5 = clock();
QuickSort(a5, N);
int end5 = clock();
int begin6 = clock();
MergeSort(a6, N);
MergeSortNonR(a6, N);
int end6 = clock();
int begin7 = clock();
BubbleSort(a7, N);
int end7 = clock();
int begin8 = clock();
CountSort(a8, N);
int end8 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
printf("SelectSort:%d\n", end3 - begin3);
printf("HeapSort:%d\n", end4 - begin4);
printf("QuickSort:%d\n", end5 - begin5);
printf("MergeSort:%d\n", end6 - begin6);
printf("BubbleSort:%d\n", end7 - begin7);
printf("CountSort:%d\n", end8 - begin8);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
free(a7);
free(a8);
}
void TestInsertSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
InsertSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestShellSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
ShellSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestBubbleSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
BubbleSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestHeapSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
HeapSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestSelectSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
SelectSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestQuickSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
QuickSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestQuickSortNonR()
{
int arr[] = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
QuickSortNonR(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestMergeSortNonR()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
MergeSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestMergeSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
//MergeSort(arr, sizeof(arr) / sizeof(int));
MergeSortNonR(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
void TestCountSort()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
CountSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
}
int main()
{
TestOP();
//TestQuickSortNonR();
//TestMergeSortNonR();
//TestCountSort();
return 0;
}
我们运行1000000个数据,结果如图: