算法——排序

排序

        下面的代码会用到宏定义,因为再C中没有swap交换函数,所以对于swap的宏定义代码如下:

#define swap(a, b) {\
    __typeof(a) __a = a; a = b; b = __a;\
}

        

        稳定排序:

         1.插入排序:

        插入排序会将数组,分位两个部分,一般分为前后两部分,而前半部分为已排序区,后半部分为未排序区;插入排序的操作就是把,未排序区中的元素插入到已排序区中的去,并且满足排序区的单调性;如图下面的操作,实现一个单调递增序列:

        数组的原本样子,现在使位置0是已排序区,先去从位置1开始去插入;

        将12插入到23前面,使位置0,1形成单调递增;

        进行插入,对位置2插入,发现不用插入,直接对下一个位置进行插入:

        位置4也不用进行插入保持原位置,对位置5进行插入:

        最后完成排序;

        时间复杂度:O(n ^2)

        代码实现:

void insert_sort(int *arr, int n) {//arr排序数组,n数组长度
    for (int i = 1; i < n; i++) {//位置0开始定为已排序区
        for (int j = i; j >= 1 && arr[j] < arr[j - 1]; j--) {//将位置i进行插入到前面的以排序区
            swap(arr[j], arr[j - 1]);//交换位置
        }
    }
    return ;
}

      2.冒泡排序:

        冒泡排序,为什么会叫冒泡排序,假如实现单调递增序列,那么每一次都会将未排序中的最大的元素放到未排序区中的最后去,把数组立起来数组的最后的位置在上,那么是不是每次未排序的最大元素会像冒泡一样往上升,所以叫冒泡排序;

        如图数组最开始状态:

        第一次冒泡:

        第一次完冒泡后,最大的元素已经放到了数组最后位置,也相当于放到了已排序区中了;然后这样一直循环直到排完序;

        时间复杂度:O(n^2)

        代码实现:

        做了一个小小的优化;

void bubble_sort(int *arr, int n) {
    int time = 1;//用来标记这次循环是否发生了冒泡交换
    for (int i = 1; i < n && time; i++) {//如果没有发生交换说明数组已经排好了序
         time = 0;
        for (int j = 0; j < n - i; j++) {
            if (arr[j] >= arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                time++;
            }
        }
    }
    return ;
}

        3.归并排序:

       将一个数组从中间分开,然后通过递归一直将子数组进行分开,直到数组只有两个元素,然后通过回溯的过程中进行排序,然后一直回溯到整个数组并拢,完成排序;

        如图,将数组这样二分下去:

        然后从下往上进行排序,单调递增:

        合并,排序:

        

        合并,在排序:

        

        最终完成排序;

        时间复杂度:O(nlogn)

        代码实现:

        这个过程比较容易理解,就是代码实现有那么一点复杂,来看代码:

        

void merge_sort(int *arr, int l, int r) {//数组的头位置,r数组的末尾在
    if (r - l <= 1) {//当分到只有2个元素和1个元素时,递归出口
        if (r - l == 1 && arr[l] > arr[r]) {//两个元素,进行排序
            swap(arr[l], arr[r]);
        }
        return ;
    }
    int mid = (l + r) >> 1;
    //开始分列
    merge_sort(arr, l, mid);//递归左边数组
    merge_sort(arr, mid + 1, r);//递归右边数组
    int *temp = (int *)malloc(sizeof(int) * (r - l + 1));//创建一个空间,来存子数组的元素
    int p1 = l, p2 = mid + 1, k = 0;//p1数组分裂开的前部分的起始坐标,p2数组分裂开后半部分的起始坐标
    while (p1 <= mid || p2 <= r) {
        if (p2 > r || (p1 <= mid && arr[p1] < arr[p2])) temp[k++] = arr[p1++];
        else temp[k++] = arr[p2++];
    }
    memcpy(arr + l, temp, sizeof(int) * (r - l + 1));//将排好序的子数组拷贝给原数组
    free(temp);
    return ;
}

        4.基数排序:

        这里假设数组都是两位数,先对数组进行元素的个位进行排序,然后在对数组的十位进行排序,这样就能对数组拍好序;如果位数不相同,取位数最大的数进行位数排序假如最大的位数是3位,那么就进行3次位数排序,如果位数10位那就进行10次位数排序;

        如图数组最初:

        进行个位排序,上面的表格就是对于个位数的统计,对于排序时会起到作用:     

  

        在进行10位排序:

        最终完成排序:

        时间复杂度:O(n)

        代码实现:

        

