【C语言】数据结构——排序三(归并与计数排序)

💗个人主页💗
⭐个人专栏——数据结构学习⭐
💫点击关注🤩一起学习C语言💯💫

目录

  • 导读:
  • 1. 归并排序
    • 1.1 基本思想
    • 1.2 递归实现
    • 1.3 非递归实现
  • 2. 计数排序
    • 2.1 基本思想
    • 2.2 代码实现

导读:

我们在前面学习了排序,包括直接插入排序,希尔排序,选择排序,堆排序,冒泡排序和快排。
今天我们来讲一讲归并排序和计数排序。
关注博主或是订阅专栏,掌握第一消息。

1. 归并排序

1.1 基本思想

归并排序的基本思想是将待排序的数组分成两个较小的子数组,然后递归地对这两个子数组进行排序,最后将两个有序的子数组合并成一个有序的数组。
在这里插入图片描述

  1. 将待排序数组分成两个较小的子数组,直到子数组中只剩下一个元素。
  2. 对两个子数组分别进行归并排序,即递归调用归并排序函数。
  3. 将两个有序的子数组进行合并,得到一个有序的数组。合并过程中,从两个子数组的第一个元素开始,依次比较大小,将较小的元素放入新的数组中。
  4. 将合并得到的有序数组返回。

归并排序的关键在于合并两个有序的子数组,这一步可以使用辅助数组来存储合并的结果。合并时,可以使用两个指针分别指向两个子数组的起始位置,比较指针指向的元素的大小,将较小的元素放入新的数组中,并将对应指针后移一位。当一个子数组遍历完后,将另一个子数组剩余的元素直接放入新的数组中即可。

1.2 递归实现

非递归实现归并排序的思想是通过迭代和循环来分割和合并数组,而不使用递归。

  1. 初始化一个辅助数组,用于存储合并的结果。
  2. 从数组的起始位置开始,将数组中的每个元素看作一个独立的有序序列,将它们分别放入辅助数组。
  3. 设置一个变量gap,初始值为1,代表每次合并的有序子序列的长度。
  4. 进入循环,循环条件是gap小于数组的长度。
  5. 在每一次循环中,将两个有序子序列合并并放入辅助数组中。
  6. 将合并得到的有序子序列放回原数组的对应位置。
  7. 将gap的值加倍,继续下一轮循环,直到gap大于或等于数组的长度

通过不断合并较小的有序子序列,直到整个数组排序完成,即可得到最终的有序数组。非递归实现归并排序的好处是减少了函数调用的开销,提高了排序的效率。

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end){return;}int mid = (begin + end) / 2;_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

1.3 非递归实现

//非递归
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){//printf("gap:%2d->", gap);for (size_t i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;// [begin1, end1][begin2, end2] 归并// 边界的处理if (end1 >= n || begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}//printf("\n");gap *= 2;}free(tmp);
}

2. 计数排序

2.1 基本思想

计数排序是一种线性时间复杂度的排序算法,适用于一定范围内的整数排序。其基本思想是通过统计每个元素的出现次数,然后根据统计结果将元素放置到正确的位置上。

  1. 统计每个元素出现的次数,创建一个计数数组,并初始化为0。
  2. 遍历待排序的数组,将每个元素对应的计数数组的对应位置加1,即统计元素出现次数。
  3. 对计数数组进行顺序累加,得到每个元素在排序后的数组中的最后一个下标位置。
  4. 创建一个临时数组,长度与待排序数组相同。
  5. 遍历待排序数组,根据元素值在计数数组中的累加结果,将元素放置到临时数组中的对应位置上。
  6. 将临时数组复制回原始数组,完成排序。

需要注意的是,计数排序只适用于非负整数排序,并且在k不是很大的情况下才能保证排序的效率。

2.2 代码实现

