插入排序增强版

插入排序算法C语言程序.zip 插入排序算法C语言程序,算法思路:先对数组前两个数进行大小比较,将第三个数与前两个数比较,插入合适位置,再将第四个数与前三个数比较并插入,以此类推 立即下载

排序算法按照其稳定性来说,分为非稳定性排序与稳定性排序算法。判断稳定性与非稳定性算法的依据一般在是否为相邻元素之间的数据交换,不然一般为非稳定排序。比如快速排序,Shell排序等都不是稳定排序,而像冒泡排序,插入排序,合并排序等都是稳定排序。C语言库函数中的qsort函数是基于快速排序的,因此排序之后,未参加排序的数据之间的顺序可能被打乱。有时我们又需要在某一类场景下利用到稳定排序算法。而C++ STL算法库中也对应着这两种算法接口函数: sort与stable_sort,而stable_sort就是稳定排序算法。当然稳定与否还是与具体实现相关,如果算法在交换两个元素时,用的是严格意义上的比较运算,则可能就是稳定的,如果不是严格的(比如>=),那即使用稳定的算法,也可能排序后的相对元素位置也发生改变了。下面我们研究下稳定的插入排序算法实现,原理就不用多说了,算法流程类似与我们玩纸牌手法,插入新元素之前,原来的序列就是已经排序好的,这样从时间复杂度来说,和冒泡排序一样,都是O(n^2)。以下是简单的实现源码:

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

void insertSort(int a[], int len) //插入排序
{
    if (len < 2) // 如果元素个数小于2,直接返回
    {
        return;
    }
    for (int i = 1; i < len; i++)
    {
        int temp = a[i]; //先将待交换的元素保存起来
        int j = i - 1;
        while (j >= 0 && a[j] > temp) //按照升序排列,如果指定排序元素之前的元素比待排序元素大
        {	                          //,那么从此元素开始到开头,都向后移动一位
            a[j +1] = a[j];
            j--;
        }
        a[j+1] = temp;  //将交换的数据插入到合适位置
    }
}

#define SIZE  sizeof(a)/sizeof(a[0])

int main()
{
    int a[10] = {0};

    srand(time(NULL));

    for (int i = 0; i < SIZE; i++)//随机生成10个数字
    {
        a[i] = rand() % 100;
    }

    for (int i = 0; i < SIZE; i++)//打印排序之前的数字
    {
        printf("%d ", a[i]);
    }
    printf("\n");
    insertSort(a, SIZE);

    for (int i = 0; i < SIZE; i++)//打印排序之后的数字
    {
        printf("%d ", a[i]);
    }

    return 0;
} 

上面只是对整数进行排序,那么能不能对一般数据类型(主要指的是结构体类型)进行排序呢? 答案是肯定的。我们先看看库函数中qsort的函数接口。实际上qsort一般对连续内存的数组进行排序的,qsort一共有4个参数,第1个参数是数组基址,第2个参数是元素个数,第3个参数是每个元素大小,第4个参数是提供给调用者的回调函数,用来改变排序规则用的。我们也可以提供类似的接口,我们这里交换元素的概念变为交换两块内存的内容,本质上和一般的插入排序算法一样,一般插入排序本质上也是交换4个字节的内存数据。于是我们得到如下增强版的插入排序算法:

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

void insertSort(void *base , size_t num, size_t size, int(*cmp_func)(const void*, const void *) ) //插入排序
{
    if (num < 2 || size < 1) // 如果元素个数小于2,直接返回
    {
        return;
    }
    void *pTemp = (void*)malloc(size); //不判定为空指针
    memset(pTemp, 0x0, size);
    for (size_t i = 1; i < num; i++)
    {
        memcpy(pTemp, (char*)base + i * size, size);//先将待交换的元素保存起来
        int j = i - 1; //防止无符号溢出,此处用有符号数
        while (j >= 0 && cmp_func((char*)base + j * size, pTemp) > 0) //按照升序排列,如果指定排序元素之前的元素比待排序元素大
        {	                          //,那么从此元素开始到开头,都向后移动一位
            memcpy((char*)base + (j+1)*size, (char*)base + j * size, size);
            j--;
        }
        memcpy((char*)base + (j+1)*size, pTemp, size);  //将交换的数据插入到合适位置
    }

    free(pTemp);

}

#define SIZE  sizeof(a)/sizeof(a[0])

int cmp_func(const void *a, const void *b)
{
    return *(int*)a - *(int*)b;
}

