排序算法简介
1. 冒泡排序
冒泡排序是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
2. 选择排序
选择排序是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
3. 插入排序
插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
4. 归并排序
归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
5. 堆排序
堆排序利用的是堆顶元素为最大(最小)值的性质。该方法先把数组建立成堆的形式,然后不断取出堆顶元素,取出的元素依次放入数组末尾。整个数组分为前端的堆区和末尾的有序区。随着有序的末尾不断变长,前端的堆不断缩小,整个序列逐渐变得有序。
6. 快速排序
快速排序的基本思想是:选择数组中的一个元素作为基准,然后把所有比它小的元素放在一边,所有比他大的元素放在另一边。然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
7. 计数排序
计数排序是一个非基于比较的排序算法。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法。
试验介绍
- 写出以上的排序方法。
- 生成长度为length的随机数组,范围为0 ~length - 1 。
- 分别用不同算法进行排序,记录算法消耗时间。
- 把数组长度增加一倍,再次进行一组实验。
- 在数据相同情况下,比较各种排序算法的耗时。
- 分析当数据量成倍增加时,各种算法的耗时变化规律。
源码分享
/*
* @Author: your name
* @Date: 2020-12-15 21:15:54
* @LastEditTime: 2020-12-17 23:44:36
* @LastEditors:
* @Description: In User Settings Edit
* @FilePath: \VSCODE\kkb\quick_sort.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
void print_arr(int *arr, int length);//打印数组
void bubble_sort(int *arr, const int length);//冒泡排序
void select_sort(int *arr, const int length);//选择排序
void insert_sort(int *arr, const int length);//插入排序
void merge_sort(int *arr, const int length);//归并排序
void heap_sort(int *arr, const int length);//堆排序
void quick_sort(int *arr, const int length);//快速排序
void count_sort(int *arr, const int length);//计数排序
int sort_test(int length);//测试各种排序
int main(int argc, char const *argv[])
{
srand(time(NULL));
for (int length = 10000;length <= 100000 ; length *= 2){
sort_test(length);//进行一组测试
}
return 0;
}
int sort_test(int length)
{
int *arr;
int *test;
arr = malloc(length * sizeof(int));
test = malloc(length * sizeof(int));
for (int i = 0; i < length; i++){
arr[i] = rand() % length;//填充随机数
}
//接下来进行测试
#define TEST(func, args...) {\
memcpy(test, arr, length * sizeof(int));\
clock_t use_t = clock();\
func(test, args);\
use_t = clock() - use_t;\
use_t *= 1000.0 / CLOCKS_PER_SEC;\
printf("%s :\t%ld ms\n", #func, use_t);\
/*print_arr(test,length);*/\
}
printf("The length of array is %d. \n", length);
TEST(bubble_sort, length);
TEST(select_sort, length);
TEST(insert_sort, length);
TEST(merge_sort, length);
TEST(heap_sort, length);
TEST(quick_sort, length);
TEST(count_sort, length);
#undef TEST
free(arr);
free(test);
return 0;
}
void print_arr(int *arr, int length)
{
for (int i = 0; i < length; i++){
printf("%d ", arr[i]);
}
printf("\n");
}
void select_sort(int *arr, const int length)
{
for (int i = 0; i < length - 1;i++){
int min_i = i;//选出从下标 i 开始 最小的元素
for (int j = i + 1; j < length; j++){
if (arr[j] < arr[min_i]) {
min_i = j;
}
}
if (i != min_i){//把未排序区的最小元素放到排序区末尾
int temp = arr[i];
arr[i] = arr[min_i];
arr[min_i] = temp;
}
}
}
void bubble_sort(int *arr, const int length)
{
//排序length-1次
for (int i = 0; i < length - 1;i++){
int is_ok = 1;
int compT = length - i - 1;//本次排序的 比较次数
for (int j = 0; j < compT; j++){
//相邻两个元素比较
if (arr[j] > arr[j + 1]){//交换操作
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
is_ok = 0;
}
}
if (is_ok){
break;//没有交换,已经全部有序,排序完成
}
}
}
void insert_sort(int *arr, const int length)
{
for (int i = 1; i < length; i++){
int temp = arr[i];//本次插入的元素为arr[i]
int j = i - 1;
while (j >= 0 && arr[j] > temp){//比arr[i]大的元素都后移,让出插入位置
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;//arr[i]插入到对应的位置
}
}
void quick_sort(int *arr, const int length)
{
if (length <= 1){
return;
}
int temp = arr[0];//基准元素
int left = 0;
int right = length - 1;
while (left < right){
while (left < right){
if (arr[right] < temp){
arr[left] = arr[right];
break;
}
right--;
}
while (left < right){
if (arr[left] > temp){
arr[right] = arr[left];
break;
}
left++;
}
}
arr[left] = temp;//确定基准元素位置
quick_sort(arr, left);//左边继续排序
quick_sort(arr + left + 1, length - left - 1);//右边继续排序
}
void merge_sort(int *arr, const int length)
{
if (length <= 1){
return;
}
int len1 = length / 2;
int len2 = length - len1;
int *arr1 = arr;
int *arr2 = arr + len1;
merge_sort(arr1, len1);//左半区间排序
merge_sort(arr2, len2);//右半区间排序
int *temp_arr = (int *)malloc(length * sizeof(int));
int index = 0;
int index1 = 0;
int index2 = 0;
while (index < length){//把两个有序的区间合并到临时区域
if (index1 < len1 && (arr1[index1] < arr2[index2] || index2 == len2)){
temp_arr[index++] = arr1[index1++];
}
else{
temp_arr[index++] = arr2[index2++];
}
}
memcpy(arr, temp_arr, length * sizeof(int));//合并完成,复制到原来的数组
free(temp_arr);
}
static void adjust_heap(int *arr, const int length, const int pos)
{
int lchild = 2 * pos + 1;
int rchild = 2 * pos + 2;
int max_index = pos;
if (lchild < length && arr[lchild] > arr[max_index]) {
max_index = lchild;
}
if (rchild < length && arr[rchild] > arr[max_index]) {
max_index = rchild;
}
if (max_index != pos) {//需要交换
int temp = arr[pos];
arr[pos] = arr[max_index];
arr[max_index] = temp;
adjust_heap(arr, length, max_index);//继续调节子树
}
}
void heap_sort(int *arr, const int length)
{
for (int i = length / 2; i >= 0; i--){//建立大顶堆
adjust_heap(arr, length, i);
}
for (int i = length - 1; i > 0; i--){
int temp = arr[i]; //交换 堆顶元素arr[0] 和 arr[i]
arr[i] = arr[0];
arr[0] = temp;
adjust_heap(arr, i, 0); //调整堆
}
}
void count_sort(int *arr, const int length)
{
int min_num = arr[0];//数组最小值
int max_num = arr[0];//数组最大值
for (int i = 1; i < length; i++){
if (arr[i] < min_num){
min_num = arr[i];
}
else if (arr[i] > max_num){
max_num = arr[i];
}
}
int *count_arr = calloc(max_num - min_num + 1, sizeof(int));//根据最大最小值的差确定计数数组大小
for (int i = 0; i < length; i++){
count_arr[arr[i] - min_num]++;//计数
}
for (int i = 0, count_i = 0; count_i < max_num - min_num + 1; count_i++){
while (count_arr[count_i] != 0){
arr[i++] = count_i + min_num;//根据计数结果产生有序数组
count_arr[count_i]--;
}
}
free(count_arr);
}
测试结果
The length of array is 10000.
bubble_sort : 222 ms
select_sort : 164 ms
insert_sort : 21 ms
merge_sort : 3 ms
heap_sort : 2 ms
quick_sort : 1 ms
count_sort : 0 ms
The length of array is 20000.
bubble_sort : 732 ms
select_sort : 542 ms
insert_sort : 61 ms
merge_sort : 4 ms
heap_sort : 2 ms
quick_sort : 1 ms
count_sort : 0 ms
The length of array is 40000.
bubble_sort : 2998 ms
select_sort : 2158 ms
insert_sort : 199 ms
merge_sort : 8 ms
heap_sort : 3 ms
quick_sort : 3 ms
count_sort : 0 ms
The length of array is 80000.
bubble_sort : 12421 ms
select_sort : 8593 ms
insert_sort : 827 ms
merge_sort : 17 ms
heap_sort : 7 ms
quick_sort : 7 ms
count_sort : 1 ms
可以看出,冒泡排序是几种算法中最慢的,而计数排序展现出了强大的能力。对于冒泡排序、选择排序、和插入排序这几种时间复杂度为O(n^2)的算法,当数据量为原来的两倍时,所耗时间为原来的4倍。