//计数排序
void CountSort(int* a, int n)
{int min = a[0], max = a[0];for (int i = 1; i < n; i++){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if (count == NULL){printf("calloc fail\n");return;}// 统计次数for (int i = 0; i < n; i++){count[a[i] - min]++;}// 排序int i = 0;for (int j = 0; j < range; j++){while (count[j]--){a[i++] = j + min;}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

考研机试题收获——高精度进制转换

代码的第一遍真的很重要&#xff0c;在第一次打的时候尽量把问题思考全面&#xff0c;不要漏打少打&#xff0c;尽量不要留bug给之后de。 一、基础方面 一、处理输出的结束问题 scanf和cin默认都不会读取空格 ①scanf()&#xff1a;如果从文件中读取数据&#xff0c;当scanf()…

134基于matlab的时间序列预测

基于matlab的时间序列预测&#xff0c;包括最小二乘支持向量机和粒子群优化支持向量机及改进的粒子群优化支持向量机。输出测试结果&#xff0c;具有GUI可视化界面。程序已调通&#xff0c;可直接运行。 134matlab时间序列预测粒子群优化 (xiaohongshu.com)

.Net 8.0 Web API Controllers 添加到 windows 服务

示例源码下载&#xff1a;https://download.csdn.net/download/hefeng_aspnet/88747022 创建 Windows 服务的方法之一是从工作线程服务模板开始。 但是&#xff0c;如果您希望能够让它托管 API 控制器&#xff08;也许是为了查看它正在运行的进程的状态&#xff09;&#xff0…

TCP之三次握手四次挥手与UDP区别

文章目录 1 TCP三次握手四次挥手1.1 数据包说明1.1.1 TCP数据包1.1.2 UDP数据包1.1.3 TCP和UDP差异1.1.4 TCP可靠性传输机制 1.2 三次握手1.2.1 三次握手定义1.2.2 三次握手问题1.2.2.1 问题引入分析1.2.2.2 历史连接1.2.2.3 同步双方初始序列号1.2.2.4 避免资源浪费 1.3 四次挥…

遥感影像-语义分割数据集:高分卫星-云数据集详细介绍及训练样本处理流程

原始数据集详情 简介&#xff1a;该云数据集包括RGB三通道的高分辨率图像&#xff0c;包含高分一、高分二及宽幅数据集。 KeyValue卫星类型高分系列覆盖区域未知场景未知分辨率1m、2m、8m数量12000单张尺寸1024*1024原始影像位深8位标签图片位深8位原始影像通道数三通道标签图…

08- OpenCV:形态学操作(膨胀与腐蚀 、提取水平与垂直线)

目录 前言 一、膨胀&#xff08;Dilation&#xff09;与 腐蚀&#xff08;Erosion&#xff09; 二、形态学操作 1、开操作&#xff08;Opening&#xff09; 2、闭操作&#xff08;Closing&#xff09; 3、形态学梯度&#xff08;Morphological Gradient&#xff09; 4、…

SpringBoot教程(十一) | SpringBoot集成Mybatis

SpringBoot教程(十一) | SpringBoot集成Mybatis 上一篇文章我们介绍了SpringBoot集成JdbcTemplate.简单体验了一下JdbcTemplate框架的用法&#xff0c;今天的内容比较重要&#xff0c;我们来介绍一下SpringBoot集成Mybatis的步骤。 1、 Mybatis 介绍 MyBatis 本是apache的一…

C#,入门教程(18)——分支语句(switch-case)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(17)——条件语句&#xff08;if-else&#xff09;的基础知识https://blog.csdn.net/beijinghorn/article/details/124033376 1、switch概述 switch-case分支语句 可以理解为 大号 的 if-else。 switch语句以switch关键字开头&…

树莓派4B+ubuntu20.04+ros1桌面配置(一)

烧录系统至树莓派 下载系统&#xff1a; 方案一 https://ubuntu.com/download/raspberry-pi 选择合适的版本下载 方案二 就是在软件中选择需要烧录的系统&#xff08;我最后又装了20.04的ubuntu server系统&#xff0c;因为22的系统不能装ros1&#xff09; 方案三(采用…

vue中使用高德地图,根据类型显示不同点,点击出现弹框居中显示,并在可视化区域显示所有点

效果图 一、安装 vue-amap 插件 npm install vue-amap --save二、vue页面 <template> <div><ul styledisplay:flex;><liv-for"(item, index) in checkList":key"index"click"onClick(item)":class"item.checked ?…

Vue加载序列帧动图

解读方法 使用<img :src"currentFrame" alt"加载中" /> 加载图片动态更改src的值使用 requestAnimationFrame 定时更新在需要的页面调用封装的组件 <LoadToast v-if"showLoading" /> 封装组件 <template><div class"…

python flask学生管理系统

预览 前端 jquery css html bootstrap: 4.x 后端 python: 3.6.x flask: 2.0.x 数据库 mysql: 5.7 学生管理模块 登录、退出查看个人信息、修改个人信息成绩查询查看已选课程选课、取消选课搜索课程课程列表分页功能 教师模块 登录、退出查看个人信息、修改个人信息录入…