int main()
{
    int a[10] = {0};

    srand(time(NULL));

    for (unsigned int i = 0; i < SIZE; i++)//随机生成10个数字
    {
        a[i] = rand() % 100;
    }

    for (unsigned int i = 0; i < SIZE; i++)//打印排序之前的数字
    {
        printf("%d ", a[i]);
    }
    printf("\n");

    insertSort(a, SIZE, sizeof(int), cmp_func);

    for (unsigned int i = 0; i < SIZE; i++)//打印排序之后的数字
    {
        printf("%d ", a[i]);
    }

    return 0;
}

假设现在有一个需求,班级一共有10名学生,学生信息包括学生id号,学生姓名及考试总成绩(100分制算)。现在需要对学生信息进行排名:学生成绩按照从高分往低分排序,如果两人成绩一样,则按照学生的姓名字典序排序。

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

typedef struct
{
	int id; //学生ID
	char *name; //学生姓名
	int score; //分数
}student_t;

void insertSort(void *base , size_t num, size_t size, int(*cmp_func)(const void*, const void *) ) //插入排序
{
    if (num < 2 || size < 1) // 如果元素个数小于2,直接返回
    {
        return;
    }
    void *pTemp = (void*)malloc(size); //不判定为空指针
    memset(pTemp, 0x0, size);
    for (size_t i = 1; i < num; i++)
    {
        memcpy(pTemp, (char*)base + i * size, size);//先将待交换的元素保存起来
        int j = i - 1;
        while (j >= 0 && cmp_func((char*)base + j * size, pTemp) > 0) //按照升序排列,如果指定排序元素之前的元素比待排序元素大
        {	                          //,那么从此元素开始到开头,都向后移动一位
            memcpy((char*)base + (j+1)*size, (char*)base + j * size, size);
            j--;
        }
        memcpy((char*)base + (j+1)*size, pTemp, size);  //将交换的数据插入到合适位置
    }

    free(pTemp);

}

student_t student[] =
{
		{320991, "zhangsan", 65},
		{543321, "lisi", 78},
		{333453, "xiaoqi", 79},
		{456566, "hangang", 77},
		{354444, "zhaolei", 91},
		{456566, "hangang", 87},
		{242333, "sunjunior", 99},
		{424444, "wangyong", 88},
		{445444, "santian", 73},
		{445444, "qidong", 91}
};

#define g_student_num sizeof(student)/sizeof(student[0])

int cmp_func(const void *a, const void *b)
{
	student_t *pa = (student_t*)a;
	student_t *pb = (student_t*)b;
	if (pa->score != pb->score)
	{
		return pb->score - pa->score;
	}
	else
	{
		return strcmp(pa->name, pb->name);
	}
}

int main()
{
    for (unsigned int i = 0; i < g_student_num; i++)//打印排序之前的数字
    {
        printf("%d:%s:%d\n", student[i].id, student[i].name, student[i].score);
    }
    printf("*********************************\n");

    insertSort(student, g_student_num, sizeof(student_t), cmp_func);

    for (unsigned int i = 0; i < g_student_num; i++)//打印排序之后的数字
    {
        printf("%d:%s:%d\n", student[i].id, student[i].name, student[i].score);
    }

    return 0;
}


最后再验证下此增强版排序算法的稳定性,即如果数据元素不发生交换的话,排序之前与之后的相对位置不变。现在对上面学生学号分成10类,以学号最后一位作为分类号,如果学号最后一位数字相同,则归为一类,那排序后的情况如何呢?我们只需在上面源码下修改下比较函数即可,其他不变

 

int cmp_func(const void *a, const void *b)
{
	student_t *pa = (student_t*)a;
	student_t *pb = (student_t*)b;
	return pa->id % 10 - pb->id % 10;
}


从上图我们可以看到,排序之前与之后,元素的相对位置还是维持原样,从而证明了增强板插入排序算法是稳定的。

同样,我们将此排序算法运用链表来实现,也可以同样实现其要求,下面是用单链表来实现排序的功能:

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

typedef struct ListNode
{
    void *data;
    struct ListNode *next;

}SList;

SList *insertSort(SList *head, int (*cmp_func)(const void *a, const void *b))
{
    if (head == NULL || head->next == NULL)
    {
        return head;
    }

    SList *newHead = NULL; //原链表剩下的用于直接插入的结点头指针
    SList *pNode = NULL; //待插入结点
    SList *pre = NULL; //前驱指针变量
    SList *cur = NULL; //指向排序好的链表当前指针变量

    newHead = head->next;
    head->next = NULL;
    while (newHead != NULL)
    {
        pNode = newHead;   //待插入结点
        cur = head;   //已经排序好的结点
        while (cur != NULL && (cmp_func(cur->data, pNode->data) < 0))
        {
            pre = cur;
            cur = cur->next;
        }
        //退出循环,找到插入位置
        newHead = newHead->next;
        if (cur == head)
        {
            head = pNode;
        }
        else
        {
            pre->next = pNode;
        }

        pNode->next = cur; //完成插入动作
    }

    return head;
}


