插入排序详解

插⼊排序算法是所有排序⽅法中最简单的⼀种算法,其主要的实现思想是将数据按照⼀定的顺序⼀ 个⼀个的插⼊到有序的表中,最终得到的序列就是已经排序好的数据。

1. 直接插⼊排序

直接插⼊排序是插⼊排序算法中的⼀种,采⽤的⽅法是:在添加新的记录时,使⽤顺序查找的⽅式 找到其要插⼊的位置,然后将新记录插⼊。

直接插⼊排序(Straight Insertion Sort)的基本思想是:

  • 把n个待排序的元素看成为⼀个有序表和⼀个⽆序表。
  • · 开始时有序表中只包含1个元素,⽆序表中包含有n-1个元素,排序过程中每次从⽆序表中取出第⼀ 个元素,将它插⼊到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。

插⼊排序算法还包括折半插⼊排序、2-路插⼊排序和希尔排序等

例如采⽤直接插⼊排序算法将⽆序表{3,1,7,5,2,4,9,6}进⾏升序排序的过程为:

  • ⾸先考虑记录 3 ,由于插⼊排序刚开始,有序表中没有任何记录,所以 3 可以直接添加到有序表 中,则有序表和⽆序表可以如图 1所示:

  •  向有序表中插⼊记录 1 时,同有序表中记录 3 进⾏⽐较,1<3,所以插⼊到记录 3 的左侧,如图所示:

  • 向有序表插⼊记录 7 时,同有序表中记录 3 进⾏⽐较,3<7,所以插⼊到记录 3 的右侧,如图 所示:

  • 向有序表插⼊记录 2 时,同有序表中记录 7进⾏⽐较,2<7,再同 5,3,1分别进⾏⽐较,最终确 定 2 位于 1 和 3 中间,如图所示:

  • 照此规律,依次将⽆序表中的记录 4,9 和 6插⼊到有序表中,如图所示:

直接插⼊排序:将待排序的数组,分成两个序列,前⾯的序列保持有序,依次选择后⾯的元素,往 前⾯插⼊。(就地排序、稳定排序)

2. 折半插⼊排序

上述算法在查找插⼊位置时,采⽤的是顺序查找的⽅式,⽽在查找表中数据本身有序的前提下,可 以使⽤折半查找来代替顺序查找,这种排序的算法就是折半插⼊排序算法。

3. 2-路插⼊排序

2-路插⼊排序算法是在折半插⼊排序的基础上对其进⾏改进,减少其在排序过程中移动记录的次数 从⽽提⾼效率。

具体实现思路为:另外设置⼀个同存储记录的数组⼤⼩相同的数组 d,将⽆序表中第⼀个记录添加 进 d[0] 的位置上,然后从⽆序表中第⼆个记录开始,同 d[0] 作⽐较:如果该值⽐ d[0] ⼤,则添加到其 右侧;反之添加到其左侧。

在这⾥的数组 d 可以理解成⼀个环状数组。

3. 希尔排序

希尔排序,⼜称“缩⼩增量排序”,也是插⼊排序的⼀种,但是同前⾯⼏种排序算法⽐较来看,希尔 排序在时间效率上有很⼤的改进。

在使⽤直接插⼊排序算法时,如果表中的记录只有个别的是⽆序的,多数保持有序,这种情况下算 法的效率也会⽐较⾼;除此之外,如果需要排序的记录总量很少,该算法的效率同样会很⾼。希尔排序 就是从这两点出发对算法进⾏改进得到的排序算法。

希尔排序的具体实现思路是:先将整个记录表分割成若⼲部分,分别进⾏直接插⼊排序,然后再对整个 记录表进⾏⼀次直接插⼊排序。

例如⽆序表{49,38,65,97,76,13,27,49,55,4}进⾏希尔排序的过程为:

  • ⾸先对 {49,13},{38,27},{65,49},{97,55},{76,4} 分别进⾏直接插⼊排序(如果需要调 换位置也只是互换存储位置),如下图所示:

 上图中两两进⾏⽐较,例如 49 和 13 进⾏⽐较,13<49,所以交换存储位置。

  • 通过⼀次排序,⽆序表中的记录已基本有序,此时还可以再进⾏⼀次分割,如下图所示:

  • 经过两次分割,⽆序表中已基本有序,此时对整张表进⾏⼀次直接插⼊排序(只需要做少量的⽐较 和插⼊操作即可),最终希尔排序的结果为:

希尔排序的过程中,对于分割的每个⼦表,其各⾃包含的记录在原表中并不是相互挨着的,⽽是相 互之间相隔着某个固定的常数。例如上例中第⼀次排序时⼦表中的记录分割的常量为 5,第⼆次排序时 为 3。

