各种排序算法的实现与比较

排序算法简介

1. 冒泡排序

冒泡排序是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

2. 选择排序

选择排序是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。

3. 插入排序

插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

4. 归并排序

归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

5. 堆排序

堆排序利用的是堆顶元素为最大(最小)值的性质。该方法先把数组建立成堆的形式,然后不断取出堆顶元素,取出的元素依次放入数组末尾。整个数组分为前端的堆区和末尾的有序区。随着有序的末尾不断变长,前端的堆不断缩小,整个序列逐渐变得有序。

6. 快速排序

快速排序的基本思想是:选择数组中的一个元素作为基准,然后把所有比它小的元素放在一边,所有比他大的元素放在另一边。然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

7. 计数排序

计数排序是一个非基于比较的排序算法。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法。

试验介绍

  1. 写出以上的排序方法。
  2. 生成长度为length的随机数组,范围为0 ~length - 1 。
  3. 分别用不同算法进行排序,记录算法消耗时间。
  4. 把数组长度增加一倍,再次进行一组实验。
  5. 在数据相同情况下,比较各种排序算法的耗时。
  6. 分析当数据量成倍增加时,各种算法的耗时变化规律。

源码分享

/*
 * @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倍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值