C语言分析基础排序算法——插入排序

目录

插入排序

直接插入排序

希尔排序

希尔排序基本思路解析

希尔排序优化思路解析

完整希尔排序文件


插入排序

直接插入排序

所谓直接插入排序,即每插入一个数据和之前的数据进行大小比较,如果较大放置在后面,较小放置在前面,具体思路分析如下:

//以下面的数组为例
int data[] = { 23,34,84,99,67,26,21,89 };

参考代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

void DirectInsert_sort(int* data, int sz)
{
    // 遍历数组
    for (int i = 1; i < sz; i++)
    {
        int tmp = data[i];//获取数组的当前元素
        int end = i - 1;//获取数组的当前元素的上一个元素
        //当遇到比tmp大数值时,挪动数据,直到遇到比当前数据小的数据时跳出循环
        while (end >= 0)
        {
            if (data[end] > tmp)
            {
                data[end + 1] = data[end];
                end--;
            }
            else
            {
                break;
            }
        }
        //在end的后一个位置插入数据,插入完毕后直接更新i和end继续比较
        data[end + 1] = tmp;
    }
}

int main()
{
    int data[] = { 23,34,84,99,67,26,21,89 };
    int sz = sizeof(data) / sizeof(int);
    DirectInsert_sort(data, sz);
    //打印排序后的数组
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", data[i]);
    }

    return 0;
}
输出结果:
21 23 26 34 67 84 89 99

通过分析可以得出直接插入排序在最坏的情况下(数据为完全逆序的状态)时间复杂度为O(N2),而在最好的情况下(已经为有序状态,只需要遍历一遍数据即可),时间复杂度为O(N)

希尔排序

希尔排序基本思路解析

希尔排序的基本思路是将一组数据按照间隔值gap分成gap组,对各组进行插入排序,这一步被称作预排序,接着再使用直接插入排序对整体再进行一次排序,具体过程如下