void number_sort(int *arr, int n, int exp) {
    int count[10] = {0};
    for (int i = 0; i < n; i++) {
        count[arr[i] / exp % 10] += 1;//对每位数的数的个数统计
    }
    for (int i = 1; i < 10; i++) {
        count[i] += count[i - 1];//位数排序,从小到大,现在的操作就是使count变为下标
    }
    int *sum = (int *)malloc(sizeof(int) * n);
    for (int i = n - 1; i >= 0; i--) {//假如个位已近排序后,那么个位大的在后,而根据十位排序也是从高位排到低位,所以需要倒过来存
        sum[count[arr[i] / exp % 10] - 1] = arr[i];//下标从0开始,所以需要-1;
        count[arr[i] / exp % 10]--;//对应的位数已经排序一个,所以数量-1;
    }
    memcpy(arr, sum, sizeof(int) * n);
    free(sum);
    return ;
}

void radix_sort(int *arr, int n) {
    int max = INT_MIN;
    for (int i = 0; i < n; i++) {//获取最大数的数
        max = max > arr[i] ? max : arr[i];
    }
    for (int i = 1; max / i > 0; i *= 10) {//从个位一直到大于最大数的位数
        number_sort(arr, n, i);
    }
    return ;
}

非稳定排序:

        1.选择排序:

        将数组分为两个区域一个已排序区和一个未排序区,每次将未排序区中的最值放在已排序区中;

        如图将54放放到最后,也就是已排序区中;

        这样一直在未排序区中选择,直到排序完成:

        时间复杂度: O(n^2)

        代码实现:

        

void select_sort(int *arr, int n) {//这里实现的是将最小的元素放在最前面,现在前面就是已排序区
    for (int i = 0; i < n - 1; i++) {
        int ind = i;//ind先记录当前准备排序的位置
        for (int j = i + 1; j < n; j++) {
                  if (arr[ind] > arr[j]) ind = j;//ind记录未排序区中最小值的值的位置
        }
        swap(arr[ind], arr[i]);//将未排序的区的最小值放在准备排序的位置
    }
    return ;
}

        2.快速排序:

        选择数组头步位置,作为基数,用一个变量记录下来,然后将这个基数作为中间值,将数组分为两个部分,前半部分小于这个基数,后半部分大于基数,然后在对两个部分进行上面的操作,直接这两个部分不能再分;这里不一定是二分开的,有可能这个基数是最大值,那么就没有前半部分;

        如图数组初始状态:

        将32作为基数,把数组分为两部分分:

        然后再对左边部分进行快排,右边部分进行快排

        这里只是刚好,不用变化,然后继续上面的操作,直到最后排完序:

        

        完成排序。

        时间复杂度:

        O(nlogn)

        最坏:O(n^2)

        代码实现:

        

//l最初为0,r最初为n-1
void quick_sort(int *arr, int l, int r) {
    if (r < l) return ;
    int x = l, y = r, num = arr[l];
    while (x < y) {
        while (x < y && arr[y] >= num) y--;//如果大于基数的部分,该位置数大于基数就不用交换
        if (x < y) arr[x++] = arr[y];//如果x<y,说明y位置的值是小于基数的,就放到前面去,第一次的时候x的位置就是基数的位置,所以覆盖的是基数的位置
        while (x < y && arr[x] <= num) x++;//如果小于基数的部分,该位置数小于等于基数就不用交换
        if (x < y) arr[y--] = arr[x];//x<y,说明x位置的值是小于基数的,现在y位置是之前小于基数的位置,直接覆盖
    }
    arr[x] = num;//将基数放到分割位置
    quick_sort(arr, l, x - 1);//小于基数的部分
    quick_sort(arr, x + 1, r);//大于基数的部分
    return ;
}

        3.希尔排序

        希尔排序其实就是对于选择排序的一个优化的算法,而最最坏的情况就是降为一个普通的选择排序。

        选择一个移动的长度,最开始选择数组长度的1/2,然后一直除2,直到步长等于1,进行一次插入排序;这个步长的作用就是,从一个位置往前移动多少步进行对该位置的值进行比较,如果不满住单调性就进行交换,然后交换如果还能往前目前的步长长度,继续往前比较;

        如图,直接上图片理解:

        现在的步长选作4,那么就从位置4进行开始排序:

        

        他俩进行比较,然后数组是单调递增,就就需要交换,然后比较位置向后移动:

        

        然后第一次步长结束后,那么步长在除以2:

        然后再次进行比较

        继续往后

        这里发生了交换,而且现在的位置还能往前走步长,所以也需要比较:

        比较后,不用发生交换,继续刚刚的位置往后:

        这里发生了交换,又需要往前走:

        直到不能发生交换,然后现在走到了最后的位置,进行步数除以2,等于1,进行一次选择排序:

        这里发生了交换,那就需要再往前走步长那么长进行比较:

        最后遍历,没有发生交换,并且步长为除2等于0了,完成排序;

        时间复杂度:O(nlogn) 

        最坏:O(n^2)

        代码实现:

        