通过此种⽅式,对于关键字的值较⼩的记录,其前移的过程不是⼀步⼀步的,⽽是跳跃性的前移, 并且在最后⼀次对整表进⾏插⼊排序时减少了⽐较和排序的次数。

//insertSort.h
#ifndef INSERT_SORT_H
#define INSERT_SORT_H
#include "../sortHelper.h"

// 直接插入排序(找位置插入)
void insertSort(SortTable *table);
// 直接插入法(扑克牌的交换)
void insertSortV1(SortTable *table);
// shell排序
void shellSort(SortTable *table);
// shell排序
void shellSort1(SortTable *table);
#endif

#include "insertSort.h"
void insertSortV1(SortTable*table)
{
    for(int i=1;i<table->length;i++)
    {
        Element e=table->data[i];
        int j;
        for(j=i;j>0&&table->data[j-1].key>e.key;j--)
        {
            swapElement(&table->data[j-1],&table->data[j]);
        }
        table->data[j]=e;
    }
}
/* 1. 默认第一个元素就是有序,那么从第二个元素开始和前面有序的区域进行比较
 * 2. 待插入的元素i,和有序区域从后往前依次查找
 * 		若待插入元素 < 以有序区域的值,有序的区域的值后移一位
 * 		插入查找的位置[0...i-1]的位置
 * */
void insertSort(SortTable *table)
{

    for(int i=1;i<table->length;++i)
    {
            int j = i - 1;
            Element tmp = table->data[i];
            while (j > -1 && tmp.key < table->data[j].key) {
                table->data[j + 1] = table->data[j];
                j--;
            }
            table->data[j + 1] = tmp;
        }
}
void shellSort(SortTable *table)
{
    int gap;
    for(gap=table->length/2;gap>0;gap/=2)
    {
        for(int i=gap;i<table->length;++i)
        {
            Element temp=table->data[i];
            int j;
            for(j=i;j>=gap&&table->data[j-gap].key>temp.key;j-=gap ){
                swapElement(&table->data[j],&table->data[j-gap]);
            }
            table->data[j]=temp;
        }

    }
}
void shellSort1(SortTable *table)
{
    int gap;
    for(gap=table->length/2;gap>0;gap/=2)
    {
        for(int i=gap;i<table->length;++i)
        {
            Element temp=table->data[i];
            int j;
            for(j=i;j>=gap&&table->data[j-gap].key>temp.key;j-=gap ){
                swapElement(&table->data[j],&table->data[j-gap]);
            }
            table->data[j]=temp;
        }

    }
}
#include "insertSort.h"

int main()
{
    int n = 100000;	// 增大数字
    SortTable *table1 = generateRandomArray(n, 0, n);
    SortTable *table2 = copySortTable(table1);
    SortTable *table3 = copySortTable(table1);
    SortTable *table4 = generateLinearArray(n, 10);

    testSort("random insertSort: ", insertSortV1, table1);
    testSort("random insertSort: ", insertSort, table2);
    testSort("shell  insertSort: ", shellSort, table3);
    testSort("linear insertSort: ", insertSort, table4);
    releaseSortTable(table1);
    releaseSortTable(table2);
    releaseSortTable(table3);
    releaseSortTable(table4);
    return 0;
}
//sortHelp.h
#ifndef SORT_HELPER_H
#define SORT_HELPER_H
#include <stdio.h>
#include <stdlib.h>

typedef int keyType;
typedef struct {
    keyType key;		// 查找表中每个数据元素的关键值
    void *data;			// 数据的其他区域
}Element;

typedef struct {
    Element *data;		// 存放查找表中数据元素的首地址
    int length;			// 查找表的元素个数
}SortTable;
enum sortStatus{success, failed};

