排序
目录
常见的排序有以下几种:
------1、冒泡排序
------ 2、选择排序
--------- 3、插入排序
------------4、希尔排序
------------------5、快速排序(递归,非递归)
------------------6、归并排序(递归,非递归)
------------------7、堆排序
------------------------------8、计数排序
黑线长度一样的排序代表他们的时间复杂度在一个量级,也就是排序所花费的时间差距不大,并且黑线越长排序越快。但是计数排序较为特殊,它只能排整数,但是在排整数时效率非常的高。
这篇博客就是来带领大家完成上面的所有排序,并且比较所有排序的效率(degug版本下)。
(1)、冒泡排序,冒泡排序作为最简单的排序,对于刚开始接触的同学来说具有一定的意义。
1、冒泡排序:通过循环比较每一个数和它下一位的数,根据自己的需求进行交换,例如升序,就将前大后小的进行交换,这样一次循环以后就选出了最大的数。
上图就是第一次循环,接下来我们将最后一位固定,将前面的数再次进行这样的操作即可。
因为这个排序很简单,我就直接简单介绍一下。
2、代码如下:
c
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int* getarr()
{
int n = 100000;//数据个数
int* arr = (int*)malloc(sizeof(int) * n);//***
srand((unsigned int)time(0));
for (int i = 0; i < n; i++)
{
arr[i] = rand() + i;//rand生成随机数,但随机数有大量重复,+i可以减少重复
}
return arr;
}
void swap(int* x, int* y)
{
int tep = *x;
*x = *y;
*y = tep;
}
void bubbesort(int* arr, int n)
{
for (int j = 0; j < n; j++)
{
for (int i = 0; i < n - j - 1; i++)//这里n-1是因为下面我们比较的最后一个arr[i+1]中i+1必须小于n,否则会越界
{
if (arr[i] > arr[i + 1])
{
swap(&arr[i], &arr[i + 1]);//交换的代码在其他排序中也会用到所以我们将其封装为函数
}
}
}
}
int main()
{
int* arr = getarr();
int n = 100000;//数据个数
int begin = clock();//程序运行到此处的时间
bubbesort(arr, n);//冒泡排序
int end = clock();//两次时间之差就是程序运行的时间
printf("%d ", end - begin);//打印程序运行的时间,单位为毫秒
/*for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}*/这一部分我们测试时可以使用,测试时将n改小,可以较为直观的观察代码的正确与否。
return 0;
}
3、然后我们通过end-begin观察其效率,不同的计算机效率会有所差异、
当n为10000时,我的程序大概在130毫秒左右,
当n为100000,我的程序在7400毫秒,也就是7.4秒。
大家可以看到这个冒泡在n大于100000已经非常的慢了,所以它处于最低的档次。
它的时间复杂度为O(n^2);
(2)选择排序
1、选择排序一样也是很简单的一个排序,原理是 先经过一次循环,选出最大或者最小的数,将它放于最后或最前,第二次循环选出次大或次小,直到选到最后一个数,就完成了我们的排序, 但是这个排序也非常的慢,所以我们可以将其稍微优化,那就是一次选出最大和最小两个数。
2、代码实现
c
void selectsort(int* arr, int n)
{
int begin = 0, end = n - 1;
while (begin < end)
{
int mini = begin, maxi = begin;
for (int i = begin + 1; i <= end; i++)
{
if (arr[i] < arr[mini])
mini = i;
if (arr[i] > arr[maxi])
maxi = i;
}
swap(&arr[begin], &arr[mini]);
if (maxi == begin)
maxi = mini;
swap(&arr[end], &arr[maxi]);
begin++;
end--;
}
}
int main()
{
int* arr = getarr();
int n = 10000;//数据个数
int begin = clock();//程序运行到此处的时间
//bubbesort(arr, n);//冒泡排序
selectsort(arr, n);//选择排序
int end = clock();//两次时间之差就是程序运行的时间
printf("%d ", end - begin);//打印程序运行的时间
/*for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}*/
return 0;
}
3、当n为10000时我的计算机大概在28毫秒左右,
---- 当n为100000时我的计算机大概在2800毫秒左右。
相较于冒泡还是提升了一些,但当我们的n达到百万级别,也是非常的慢。
它的时间复杂度也为O(n^2);
(3)插入排序
插入排序相较于前两个排序要快了不少,它已经拥有了一些实际应用的场景。
1、原理:就是先将第一个元素作为区间,将第二个元素与前一个区间的元素依次比较,将次放在大于前一个数小于后一个数的位置,然后再将区间扩大至最后一个元素的前一个。
2、代码实现
c
void insertsort(int* arr, int n)
{
for (int i = 0; i < n - 1; i++)
{
int end = i;
int x = arr[end + 1];
while (end >= 0)
{
if (arr[end] > x)
{
arr[end + 1] = arr[end];
end--;
}
else
{
break;
}
}
arr[end + 1] = x;
}
}
int main()
{
int* arr = getarr();
int n = 10000;//数据个数
int begin = clock();//程序运行到此处的时间
//bubbesort(arr, n);//冒泡排序
//selectsort(arr, n);//选择排序
insertsort(arr, n);//插入排序
int end = clock();//两次时间之差就是程序运行的时间
printf("%d ", end - begin);//打印程序运行的时间
/*for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}*/
return 0;
}
3、当n为10000时,效率大概为17毫秒。
----当n为100000时,效率大概为410毫秒。
----当n为1百万时,效率大概为4500毫秒。
从这组数据可以看出插入排序相较于前面两个排序提升相当大。
它的时间复杂度也为O(n^2);
为什么同样为n^2,效率差距如此大呢?那是因为,当数据为随机数时插入排序中的每一趟可能只移动一两个数据,而冒泡和选择要走完每一次循环。
(4)希尔排序
上面我们写了插入排序,那要是原数组中的数据与我们要排的数据顺序刚好相反,那么插入排序就会走满每一趟排序,那么它的效率就会退化,可能会变成冒泡的效率。而希尔排序就是为了解决这种情况而做出的优化。
1、原理:对原数组做预排序,使原数组趋近有序,最后在进行一次插入排序。 大家可能会觉得这样比单纯的插入排序复杂许多,效率会变慢,其实不然。
2、代码:
c
void shellsort(int* arr, int n)
{
int gap = n - 1;//将数据分组,每组gap个数据
while (gap > 1)
{
gap = gap / 3 + 1;
for (int j = 0; j < gap; j++)
{
for (int i = j; i < n - gap; i += gap)
{
int end = i;
int x = arr[end + gap];
while (end >= 0)
{
if (arr[end] > x)
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = x;
}
}
}
}
int main()
{
int* arr = getarr();
int n = 1000000;//数据个数
int begin = clock();//程序运行到此处的时间
//bubbesort(arr, n);//冒泡排序
//selectsort(arr, n);//选择排序
//insertsort(arr, n);//插入排序
shellsort(arr, n);
int end = clock();//两次时间之差就是程序运行的时间
printf("%d ", end - begin);//打印程序运行的时间
/*for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}*/
return 0;
}
3、当n为100万时,效率大概在120毫秒层次。
---当n为1000万时,效率大概在1500毫秒。
通过时间可以看出希尔排序相较于上面的其他排序效率提升了一个档次。
它的时间复杂度较为特殊,大概在O(n^1.3)。 ***
大家只需要记住这个结论即可。
(5)计数排序
这里我就不按顺序介绍了,因为快排、堆排、归并较为复杂一些,这篇博客只介绍简单的几个。
1、原理:我们遍历一遍数组,开一个数组中最大数字空间的数组(初始所有位置全为0),然后再遍历数组,原数组出现哪个数字,在开辟的数组对应下标位置处+1,然后再依次遍历开辟的数组,将数组元素不为0的位置赋值回原数组,数组元素为几就赋值几次。
2、代码:
c
void countsort(int* arr, int n)
{
int min = arr[0], max = arr[0];
for (int i = 1; i < n; i++)
{
if (arr[i] < min)
min = arr[i];
if (arr[i] > max)
max = arr[i];
}//求出最大值与最小值,根据需求开辟数组,否则会大量浪费空间
int k = max - min + 1;
int* count = (int*)calloc(k, sizeof(int));
for (int i = 0; i < n; i++)
{
count[arr[i] - min]++;
}
int x = 0;
for (int j = 0; j < k; j++)
{
while (count[j]--)
{
arr[x++] = j + min;
}
}
free(count);
count = NULL;
}
int main()
{
int* arr = getarr();
int n = 100000000;//数据个数
int begin = clock();//程序运行到此处的时间
//bubbesort(arr, n);//冒泡排序
//selectsort(arr, n);//选择排序
//insertsort(arr, n);//插入排序
//shellsort(arr, n);//希尔排序
countsort(arr, n);//计数排序
int end = clock();//两次时间之差就是程序运行的时间
printf("%d ", end - begin);//打印程序运行的时间
/*for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}*/
return 0;
}
3、当我们的数据量在1千万,排序只需要130毫秒左右,
-----数据量在1亿,排序也只需要1300毫秒左右。
大家可以看到这个效率非常的快,但它仅限于整数,此外负数是可以用这个排序的。
此外快排、堆排、归并下一章继续。