三种基本排序算法:桶排序,冒泡排序,快速排序

第一节 桶排序 (最快最简单的排序)

1、概括

就实现申请大小为的数组为例,int a[11]。首先将所有变量初始化为0,表示还没有出现过任何数字。


下面开始处理得到的数字:

若存入的第一个数字是5,就将相对应的a[5]的值在原来的基础上增加1.即将a[5]的值从0改为1,表示5出现过一次。
若第二个数字是3,将相对应的a[3]的值在原来的基础上增加1,即a[3] = 1。
注意!如果下一个数字也是5,将a[5]的值在原来的基础上+1,即a[5]++;

以此类推,处理第四个数字 “2” 和第五个数字 “8” ,最终得到下面的图.

或者可以用插旗子更加直观一些

2、代码实现

// 排序1000以内的数字,降序输出结果
#include <stdio.h>
int main()
{
    int book[1001], i, j, t, n;

    for (i = 0; i <= 1000; i++)
    {
        book[i] = 0; // 将所有的元素初始化为0
    }

    printf("请输入元素个数:");
    scanf("%d", &n); // 输入一个数字n,表示接下来有n个数

    if (n < 0 || n > 1000)
    {
        printf("输入的 'n' 无效。应该在 0 到 1000 之间。\n");
        return 1; // 以错误代码退出程序。
    }

    printf("请输入 %d 个元素:\n", n);

    for (i = 1; i <= n; i++) // 循环读入n个数字,并且进行桶排序
    {
        scanf("%d", &t); // 将每个数读到变量t中

        if (t < 0 || t > 1000) // 错误检测
        {
            printf("输入的 't' 无效。应该在 0 到 1000 之间。\n");
            return 1; // 以错误代码退出程序。
        }

        book[t]++; // 进行计数,对编号为t的桶放一个“旗子”
    }

    // 降序排序
    for (i = 1000; i >= 0; i--) // 依次判断编号1000~1的桶
    {
        for (j = 1; j <= book[i]; j++) // 出现了几次小旗子就将桶的编号打印几次
        {
            printf("%d ", i);
        }
    }
    getchar();
    getchar(); // 暂停程序
    return 0;
}

如果要实现升序排序,只需要将

改为此即可

3、总结:

1、第一个for循环,循环了m次(桶的个数)。第二个for循环,循环了n次。第三个for循环,循环了m+n次。共循环了m+n+m+n次,即时间复杂度为O(2*(m+n)) -> O(M+N)。
2、浪费空间。必须根据数组的大小进行排序,如果我要排序10000和1,那就需要定义a[10001],数组太大,需要遍历到10000。上文只是简单介绍桶排序,真正的桶排序很复杂,解决了这个问题
3、局限性太大。现有3个人的名字和分数,张三:86分,李四:98,王五:68,请按照分数进行升序排序,打印结果 “王五,张三,李四”,但是桶排序只能排序出成绩,无法输出名字,我们无法知道排序后的分数原本对应的哪一个人。这该怎么办呢?请看下节,冒泡排序

第二节 冒泡排序

1、概括

冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。
冒泡排序是我大一接触的第一个算法,比较简单,直接给出例子吧。

2、代码展示:

#include <stdio.h>
int main()
{
    int a[100], i, j, n, t;
    scanf("%d", &n); // 输入需要排序的数字个数
    for (i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
// 核心部分
    for (j = 1; j <= n - 1; j++) // 相邻对比
    {
        for (i = 1; i < n; i++)
        {
            if (a[i] < a[i + 1]) // 比较大小并转换
            {
                t = a[i]; 
                a[i] = a[i + 1];
                a[i + 1] = t;
            }
        }
    }

    for (i = 1; i <= n; i++) // 输出结果
    {
        printf("%d ", a[i]);
    }
    getchar();
    return 0;
}

运行结果如下:

将上面的代码稍加修改,就可以解决第一节遗留的问题了

#include <stdio.h>
struct student
{
    char name[21];
    int score;
}; // 创建结构体存储姓名和分数
int main()
{
    struct student a[100], t;
    int i, j, n;
    scanf("%d", &n); // 输入需要排序的数字个数
    for (i = 1; i <= n; i++)
    {
        scanf("%s %d", a[i].name,&a[i].score);
    }
    // 核心部分
    for (j = 1; j <= n - 1; j++) // 相邻对比
    {
        for (i = 1; i < n; i++)
        {
            if (a[i].score < a[i + 1].score) // 比较大小并转换
            {
                t = a[i];
                a[i] = a[i + 1];
                a[i + 1] = t;
            }
        }
    }

    for (i = 1; i <= n; i++) // 输出结果
    {
        printf("%s\n ", a[i].name);
    }
    getchar();
    return 0;
}

运行结果如下

3、总结:

1、冒泡排序的核心是双重循环嵌套。实践复杂度是O(N^2)。时间复杂度太高,不值得推荐。

第三节 快速排序(最常用的排序)

1、概括

快速排序是跳跃式的排序,通过设置基准点,将大于基准点的放在右侧,小于的放在左侧。
下图直观化表示:

2、代码展示

#include <stdio.h>

#define MAX_SIZE 101 // 定义数组最大大小

int arr[MAX_SIZE], num; // 定义全局数组和变量

void quicksort(int left, int right) {
    if (left > right) {
        return; // 处理边界条件,确保不会越界
    }

    int i = left, j = right, temp, pivot = arr[left]; // 选择第一个元素作为基准数

    while (i != j) {
        while (arr[j] >= pivot && i < j) j--; // 从右向左找比基准数小的元素
        while (arr[i] <= pivot && i < j) i++; // 从左向右找比基准数大的元素

        if (i < j) {
            temp = arr[i]; // 交换元素位置
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }

    arr[left] = arr[i]; // 将基准数归位
    arr[i] = pivot;

    quicksort(left, i - 1); // 递归调用,对左半部分排序
    quicksort(i + 1, right); // 递归调用,对右半部分排序
}

int main() {
    printf("请输入要排序的元素个数:");
    scanf("%d", &num); // 输入元素个数

    if (num < 0 || num > MAX_SIZE) {
        printf("输入的元素个数无效。请确保在 0 到 %d 之间。\n", MAX_SIZE);
        return 1; // 以错误代码退出程序
    }

    printf("请输入 %d 个元素:\n", num);

    for (int i = 1; i <= num; i++) {
        scanf("%d", &arr[i]); // 输入元素
    }

    quicksort(1, num); // 调用快速排序函数

    printf("快速排序的结果为:");
    for (int i = 1; i <= num; i++) {
        printf("%d ", arr[i]); // 输出排序结果
    }

    printf("\n");
    return 0;
}

运行结果展示

3、总结

1、快速排序的时间复杂度是不确定的,最差的结果是O(N^2),他的平均时间复杂度是O(NlogN)。
2、冒泡排序解决了桶排序浪费空间的问题,但是算法的执行效率牺牲了很多。而快速排序既不浪费空间,也可以快一点排序。

posted @ 2023-10-20 14:19  YE-  阅读(20)  评论(0编辑  收藏  举报