void shell_sort(int *arr, int n) {
    int step;
    for (step = n / 2; step > 0; step >>= 1) {//步长循环
        for (int i = step; i < n; i++) {//从步长长度开始往后循环
            for (int j = i; j >= step && arr[j] < arr[j - step]; j -= step) {
                //如果不满足单调性,发生交换,并且如果现在的位置长度大于等于步长继续往前移动进行比较
                swap(arr[j], arr[j - step]);
            }
        }
    }
    return ;
}

        4.堆排序:

        堆,堆排序在前面这个链接的文章里,因为单独将堆排序有那么一点难理解需要结合对堆的理解才容易一些;

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

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

相关文章

C语言每日一练--Day(17)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;数对 截取字符串 &#x1f493;博主csdn个人主页&#xff1a;小小unico…

Linux——守护进程

简述 不受用户登录、注销影响的进程称为守护进程 特点 后台运行&#xff1a;守护进程在后台默默地执行任务&#xff0c;不与用户交互。它不会向终端输出信息&#xff0c;也不会从终端接收输入。 无终端关联&#xff1a;守护进程通常与任何终端会话&#xff08;比如SSH会话&…

软件测试/测试开发丨Pytest和Allure报告 学习笔记

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/26755 Pytest 命名规则 类型规则文件test_开头 或者 _test 结尾类Test 开头方法/函数test_开头注意&#xff1a;测试类中不可以添加__init__构造函数 注…

cvat 安装部署

官网地址&#xff1a; https://github.com/opencv/cvat/tree/masterhttps://github.com/opencv/cvat/tree/master 1.从官网上下载源码地址。 2.配置环境变量 vim /etc/profile source /etc/profile 或者执行&#xff1a; export CVAT_HOSTyour-ip-address 3.执行命令 …

无涯教程-Android - 应用组件

应用程序组件是Android应用程序的基本组成部分&#xff0c;这些组件需要在应用程序清单文件 AndroidManifest.xml 注册&#xff0c;该文件描述了应用程序的每个组件以及它们如何交互。 Android应用程序可以使用以下四个主要组件- Sr.NoComponents & 描述1 Activities 它们…

BMC相关知识

简介 BMC&#xff08;Baseboard Management Controller&#xff09;&#xff0c;基板管理控制器&#xff0c;普通PC没有&#xff0c;服务器产品必备。BMC是一个独立的系统&#xff0c;只要通电即可运行&#xff0c;服务器无需开机&#xff0c;不依赖其它软硬件&#xff0c;如O…

ChatGPT 总结数据分析的所有知识点

ChatGPT功能非常多,特别是对某个行业,某个方向,某个技术进行总结那是相当专业的。 如下图。 直接用一个指令便总结出来数据分析当中的所有知识点内容。 AIGC ChatGPT ,BI商业智能, 可视化Tableau, PowerBI, FineReport, 数据库Mysql Oracle, Office, Python ,ETL Ex…

打造个人的NAS云存储-通过Nextcloud搭建私有云盘实现公网远程访问

文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…

EXCEL中点击单元格,所在行和列都改变颜色

在日常工作中&#xff0c;尤其是办公室工作人群&#xff0c;尝尝需要处理大量的数据&#xff0c;在对数据进行修改时&#xff0c;时长发生看错行的事情&#xff0c;导致数据越改越乱&#xff0c;因此&#xff0c;我常用的一种方法就是选中单元格时&#xff0c;所在行、列标记为…