typedef struct
{
    int id; //student id
    const char *name; //student name
    int score; //student score
}student_t;

student_t student[] =
{
        {320991, "zhangsan", 65},
        {543321, "lisi", 78},
        {333453, "xiaoqi", 79},
        {456566, "hangang", 77},
        {354444, "zhaolei", 91},
        {456566, "hangang", 87},
        {242333, "sunjunior", 99},
        {424444, "wangyong", 88},
        {445444, "santian", 73},
        {445447, "qidong", 91}
};

int g_student_num = sizeof(student) / sizeof(student[0]);

int cmp_func(const void *a, const void *b)
{
    student_t *pa = (student_t*)a;
    student_t *pb = (student_t*)b;
    if (pa->score != pb->score)
    {
        return pb->score - pa->score;
    }
    else
    {
        return strcmp(pa->name, pb->name);
    }
}


SList *createList(void)
{
    SList *pHead = NULL;
    for (int i = 0; i < g_student_num; i++)
    {
        SList *pTemp = (SList*)malloc(sizeof(SList));
        student_t *pt = (student_t*)malloc(sizeof(student_t));
        pt->id = student[i].id;
        pt->name = student[i].name;
        pt->score = student[i].score;
        pTemp->data = (void*)pt;
        pTemp->next = pHead;
        pHead = pTemp;
    }

    return pHead;
}



void traverseList(SList *pList)
{
    while (pList != NULL)
    {
        student_t *pt = (student_t*)pList->data;
        printf("%d:%s:%d\n", pt->id, pt->name, pt->score);
        pList = pList->next;
    }
    printf("****************\n");
}

int main()
{
    SList *pHead = createList();
    traverseList(pHead);
    pHead = insertSort(pHead, cmp_func);
    traverseList(pHead);



    return 0;
}

从上面排序之后的结果可以看出,跟预期结果是一致的。

macOS 发票查验全攻略:从证书导入到Safari调试 本文详细介绍了在macOS上进行发票查验的全流程,包括证书下载、钥匙串访问导入、Safari浏览器调试等关键步骤。重点解决了验证码不显示等常见问题,提供了证书信任设置和开发者工具使用的实用技巧,帮助用户顺利完成税务发票在线查验。 阅读详情

相关推荐

MAX96724解串器拨码配置实战:从COAX/GMSL2模式到IIC地址设定

本文详细解析了MAX96724解串器的拨码配置方法,涵盖电源管理模式、I2C地址设定及COAX/GMSL2视频传输模式。通过实战案例和故障排查技巧,帮助工程师快速掌握xcDeserializer4.0版本的配置要点,避免常见错误,提升车载摄像头和工业视觉系统的调试效率。

weixin_29243279的博客 576

希尔(Shell) 排序 - 增强版插入排序算法

前面讲过的几种排序多是以排序逻辑来命名的,例如冒泡,选择和插入排序,以及其他如归并排序,当然还有觉得自己足够牛 X 快速排序命名。而本文要学习的排序算法叫做希尔排序是以其设计者 Donlad Shell 命令的排序算法,该算法在 1959 年公布,能以作者来命名的算法应该是很不错的,令设计者引以为傲的。最初写出冒泡和选择排序的就没以作者来命名,可能不好意说,更可能是公共思维。 那么什么是希尔排序...

一叶不知秋 268

开源网格划分软件-Gmsh二次开发教程 (4)网格生成与控制​

​本章将深入讲解Gmsh的网格生成技术,包括全局/局部网格控制、边界层生成、自适应细化等,并提供完整的代码示例。

ZQGG的博客 1473

增强版插入排序--二分的结合