//希尔排序基础思路
void ShellSort(int* data, int sz)
{
    int gap = 3;
    //首先进行三组预排序
    for (int i = 0; i < gap; i++)
    {
        //每一组的排序
        for (int j = gap + i; j < sz; j += gap)
        {
            int end = j - gap;
            int tmp = data[j];
            while (end >= 0)
            {
                if (data[end] > tmp)
                {
                    data[end + gap] = data[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            data[end + gap] = tmp;
        }
    }

    //最后进行整体插入排序
    gap = 1;
    for (int i = gap; i < sz; i += gap)
    {
        int end = i - 1;
        int tmp = data[i];
        while (end >= 0)
        {
            if (data[end] > tmp)
            {
                data[end + gap] = data[end];
                end--;
            }
            else
            {
                break;
            }
        }
        data[end + gap] = tmp;
    }
}

希尔排序优化思路解析

以上思路只是简单的分析,接下来对细节进行分析优化,具体思路如下

首先是间隔值gapgap决定了分组的数量,也间接决定了最大值走到最后需要的次数,当gap特别大时,最大值很快就会到数据的最末尾,但是同时整体越不接近需要的升序;相反,当gap特别小时,最大值到数据末尾的次数变多,同时整体越接近有序,所以gap数值不容易确定,但是一般取gap为整体的1/3或者取1/2,即gap = size / 3gap = size / 2 (size是数组长度)

第二个问题,如果将预排序和最后的插入排序分开写,那么需要写五个循环,三层循环解决预排序,两层循环解决最后的插入排序,所以可以考虑将预排序与直接插入排序放在一起,达到在同一个循环中解决问题

可以考虑将每一次循环变量移动的位置改为移动一位,代替原来的一次移动gap位,如下图所示

而改进后的思路与原来的思路对比:原来的思路是先排一组,排完一组后再排第二组,而改进后的思路是遇到i当前位置是哪一组的就排哪一组的数据,但是需要注意的是,当前的tmp所在位置为下一个相差gap的位置,该位置需要有确切的数值可以与end进行比较,所以tmp不能越界,假设当前tmp为3,数组最大下标为7,也就是tmp所在的下标最大只能为7,即i + gap不能超过7,故i最大只能为4,所以i不能超过5

接下来是如何处理预排序和之后的直接排序放在一起的问题,对于这个问题,首先就是gap不能为一个固定值,如果gap为固定的某一个数值,例如3,那么预排序和直接插入排序还是需要两组循环才能解决,鉴于gap最后需要回到1,可以考虑从gap/3分组开始,当预排序完gap为3的一组时,接下来排gap为2的一组,最后着排gap为1的一组,因为第一次gap为3时已经将数据处理了一遍,而gap数值越小,就会使预排序的结果更接近预期的排序结果,所以可以考虑gap = gap / 3,每执行完一次预排序就更换一次gap间隔值,从而达到预排序与最后的直接插入排序放在一起,因为当gap为1时也可以满足预排序的思路,故放在一起也不会有任何冲突,只是间隔值从3变成了1,但是需要注意一个问题,当gap小于3时,gap最后的商为0,导致gap无法取到1,所以需要写成gap = gap / 3 + 1

//希尔排序优化思路
void ShellSort_modified(int* data, int sz)
{
    int gap = sz;
    while (gap > 1)
    {
        gap = gap / 3 + 1;// 此处加1是为了确保最后一个gap一定为1,因为最后的直接插入排序是整体各自比较
        for (int i = 0; i < sz - gap; i++)
        {
            int end = i;
            int tmp = data[i + gap];
            while (end >= 0)
            {
                if (data[end] > tmp)
                {
                    data[end + gap] = data[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            data[end + gap] = tmp;
        }
    }    
}

完整希尔排序文件

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

//希尔排序优化思路
void ShellSort_modified(int* data, int sz)
{
    int gap = sz;
    while (gap > 1)
    {
        gap = gap / 3 + 1;// 此处加1是为了确保最后一个gap一定为1,因为最后的直接插入排序是整体各自比较
        for (int i = 0; i < sz - gap; i++)
        {
            int end = i;
            int tmp = data[i + gap];
            while (end >= 0)
            {
                if (data[end] > tmp)
                {
                    data[end + gap] = data[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            data[end + gap] = tmp;
        }
    }    
}

int main()
{
    int data[] = { 23,34,84,99,67,26,21,89 };
    int sz = sizeof(data) / sizeof(int);
    ShellSort_modified(data, sz);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", data[i]);
    }

    return 0;
}
输出结果:
21 23 26 34 67 84 89 99

希尔排序总结:

  1. 希尔排序是对直接插入排序的优化
  2. gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/441634.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter使用auto_updater实现windows/mac桌面应用版本升级功能

因为windows应用一般大家都是从网上下载的&#xff0c;后期版本肯定会更新&#xff0c;那用flutter开发windows应用&#xff0c;怎么实现应用内版本更新功能了&#xff1f;可以使用auto_updater库&#xff0c; 这个插件允许 Flutter 桌面 应用自动更新自己 (基于 sparkle 和 wi…

生活的色彩--爱摸鱼的美工(17)

题记 生活不如意事十之八九&#xff0c; 恶人成佛只需放下屠刀&#xff0c;善人想要成佛却要经理九九八十一难。而且历经磨难成佛的几率也很小&#xff0c;因为名额有限。 天地不仁以万物为刍狗&#xff01; 小美工记录生活&#xff0c;记录绘画演变过程的一天。 厨房 食…

单调栈(例题+解析)

1、应用场景 找出一个数的左面离概述最近的且小于该数的数&#xff08;同理右面也可以&#xff09; 例如&#xff1a; 数组a[i] 3 4 2 7 5 答案&#xff1a; -1 3 -1 2 2 2、如何实现找到规律 暴力写法&#xff1a; for(int i0;i<n;i) {for(int ji-1;j>0;j--){i…

在ubuntu上使用vscode+gcc-arm-none-eabi+openocd工具开发STM32

文章目录 所需工具安装调试搭建过程中遇到的问题 写在前面 老大上周让我用vscode开发STM32&#xff0c;我爽快的答应了&#xff0c;心想大学四年装了这么多环境了这不简简单单&#xff0c;更何况vscode这两年还用过&#xff0c;然而现实总是令人不快的——我竟然花了差不多两周…

UDP实现文件的发送、UDP实现全双工的聊天、TCP通信协议

我要成为嵌入式高手之3月7日Linux高编第十七天&#xff01;&#xff01; ———————————————————————————— 回顾 重要程序 1、UDP实现文件的发送 发端&#xff1a; #include "head.h"int main(void) {int sockfd 0;struct sockaddr_i…

PHP在线图像处理程序:基于Photoshop的网页版图片处理源码

PHP在线PS修图网页版源码&#xff1a;实现照片图片处理的便捷工具 众所周知&#xff0c;许多朋友都喜欢使用PS进行图像编辑。然而&#xff0c;PS需要下载软件并对电脑配置要求较高。今天我们为大家带来一款基于浏览器的在线PS网页版源码&#xff0c;让您轻松实现在线P图和作图…

Kafka MQ 主题和分区

Kafka MQ 主题和分区 Kafka 的消息通过 主题 进行分类。主题就好比数据库的表&#xff0c;或者文件系统里的文件夹。主题可以被分为若干个 分区 &#xff0c;一个分区就是一个提交日志。消息以追加的方式写入分区&#xff0c;然 后以先入先出的顺序读取。要注意&#xff0c;由…

【论文阅读】Mamba:选择状态空间模型的线性时间序列建模(一)

文章目录 Mamba:选择状态空间模型的线性时间序列建模介绍状态序列模型选择性状态空间模型动机&#xff1a;选择作为一种压缩手段用选择性提升SSM 选择性SSM的高效实现先前模型的动机选择扫描总览&#xff1a;硬件感知状态扩展 Mamba论文 Mamba:选择状态空间模型的线性时间序列建…

软件测试入门

文章目录 一、入门1. 软件2. 软件基本组成3. 软件产生过程4. 软件测试5. 软件测试目的&#x1f3c6; 小结 二、测试主流技能1. 功能测试2. 自动化测试3. 接口测试4. 性能测试&#x1f3c6; 小结 三、测试分类1. 按测试阶段划分2. 按代码可见度划分&#x1f3c6; 小结 三、质量模…

数字人ai直播软件突破AI大模型技术,改变未来科技格局!

数字人AI直播软件在AI大模型技术上的突破&#xff0c;将不可避免地改变未来科技格局。这一突破让人们看到了AI技术的无限可能性&#xff0c;并为未来的科技发展打开了新的大门。 AI大模型技术是近年来人工智能领域的一个热点&#xff0c;它通过构建庞大、复杂的神经网络模型&a…

bug - poi getMergedRegion合并后的行列number错误

第一个CellRangeAddress 的Row number 应该是0&#xff0c;但是给出的是1。 其它的CellRangeAddress 与实际大致相差4-5不等&#xff0c;没有规律。 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>…

气象数据免费下载(超级好用)

你是不是做实验经常性的需要一些气象数据&#xff0c;例如PM2.5、相对湿度、月均温度等等…… 但是当你开始寻找数据时就遇到困难了&#xff0c;由于权限、数据网站之类的麻烦你会花费大量无用时间&#xff0c;甚至有时候一无所获得不偿失&#xff0c;这就很头疼了&#xff01;…

一篇了解电感的使用

一、电感理论基础 1.电感的定义 当电流通过线圈后&#xff0c;会产生磁场&#xff0c;磁感线穿过线圈&#xff0c;产生的磁通量与电流 i有如下关系&#xff1a; 将漆包线、纱包线或塑皮线等在绝缘骨架或磁心、铁心上绕制而成的器件&#xff0c;当线圈通过电流后&#xff0c;在…

工地安全反光衣穿戴监测报警摄像机

工地安全反光衣穿戴监测报警摄像机是为了提高工地施工人员的安全意识和监管效率而设计的。这种设备结合了反光衣、监测系统和报警摄像机的功能&#xff0c;可以有效减少工地事故的发生。 首先&#xff0c;工地安全反光衣是一种具有高度可见度的服装&#xff0c;能够使穿戴者在夜…

Unity中PICO实现移动交互

文章目录 前言一、在允许行走的地面加上对应的组件1、Teleportation Anchor 移动锚点2、Teleportation Area 移动区域 二、在 玩家&#xff08;需要移动的对象&#xff09;上挂载对应组件1、Teleportation Provider 被移动对象2、在 Teleportation Anchor 或 Teleportation Are…

一文学会搭建 cli 脚手架工具

文章目录 设置工具命令package.json bin 字段注释&#xff1a;#!/usr/bin/env node设置环境变量 接收命令选项参数process 实现commander 命令行交互&#xff1a;inquirer下载项目模板&#xff1a;download-git-repo执行额外命令&#xff1a;自动安装依赖child_processexeca 体…

FineReport决策报表Excel导出数据不全解决办法

一、首先建立决策报表 决策报表不带参数导出办法&#xff08;即没有参数面板&#xff09; 普通决策报表导出&#xff08;没有搜索面板&#xff09; 如果决策报表带参数&#xff08;即有搜索框&#xff09;&#xff0c;用上面的办法只能导出部分数据&#xff0c;数据不全 二、…

Go语言框架路由Controller控制器设计思路gin路由根据控制器目录分层生成路由地址

Controller设计好处 框架设计用controller分请求路由层级&#xff0c;应用从app目录开始对应请求url路由地址&#xff0c;这样设计师方便开发时候通过请求地址层级快速定位接口方法对应的代码位置。 例如api接口请求路径为&#xff1a;​​http://localhost:8110/​​busines…

C.C语言分支和循环语句

文章目录 一. 什么是语句 二. 分支语句&#xff08;选择结构&#xff09; 2.1. if 语句 2.1.1. 语法结构 2.1.2. 悬空else 2.1.3. 书写形式的对比 2.1.4. 练习 2.2. switch 语句 3.2.1. 语法结构 3.2.2. 在switch语句中的 break 3.2.3. default子句 3.2.4. 练习 三…

打造你的贪吃蛇游戏:HTML、CSS与JavaScript的完美结合

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…