void swapElement(Element *a, Element *b);					// 交换元素a和元素b
SortTable *generateRandomArray(int n, int low, int high);	// 产生随机数范围[low,high]
SortTable *generateLinearArray(int n, int swapTimes);		// 参数顺序空间,随机交换swapTimes次
SortTable *copySortTable(SortTable *old);					// 拷贝和old一样值的排序表
void releaseSortTable(SortTable *table);
// 排序算法函数的别名
typedef void (*sortHandler)(SortTable *);
// 测试sortName的排序算法
void testSort(const char *sortName, sortHandler sort, SortTable *table);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "sortHelper.h"
/* 交换a和b的元素值 */
void swapElement(Element *a, Element *b) {
    Element tmp;
    memcpy(&tmp, a, sizeof(Element));
    memcpy(a, b, sizeof(Element));
    memcpy(b, &tmp, sizeof(Element));
}
/* 产生n个随机数的排序表,值的范围是[low, high] */
SortTable *generateRandomArray(int n, int low, int high) {
    SortTable *table = (SortTable *) malloc(sizeof(SortTable));
    if (table == NULL) {
        fprintf(stderr, "sort table malloc failed!\n");
        return NULL;
    }
    table->length = n;
    table->data = (Element *) malloc(sizeof(Element) * n);
    if (table->data == NULL) {
        fprintf(stderr, "element malloc failed!\n");
        free(table);
        return NULL;
    }
    srand(time(NULL) + 1);
    for (int i = 0; i < n; ++i) {
        table->data[i].key = (rand() % (high - low + 1)) + low;
        table->data[i].data = NULL;
    }
    return table;
}
/* 产生n个随机交换swapTimes次的有序顺序表 */
SortTable *generateLinearArray(int n, int swapTimes) {
    SortTable *table = (SortTable *)malloc(sizeof(SortTable));
    if (table == NULL) {
        fprintf(stderr, "sort table malloc failed!\n");
        return NULL;
    }
    table->data = (Element *)malloc(sizeof(Element) * n);
    if (table->data == NULL) {
        fprintf(stderr, "data malloc failed!\n");
        free(table);
        return NULL;
    }
    table->length = n;
    for (int i = 0; i < n; ++i) {
        table->data[i].key = i;
        table->data[i].data = NULL;
    }
    // 在已经有序的排序表中,交换swapTimes次
    srand(time(NULL) + 2);
    for (int i = 0; i < swapTimes; ++i) {
        int pos1 = rand() % n;
        int pos2 = rand() % n;
        swapElement(&table->data[pos1], &table->data[pos2]);
    }
    return table;
}
/* 拷贝一个排序表,使用同样的数据进行不同排序算法的测试 */
SortTable *copySortTable(SortTable *old) {
    SortTable *table = (SortTable *) malloc(sizeof(SortTable));
    table->length = old->length;
    table->data = malloc(sizeof(Element) * old->length);
    for (int i = 0; i < old->length; ++i) {
        table->data[i].key = old->data[i].key;
        table->data[i].data = old->data[i].data;
    }
    return table;
}
/* 释放table */
void releaseSortTable(SortTable *table) {
    if (table) {
        if (table->data) {
            free(table->data);
        }
        free(table);
    }
}
// 检查排序表里的数据,是否是从小到大排序
static enum sortStatus checkData(SortTable *table) {
    for (int i = 0; i < table->length - 1; ++i) {
        if (table->data[i].key > table->data[i + 1].key) {
            printf("Check Sort Data Failed: %d : %d\n", table->data[i].key, table->data[i + 1].key);
            return failed;
        }
    }
    return success;
}
/* 测试sortName的排序算法,算法通过sort传递函数名,数据以table传入 */
void testSort(const char *sortName, sortHandler sort, SortTable *table) {
    clock_t start = clock();
    sort(table);
    clock_t end = clock();
    if (checkData(table) == failed) {
        printf("%s failed!\n", sortName);
        return;
    }
    printf("%s cost time: %fs.\n", sortName, (double) (end - start) / CLOCKS_PER_SEC);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
链表实现直接插入排序是一种常用的排序算法。它可以通过修改链表中的指针来给出排序结果,而不需要移动元素。具体实现步骤如下: 1. 创建一个头结点,作为新链表的起点,并将原链表的第一个结点作为已排序的部分。 2. 从原链表中取下一个结点,将其插入到已排序的链表中的正确位置,保持链表的有序性。 3. 重复步骤2,直到所有结点都被插入到已排序的链表中。 具体的代码实现可以参考引用中给出的代码示例,其中使用了头结点和两个指针(p和q)来实现链表的插入操作。在每次插入结点后,可以通过输出链表的值来验证排序的进程。 需要注意的是,链表实现的直接插入排序算法不改变原链表中元素的位置,而是在链表中给出其在排序表中的位置。这种算法适用于对链表进行排序的场景。 总结起来,链表实现直接插入排序的步骤包括: 1. 创建一个头结点,并将原链表的第一个结点作为已排序的部分。 2. 从原链表中取下一个结点,并将其插入到已排序的链表中的正确位置。 3. 重复步骤2,直到所有结点都被插入到已排序的链表中。 这样就可以利用链表实现直接插入排序算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [基于双链表的直接插入排序、直接选择排序、冒泡排序](https://download.csdn.net/download/m0_62171658/86731089)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [直接插入排序算法之表插入排序详解](https://blog.csdn.net/sunnyoldman001/article/details/127080111)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [链表直接插入排序](https://blog.csdn.net/qq_41027398/article/details/122275622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值