三种常用又简单的排序算法

2021-11-09

关键字:桶排序


1、桶排序

桶排序一般用于对一组知道上下限的整数序列中。

因为桶排序的核心原理就是全覆盖式计数,为整个区间每一个数创建一个计数器,遍历待排序序列,为每一个出现的数计数加1,最后根据需要从头至尾或从尾至头打印区间计数。

举个例子,假设要为一个班级的学生数学考试分数做个排序。

已知分数范围为 0 ~ 100,是一个知道上下限的区间。现有分数:52、70、41、99、64、64。创建一个101个长度的数组score,第0号表示成绩为0分的人数,第1号为成绩为1分的人数,第101号为成绩为100分的人数。遍历待排序序列,第一个成绩为52,则将score数组第53号的值加1,第二个成绩为70,则将score数组第71号的值加1,依此类推直至结尾。当我们需要获取排序结果时,例如想要将成绩从高到低打印出来,则可以逆遍历score数组,当其计数值大于0时则打印出来即可。

桶排序的优点就是速度快,缺点是对序列元素有要求,且其空间占用会随着序列规模的增大而增大。

以下是一个桶排序的C语言实例:

#include <stdio.h>
#include <string.h>

int main()
{
    //待排序序列
    const char scores[] = {71, 100, 7, 40, 88, 100, 91, 96, 71, 84, 60, 40, 71, 0, 1, 0};
    //序列上下限为 0 ~ 100 的整数。
    char score_bucket[101];
    
    memset(&score_bucket, 0, 101);
    
    int count = sizeof(scores) / sizeof(char);
    int i;
    for(i = 0; i < count; i++)
    {
        score_bucket[scores[i]] += 1;
    }
    
    //打印结果(递减顺序)
    int j = 0;
    for(i = 100; i >= 0; i--)
    {
        if(score_bucket[i])
        {
            for(j = 0; j < score_bucket[i]; j++)
            {
                printf("%d,", i);
            }
        }
    }
    printf("\n");
    return 0;
}

2、冒泡排序

冒泡排序对空间的要求非常小,时间复杂度为O(n(n+1))约等于O(n2)。

其核心原理是从头到尾两两比较,将顺序不正确的两个数互换一下位置,如此一趟下来能将符合要求的最大或最小数挪到原数据的最末位,然后再重复一次操作,只不过这次因为最末一位已经是正确的顺序了,第二轮比较就只到第n-1位了。如此循环直至原数组中已排序序列数量涨至n-1个为止即表示整个排序已完成。

以下是冒泡排序的C语言实例:

#include <stdio.h>

int main()
{
    //待排序序列
    char scores[] = {71, 100, 7, 40, 88, 100, 91, 96, 71, 84, 60, 40, 71, 0, 1, 0};
    
    int count = sizeof(scores) / sizeof(char); //序列长度
    int i;
    int j;
    int loop = count - 1; //最后一个元素不用参与到排序中。
    char tmp;
    for(i = 0; i < loop; i++)
    {
        for(j = 0; j < loop - i; j++)
        {
            if(scores[j] > scores[j + 1])
            {
                tmp = scores[j];
                scores[j] = scores[j+1];
                scores[j+1] = tmp;
            }
        }
    }
    
    //print
    for(i = 0; i < count; i++)
    {
        printf("%d,", scores[i]);
    }
    printf("\n");
    
    return 0;
}

3、快速排序

桶排序速度快,但是空间消耗大。冒泡排序空间消耗小,但是速度慢。快速排序则可以一定程度上兼顾空间和时间消耗,它在数值比较上采用与冒泡排序类似的两两交换法,在空间消耗上采用函数递归调用,虽然递归调用函数也会随着序列规模上升而增长,但相对来讲空间占用还是比桶排序好很多。

快速排序的核心是选中一个基准数,在递增排序的需求下将所有大于基准数的元素放在基准数右边,将所有小于基准数的元素放在其左边。然后对分别对左半子序列和右半子序列应用同样的算法,直至子序列只剩一个元素为止。通常为了方便,基准数选第1个或最后一个。

快速排序的实现原理是将待排序序列第1个元素作为基准数,分别从序列两端收缩遍历取值。如果基准数选的是第1个,则首先从右端收缩,若基准数选的是最后一个,则首先从左端收缩。当从右端开始收缩时,一直收缩直到遇到一个数小于基准数为止,然后收缩左端,直到找到一个数大于基准数为止。然后直接交换这两个数,之后再重复两端的收缩过程,直至两端收缩碰撞为止。收缩碰撞后将碰撞值直接与基准数对调,之后便可对生成的两个子序列再分别应用此算法,直至子序列只有一个元素为止。

以下是一个C语言版的实例:

#include <stdio.h>static void quick_sort(char nums[], int start, int end)
{
    char tmp;
    int i = start;
    int j = end;
    if(start >= end)
        return;

    while(1)
    {
        if(i == j)
        {
            //直接跟基数交换
            tmp = nums[start];
            nums[start] = nums[j];
            nums[j] = tmp;
            
            //迭代左边的
            if(start < j)
            {
                quick_sort(nums, start, j - 1);
            }
            
            //迭代右边的
            if(end > i)
            {
                quick_sort(nums, i + 1, end);
            }
            
            //排序完成
            break;
        }
        else
        {
            //j先动。
            if(nums[j] < nums[start])
            {
                //轮到i动
                if(nums[i] > nums[start])
                {
                    if(i != j)
                    {
                        //可以交换
                        tmp = nums[j];
                        nums[j] = nums[i];
                        nums[i] = tmp;
                    }
                }
                else
                {
                    i++;
                }
            }
            else
            {
                j--;
            }
        }
    }
}

int main()
{
    //待排序序列
    char nums[] = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
    
    int i;
    quick_sort(nums, 0, 9);
    
    for(i = 0; i < 10; i++)
    {
        printf("%d, ", nums[i]);
    }
    printf("\n");
    
    return 0;
}

+++
原文地址:https://www.cnblogs.com/chorm590/p/15522034.html