public class BinaryInsertSort { public static void main(String[] args) { int[] arr = {5,2,4,6,1,3}; arr = BinaryInsertSortFun(arr); System.out.println(Arrays.toString(ar

带翅膀的猫的博客 3767

信号完整性分析学习--16--传输线效应

传输线效应 说到传输线效应,大多数人都会想到一个经验法则,那就是: “当互连线的长度大于1/6倍的信号上升时间的空间延伸时,互连线就体现出传输线效应。” 上升时间越小越容易体现传输线效应。 由此可见,同一根互连线,它是否体现传输线效应,要看它要传输什么样的信号。下图所示,一根延时为15ps的互连线,当它传输上升时间为60ps的信号时就会体现传输线效应;当它传输上升时间为180ps的信号时就不体现传输线效应。

qq_29476769的博客 7274

告别配置噩梦:用Vcpkg三行命令为Easy3D自动安装Boost、CGAL和Qt(VS2022实测)

本文详细介绍了如何使用Vcpkg工具在VS2022环境下为Easy3D项目一键安装Boost、CGAL和Qt等依赖库,解决传统手动配置的复杂性问题。通过三行简单命令即可自动完成下载、编译和路径配置,大幅提升开发效率,特别适合C++开发者快速搭建3D图形开发环境。

weixin_42525582的博客 210

排序算法之插入排序与希尔排序

插入排序 插入排序的核心思想为: 将无序序列插入到有序序列之中 思路 1.给定一个数组,默认将数组的第一个元素作为有序数组,将其他元素作为无序数组,并将无序数组的第一个元素用临时变量保存下来 2.从数组的第二个的元素(即 无序数组的第一个元素 )开始遍历,循环的与有序数组进行比较,根据大小插入到有序数组的相关位置 举例:给定无序数组 [ 4 3 6 2 1 9 8 7 ] 相关过程如图所示: 代...

weixin_45801057的博客 196

Prokka实战:10分钟搞定细菌基因组注释(附详细参数解析)

本文深入解析Prokka细菌基因组注释工具的核心参数与实战策略。通过拆解其集成化工作流程,详细解读物种分类、功能注释、输出控制等关键参数,并提供快速初筛、高质量发表、质粒注释及批量处理四种典型场景的高效参数组合方案,帮助用户精准掌控注释过程,将原始序列转化为有价值的生物学见解。

weixin_29271053的博客 442

C++ 8大排序算法 完美注释 算法介绍 让你醍醐灌顶

------------------------------------------------------////////////////////////////////////////////////////// ///> Author: BloodyYoLmer ///> Date: 2013/5/25 ///> description: SortAlgorithm //////

XueXingYouLeMei的专栏 3817

TC3xx系列LMU与SPR实战:将关键算法从Flash搬移至PSPR提升5倍执行效率

在实际电机控制项目中,通过将FOC算法全部迁移到PSPR,我们不仅将执行时间从56μs降至11μs,还将最坏情况下的时间偏差控制在±0.3μs以内。实测数据:在TC397上执行Flash中的32位浮点矩阵乘法,平均每条指令需要8.7个周期,而同样的代码在PSPR中仅需1.2个周期。传统开发中,工程师往往将所有代码放在Flash中运行,这相当于让F1赛车在乡村公路上行驶。迁移后不仅平均执行时间缩短,更重要的是时间抖动从±15%降低到±2%以内,这对闭环控制系统至关重要。不是所有代码都适合迁移到PSPR。

weixin_31421613的博客 297

SAP 消息号T-CODE

SAP消息号修改汇总 确实有很多消息是可以修改的 FI: oBA5|oFMG|oFPM MM: oMRM|oKZZ|oMT4|oMCQ|o04C| SD: oVAH(V4)|oVM1(VL) CO:  oPR4_ACT|oPR4_CK|oPR4_CKML|oPR4_CKPF|oPR4_KKA|oPR4_KKP|oPR4_KKS| oPR4_KKS1|oPR4_P

heng0757的专栏 1万+

8大排序之---(2)希尔排序

希尔排序  (一)基本思想: 希尔排序:     其实就是加强版的插入排序。首先把待排序的数据根据增量分成几个子序列,对子序列进行插入排序,                         直到增量为1,直接进行插入排序;                        增量 的选择:一般为数组长度的二分之一,再变为原来增量的二分之一....直到增量为1 比如数据:int a []

Yabber0914的博客 696

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

day02-常用算法-排序

冒泡排序 思路: 相邻的两个数进行比较,会进行n-1轮比较,每一轮会比较N-1次之后,将数组中最大的数排在最后 注意:每一次比较之后会交换变量值package homework;public class a02 { public static void main(String[] args) { int[] nums = new int[] { 0, 13, 94, 13

那就不能叫远方 319

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

第15周项目1 -(2)验证直接插入排序

问题及代码:/* * Copyright (c)2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名称:1.cpp * 作 者:王修文 * 完成日期:2016年12月8日 * 版 本 号:v1.0 *问题描述:验证直接插入排序,完成测试。 *输入描述:无 *程序输出:测试数据 */ 1、直接插入

WANGXIUWEN12138的博客 375

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【数据结构与算法】死磕排序算法:面试官最爱问的那些排序(上篇)

在平时的开发以及面试中,排序算法绝对是绕不开的一座大山。很多同学在看面经的时候,一遇到手撕排序就头大。别怕!这个系列将为你把那些高频排序算法掰开了、揉碎了讲解。

2301_79751930的博客 371

排序算法——希尔排序

排序算法——希尔排序希尔排序又叫缩减增量排序,是冒泡和插入排序的增强版,也是第一批冲破二次时间屏障的算法之一。它的基本思想是将数据按步长分组,对每组记录采用直接插入排序的方法进行排序,步长根据算法的进行逐渐减小,直到最后步长为1,比较相邻元素的最后一趟排序后,完成排序。 //希尔排序 根据步长进行比较 public static <T extends Comparable<T>> T[

QuestCh的博客 492

插入排序和希尔排序

今天给小伙伴们分享两个比较基础的排序算法,插入排序和希尔排序

Code_Allen的博客 1318

内部排序法的代码实现

内部排序法的代码实现冒泡排序冒泡排序增强堆积排序法插入排序法快速排序法基数排序法选择排序法希尔排序法 冒泡排序 * 冒泡排序需要比较 n(n-1)/2次,时间复杂度O(n^2) * 稳定排序 * 只需额外一个空间,空间复杂度最佳 * 适用于数据量小或有部分数据已经排过序的情况 * * 不管数据是否已经完成排序都要执行 n(n-1)/2次 public class BubbleSort { public static void main(S

北笙LBJ的博客 282

【数据结构】-java实现希尔排序算法(五)

希尔排序算法的算法思想、动态演示、C++、python实现可以参考希尔排序算法详解 希尔排序是一种插入类排序。直接插入的加强版。 算法思想:参见希尔排序算法详解 算法的性能分析 希尔排序中相等数据可能会交换位置,所以希尔排序是不稳定的算法。 java实现希尔排序算法 public static int[] ShellSort(int[] arr){ int n=arr.leng...

小呼哥的博客 232

Java-冒泡排序、插入排序、简单选择排序

public class SortStudy { public static void main(String[] args) { } /** * 冒泡排序 * 比较相邻的元素,如果第一个比第二个大,就交换他们两个,把大的数沉下去,小数冒上来 * 依次比较直到完成 */ public static void bullesort() { int[] array =...

浣熊不是小熊猫 278

数据结构练习---排序

排序算法 对于排序算法我个人将排序分为三大类 算法排序 结构排序 特殊排序 对于算法排序分为:简单排序(冒泡排序、选择排序、插入排序)复杂排序(希尔排序、归并排序、快速排序) 对于结构排序分为:堆排序和树排序 对于特殊排序分为:基数排序、计数排序、桶排序 public class sortTest { /** * 冒泡排序 * @param A * @param n */ public void BubbleSort(int A[],int n)

qq_43285863的博客 160

scratch之十大经典排序算法-插入排序

十大经典排序算法简介0、算法概述0.1 算法分类十种常见排序算法可以分为两大类:比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比...

IT_Scratch的博客 2524

Java数据结构中的常见排序算法

简单排序(冒泡、选择、插入):时间复杂度为$O(n^2)$,适合小型数据集或教育目的,但效率较低。高效排序(快速、归并):时间复杂度为$O(n \log n)$,适合大型数据集。快速排序在平均情况下更快,但最坏情况性能差;归并排序稳定且始终高效,但需要额外空间。在实际Java应用中,方法通常使用优化后的快速排序或归并排序(例如,针对对象数组使用归并排序)。选择算法时,需考虑数据规模、是否要求稳定排序(如插入排序和归并排序是稳定的),以及内存限制。

2301_80357895的博客 221

go语言:实现GnomeSort侏儒排序算法(附带源码)

go语言:实现GnomeSort侏儒排序算法(附带源码)

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 155

插入排序(2)

public class Demo02 { /** * @param args * 插入排序 */ public static void main(String[] args) { //定义数组 int arr[] = {3,2,5,6,1,8}; //轮数 for(int i =1; i<arr.length;i++){ for(int j=i;j>0;j

llixiangjian的博客 326

直接插入排序、折半插入排序、希尔排序、冒泡排序、快速排序、选择排序、归并排序--源码

直接上源码,有问题请留言: public class Main { public static void main(String[] args) { int[] a={20,30,21,35,2,1}; insert6(a); for(int i=0;i<a.length;i++){ System.out.pr...

weixin_42706227的博客 1044
上一篇: Hash函数经典用法
下一篇: 堆排序算法原理及实现
CodingGeek
博客等级 码龄18年 15粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值