suricata安装与配置

一、功能介绍 1、概述 suricata来源于经典的nids系统snort&#xff0c;是一套基于网络流量的威胁检测引擎&#xff0c;整合了ids,ips&#xff0c;network security monitoring(NSM)和PCAP processing等功能。 2、IDS功能 通过监听网卡流量并匹配规则引擎进行入侵实时监测和…

【分享】小型园区组网场景

小型园区组网图 在小型园区中&#xff0c;S2700&S3700通常部署在网络的接入层&#xff0c;S5700&S6700通常部署在网络的核心&#xff0c;出口路由器一般选用AR系列路由器。 接入交换机与核心交换机通过Eth-Trunk组网保证可靠性。 每个部门业务划分到一个VLAN中&#…

如何高效进行测试用例评审

1.用例评审的目的 为了减少测试人员执行阶段做无效工作&#xff0c;执行无效case&#xff0c;提交无效缺陷&#xff08;可以友情提醒研发同学&#xff0c;讲到自己负责的相关模块时&#xff0c;注意下是否存在异议点&#xff09;为了避免三方&#xff08;产品、研发、测试&…

使用C语言计算1/1-1/2+1/3-1/4+...+1/99-1/100

观察算式&#xff0c;发现分子都是1&#xff0c;分母从1~100&#xff0c;所以可以使用for循环产生1~100之间的数。 另一个问题是&#xff0c;如何产生正负交替的符号&#xff1f;很简单&#xff0c;这个符号本质上就是往每一项前面乘一个系数&#xff1a;一或者负一。所以只需…

【数据结构练习】单链表OJ题(二)

目录 一、相交链表二、环形链表1三、环形链表2四、链表分割五、复制带随机指针的链表 一、相交链表 题目&#xff1a; 示例&#xff1a; 注意&#xff1a;不能根据节点的值来比较是否相交&#xff0c;而是根据节点在内存中是否指向相同的位置。 例如以上图&#xff1a; 链表…

无涯教程-Android - RadioButton函数

RadioButton有两种状态:选中或未选中,这允许用户从一组中选择一个选项。 Radio Button 示例 本示例将带您完成一些简单的步骤,以展示如何使用Linear Layout和RadioButton创建自己的Android应用程序。 以下是修改后的主要Activity文件 src/MainActivity.java 的内容。 packa…

数学建模:数据的预处理

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 数据预处理数据变换数据清洗缺失值处理异常值处理 数据预处理 数据变换 常见的数据变换的方式&#xff1a;通过某些简单的函数进行数据变换。 x ′ x 2 x ′ x x ′ log ⁡ ( x ) ∇ f ( x k )…

MATLAB中mod函数转化为C语言

背景 有项目算法使用matlab中mod函数进行运算&#xff0c;这里需要将转化为C语言&#xff0c;从而模拟算法运行&#xff0c;将算法移植到qt。 MATLAB中mod简单介绍 语法 b mod(a,m) 说明 b mod(a,m) 返回 a 除以 m 后的余数&#xff0c;其中 a 是被除数&#xff0c;m 是…

储能辅助电力系统调峰的容量需求研究(matlab代码)

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《储能辅助电力系统调峰的容量需求研究》&#xff0c;是一个很常规很经典的matlab优化代码&#xff0c;主要是对火电、风电和储能等电力设备主体进行优化调度&#xff0c;在调峰能力达不到时采…

Java 面试 - Redis

Redis Redis 是基于键值对的非关系型数据库。Redis 拥有string、hash、list、set、zset等多种数据结构, redis具有惊人的读写性能, 其优秀的持久化机制是的它在断电和机械故障时也不会发生数据丢失, 可以用于热点数据存放, 还提供了键过期、发布订阅、食物、流水线、LUA脚本等多…

创建聊天机器人:产品专属ChatGPT智能问答机器人,可添加任意网站

ChatGPT智能问答机器人可以广泛应用于各种SaaS产品&#xff0c;通过创建聊天机器人可以快速反馈用户&#xff0c;并且针对性的提供解决方案&#xff0c;非常高效的完成客户问答反馈。 聊天机器人是生活中常见的一种交互方式&#xff0c;机器人根据用户输入的关键字&#xff0c;…