简单插入排序_20230611

简单插入排序_20230611

  1. 前言

简单插入排序是一类直接的排序方法,它属于稳定排序范畴,排序的时间复杂度为O(n2),当数据量较小的时候,插入排序是可选的排序方法。本文探讨最简单、最原始的直接插入排序,数据的比较次数和移动次数都未经过优化操作处理。

  1. 排序描述

插入排序过程把整个数据集合分为两部分,前面第i-1个数据已经排列有序,选择第i个数据,然后通过逐个比较,插入到第i-1数据的合适位置,从而得到前i个有序排列的数据记录。

具体过程可以理解为,先将序列中的第1个记录看成有序子序列,然后从第二个记录起逐个进行插入,指针整个序列变成按关键字非递减的有序序列为止。

  1. 排序过程

已知待排序的一组记录的初始排列所示,可以视为一组无序的初始序列,这个序列需要利用简单的插入排序方法,使其变成按关键字非递减有序。
l i s t = { 49 , 38 , 65 , 97 , 76 , 13 , 27 , 49 } list=\{49,38,65,97,76,13,27,49\} list={49,38,65,97,76,13,27,49}
假设在排序过程中,前面4个记录已按照关键字递增的次序重新排列,构造一个含有4个关键字的子序列。
s u b _ l i s t = { 38 , 49 , 65 , 97 } sub\_list= \{ 38,49,65,97\} sub_list={38,49,65,97}
接下来需要将第5个关键字76插入至子序列当中,以得到一个新的含有5个记录的有序序列。假定从97起向左进行顺序顺序查找,由于65<76<97,那么76应当插入在65和97之间,从而得到下列新的有序子序列:
s u b _ l i s t = { 38 , 49 , 65 , 76 , 97 } sub\_list=\{38,49,65,76,97\} sub_list={38,49,65,76,97}
上述插入76的过程称作一趟直接插入排序。

  1. 简单插入排序实现

4.1 数据类型

数据类型采用顺序表类型的基本结构,每个记录的数据包含关键字和记录的信息值,记录可以定义为结构体类型。

typedef int   KeyType;
typedef char* Record;


typedef struct  RcdType
{
    KeyType key;
    Record  value;
} RcdType;

完成记录的数据基本定义后,接着需要定义顺序表,顺序表的本质是已经分配好的连续数组空间,需要同时规定顺序表中实际所含的元素个数length, 由于顺序表至少包含两个基本数据信息,所以也采用结构体类型。

#define MAX_SIZE 20

typedef struct SqList
{
    RcdType    r[MAX_SIZE + 1]; //record array
    int        length; // number of record, it is the real number in the list
} SqList;

4.2 原始列表生成(无序表)

与上一章相同,采用读取文件的形式生成无需表,文件中每行包含关键字和记录的详细信息,每次读取一行,产生一个关键字,直至记录读取完毕。

#define MAX_LEN 15

void create_list(FILE *fp, SqList *list)
{
    int i;
    int n;
    char str[MAX_LEN];

    n=0;

    while(fgets(str,MAX_LEN,fp)!=NULL)
    {
        n++;
    }
   
    fseek(fp,0,SEEK_SET); //move the FILE pointer to the start position

    list->length=n;

    for(i=1;i<=n;i++)
    {
        list->r[i].value=(Record)malloc(sizeof(char)*MAX_LEN);
        memset(list->r[i].value, 0, sizeof(char) * MAX_LEN);
        fscanf(fp, "%d %s", &(list->r[i].key),list->r[i].value);
    }

    return;
}

4.3 简单排序实现

在排序过程中需要对有序关键字从右到左不断比较,这就涉及到序列的边界问题,即比较不能超过序列的下限,正常的条件下,需要对下标进行判断,确保下标不越界,假如序列中数据量很大,那么判断就是一笔不小的程序逻辑消耗。如何才能减少判断逻辑或者省去判断逻辑呢?这里需要用到空间换时间的逻辑,我们可以把序列表中的r[0]单元作为哨兵单元,然后采用LT(less than)进行大小判断,其中一类终止条件为,r[0]与自身比较,这时候由于不满足LT条件,比较程序自动结束,同时避免了越界的情形发生。

综上描述,实现简单排序的算法,利用变量i跟踪序列中已排序的元素数量,同时通过自加判断序列中所有的元素是否全部完成排序。

程序中利用变量j搜索第r[i]元素待插入的位置,在第一层循环语句中,首先比较i个元素与i-1的大小,如果第i个元素大于i-1个元素,则程序不进行任何操作,也就是说第i个元素现在就应该在这个位置上。

利用变量记录待插入的位置,最后把哨兵位置上的元素复制至待插入的位置即可,这样便完成一轮简单插入排序操作。整个简单插入排序,最坏情况下,需要(n-1)轮比较和插入操作,考虑上元素移动。那么其时间复杂度为O(n2),其空间复杂度为O(1),只需要一个辅助变量便可完成整个操作。

void insert_sort(SqList *list)
{
    int i;
    int j;

    for(i=2;i<=list->length;i++)
    {
        if(LT(list->r[i].key,list->r[i-1].key))
        {
            list->r[0]=list->r[i];
            list->r[i]=list->r[i-1];

            for(j=i-2;LT(list->r[0].key,list->r[j].key);j--)
            {
                list->r[j+1]=list->r[j];
            }

            list->r[j+1]=list->r[0];
        }
    }

    return;
}

  1. 小结

本问题通过回顾简单插入排序操作,理解了检查插入操作的基本原理和实现方式,并对简单插入操作的空间和时间复杂度进行了总结。简单插入排序在数据量不大的情况下,是一类稳定的排序操作,如果数据量很大,那么涉及到比较和移动次数会很大,需要利用其它效率较高的排序方式。

参考资料:

  1. 《数据结构》严蔚敏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值