八大排序算法【上】 -- 冒泡、简单选择、直接插入

什么是排序 & 排序算法:

排序算法
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。
– 百度百科

简单说,排序算法就是使记录从无需到有序所使用的方法。

排序算法的分类:

根据待排序的的纪录是否全在内存中可将排序分为外排序和内排序。

外排序

记录太多,不能同时全部放在内存中,排序过程中需要和外存交换数据才能进行的排序方法。

内排序

排序的整个过程中,待排序的记录全部放置在内存中的排序方法。

这篇博客介绍的是几种内排序的方法。

排序算法的稳定性 & 性能分析

稳定性

因为是根据关键字来进行排序的,因此可能存在两条记录关键字相等的情况。稳定性就是针对的这种情况。如果两条记录的关键字相同,排序完成后,这两条记录的相对位置不变则稳定性好;反之,如果这两条记录的相对位置改变了,那么算法的稳定性不好。对于不稳定的排序算法,只要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而得到稳定的特性。关于稳定性具体可参见:排序算法 及其稳定性解释。

性能分析

时间性能

时间性能是评判算法好坏的一个重要手段,排序算法主要是:比较和交换。这并不是简单的说一个算法 总的比较次数和交换次数越少 ,时间性能越好;因为可能某两个算法总的比较次数和交换次数相同,但是比较次数和交换次数的比例不一样,那么这两个算法的时间性能也肯定不同。总之,高效的算法肯定是要尽可能的减少比较和交换的次数。

辅助空间

执行某个算法所需要的辅助存储空间大小。辅助存储空间是除了存储待排序所占用的空间之外,执行算法所需要的其他存储空间。

算法的复杂性

这个指的是算法本身的复杂度,而不是时间复杂度。

冒泡排序(Bubble Sort)

基本思想:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。如下面这个gif:
这里写图片描述

排序用到的结构:

# define MAXSIZE 10

typedef struct
{
    int r[MAXSIZE+1];       // r[0] 当哨兵或者临时变量
    int length;
}SqList;

交换函数:

// 交换数据
void swap(SqList * q, int i, int j)
{
    int temp = q->r[i];
    q->r[i] = q->r[j];
    q->r[j] = temp;
}

冒泡排序

void BubbleSort(SqList *L)
{
    int i, j;
    for (i = 1; i < L->length; i++)
    {
        for (j = L->length - 1; j >= i; j--)
        {
            if (L->r[j] > L->r[j+1])
                swap(L, j, j+1);
        }
    }
}

冒泡排序最好情况的时间复杂度为:O(n);因为如果序列本身就是有效的,那么只需要进行 n-1 次比较就可以了。最坏的情况是刚好反序:1 + 2 + 3 + ……+ (n-1) = n(n-1)/2,即时间复杂度为O(n^2)。

简单选择排序算法(Simple Selection Sort)

基本思想:通过一个比较n-i次关键字之间的比较,从n-i+1条记录中选出最小的记录,并和第 i 条记录比较,如果需要,交换之。gif如下:
这里写图片描述

代码如下:

void SelectSort(SqList * L)
{
    int min, i,j;
    for (i = 0; i < L->length; i++)
    {
        min = i;
        for (j = i+1; j <= L->length; j++)
        {
            if (L->r[j] < L->r[min])
                min = j;
        }
        // min 表示最小值的下标。
        if (i != min)
            swap(L, i, min);
    }
}

简单排序算法最好和最坏情况比较次数都是一样多的,而对于交换次数,最坏时:n - 1;也就是 i 的每一次改变都需要交换。最好的情况就是交换为 0 .因此总的时间复杂度还是O(n^2);

直接插入排序(Straight Insertion Sort)

基本思想:将一个记录插入到已经排序好的有序表中,从而得到一个新的、记录数加一的有序表。gif如下:
这里写图片描述
代码如下:

void InsertSort(SqList * L)
{
    int i, j;
    for (i = 2; i < L->length; i++)
    {
        if (L->r[i] < L->r[i-1])
        {
            L->r[0] = L->r[i];
            for (j = i - 1; L->r[j] > L->r[0]; j--)
            {
                L->r[j+1] = L->r[j];
            }
            L->r[j+1] = L->r[0];
        }
    }
}

时间复杂度分析:最好的情况是,表本来就是有序的,也就是说并没有交换,比较的次数为 n-1(只需要进行 if 的比较);最坏的情况就是表为逆序的,需要比较的次数为:2 + 3 + …… + n = (n+2)(n-1)/2; 而移动的次数为(n+4)(n-1)/2; (这个移动次数有点不理解)。因为排序是随机的,所以根据概率相同原则,平均时间复杂度为O(n^2);

注意:虽然冒泡、简单选择、直接插入、三种算法的时间复杂度都是O(n^2),但是直接插入排序的时间复杂度 < 简单选择算法 < 冒泡算法 。只是用大O表示法表示是一样的。

参考资料:

文字部分:大话数据结构
gif:https://visualgo.net/en/sorting

猜你喜欢

转载自blog.csdn.net/williamgavin/article/details/80377368