椋鸟数据结构笔记#11:排序·下

文章目录

      • 外排序(外部排序)
        • 文件拆分并排序
        • 归并文件
          • 两个文件归并
          • 多文件归并
          • 优化

萌新的学习笔记,写错了恳请斧正。

外排序(外部排序)

数据量非常庞大以至于无法全部写入内存时,我们应该怎么排序这些数据呢?

这时,就要学会直接进行在外存中排序,也就是外部排序。

外排序不是一种独立的排序算法,还是要依赖于之前的那些排序方式。

其基本思想是这样的:

  • 将庞大的数据拆分为一个一个子文件,使得每一个子文件的数据量在内存排序可承受的范围内。
  • 将每一个文件进行正常的排序,可以选用之前学的任何排序方法。
  • 将排序好的文件再进行归并,最终合成为包含有序的所有数据的文件。

看起来不是很复杂,但是归并部分还是很有东西的。下面我们逐步讲解:

文件拆分并排序

这并不复杂,比方说我们将文件拆分为splitCount个:

void FileSort(const char* filename, int splitCount)
{clock_t start = clock();//分割文件FILE* fp = fopen(filename, "r");if (fp == NULL){perror("fail to open");exit(EXIT_FAILURE);}int* num = (int*)malloc(sizeof(int) * N / splitCount);	//存放每个分割文件的数据int iNum = 0;	//num数组的下标int iFile = 0;	//分割文件的下标int n = 0;	//读取的数据char splitFileName[50] = { 0 };	//分割文件名while (fscanf(fp, "%d", &n) != EOF){if (iNum < N / splitCount - 1){num[iNum++] = n;}else{num[iNum] = n;//排序HeapSort(num, N / splitCount);//写入文件sprintf(splitFileName, "SubSort\\sort_split%d.txt", iFile++);FILE* splitFile = fopen(splitFileName, "w");if (splitFile == NULL){perror("fail to open");exit(EXIT_FAILURE);}for (int i = 0; i < N / splitCount; i++){fprintf(splitFile, "%d\n", num[i]);}fclose(splitFile);printf("已分割第%d个文件,总共需分割%d个文件。\n", iFile, splitCount);iNum = 0;}}//归并文件//这里插入归并文件的代码//关闭文件fclose(fp);clock_t end = clock();printf("排序完成,共%d个数据,分割为%d个文件,归并为1个文件。\n", N, splitCount);printf("耗时:%f秒。\n", (double)(end - start) / CLOCKS_PER_SEC);
}
归并文件

这是重点也是最复杂的地方。

两个文件归并

我们知道两个排序文件归并的方法:两边一起读取,一直对比两边读取的数,不断取小写入输出文件即可。

void MergeFile(char* file1, char* file2, char* mergeFileName)
{FILE* fp1 = fopen(file1, "r");FILE* fp2 = fopen(file2, "r");if (fp1 == NULL){perror("fail to open");exit(EXIT_FAILURE);}if (fp2 == NULL){perror("fail to open");exit(EXIT_FAILURE);}FILE* fin = fopen("tmp.txt", "w");if (fin == NULL){perror("fail to open");exit(EXIT_FAILURE);}int n1, n2;int ret1 = fscanf(fp1, "%d", &n1);int ret2 = fscanf(fp2, "%d", &n2);while (ret1 != EOF && ret2 != EOF){if (n1 < n2){fprintf(fin, "%d\n", n1);ret1 = fscanf(fp1, "%d", &n1);}else{fprintf(fin, "%d\n", n2);ret2 = fscanf(fp2, "%d", &n2);}}while (fscanf(fp1, "%d", &n1) != EOF){fprintf(fin, "%d\n", n1);}while (fscanf(fp2, "%d", &n2) != EOF){fprintf(fin, "%d\n", n2);}fclose(fp1);fclose(fp2);fclose(fin);remove(file1);remove(file2);rename("tmp.txt", mergeFileName);
}

注意:这里我们先写入数据至tmp.txt中,最后在将其改名为mergeFileName是为了防止mergeFileName就是file1或者file2的情况(也即直接把file2归并到file1中而不创建新的文件,或者反过来)。

多文件归并

但是我们往往不仅仅要把数据切分为两个,而是几十个上百个。这时我们就需要使用多文件归并的方法。

  1. 直接逐个归并

    这是最容易理解也是最差的方法。就是把拆分文件1和拆分文件2归并,结果再和拆分文件3归并,结果再和拆分文件4归并……最终得到完整的归并文件。

    傻子都能看出来这个方法的不靠谱,但是我们还是将代码贴出来:

    //归并文件,这段代码填入上方FileSort函数中归并的留空位置
    char* mergeFileName = "sort_merge.txt";
    char file1[50] = "SubSort\\sort_split0.txt", file2[50] = {0};
    for (int i = 1; i < splitCount; i++)
    {sprintf(file2, "SubSort\\sort_split%d.txt", i);MergeFile(file1, file2, mergeFileName);strcpy(file1, mergeFileName);printf("已归并%d个文件,总共需归并%d个文件。\n", i + 1, splitCount);
    }
    

    这个方法是真的不靠谱,一亿个数据半小时还没归并完。

  2. 分治归并

    就是不断地两两分组归并,比方说9至16个文件归并成5至8个,5至8个归并位3至4个,再归并为2个,最后合成完整的排序后文件。

    //分治归并文件,这段代码填入上方FileSort函数中归并的留空位置
    MergeFileR(0, splitCount - 1);
    remove("SubSort\\sort_split0.txt");
    

    其中MergeFileR的定义如下:

    void MergeFileR(int left, int right)
    {if (left >= right){return;}int mid = (left + right) / 2;//递归归并MergeFileR(left, mid);MergeFileR(mid + 1, right);//归并char file1[50] = { 0 }, file2[50] = { 0 }, mergeFileName[50] = { 0 };sprintf(file1, "SubSort\\sort_split%d.txt", left);sprintf(file2, "SubSort\\sort_split%d.txt", mid + 1);sprintf(mergeFileName, "SubSort\\sort_split%d.txt", left);MergeFile(file1, file2, mergeFileName);printf("已归并%d与%d文件至%d。\n", left, mid + 1, left);
    }
    

    这个方法要靠谱一点,测试如下:

优化

进一步优化可以将基础操作从两个文件归并变成3个文件归并或者更多,这样分治时就是3合一或者更多。这样多路归并会增加内存开销和内部时间开销,但是会大大减少外部读写的开销。

更进一步优化可以引入败者树、最佳归并树等,我还不会。

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

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

相关文章

Route Discovery Route Repair

1. Route Discovery 当单播消息从一台设备发送到另一台设备&#xff0c;并且没有预先存在的路由时&#xff0c;会发起路由发现。 我们假设没有现有的路由&#xff0c;因此网络软件将开始路由发现过程。为简单起见&#xff0c;假设所有设备的路由表都为空。 以设备A向设备C发送…

[lesson49]多态的概念和意义

多态的概念和意义 函数重写回顾 父类中被重写的函数依然会继承给子类 子类中重写的函数将覆盖父类中的函数 通过作用域分辨符(::)访问父类中的同名成员 多态的概念和意义 面向对象中期望的行为 根据实际的对象类型判断如何调用重写函数父类指针(引用)指向 父类对象则调用…

【LeetCode】---118.杨辉三角

一、题目解析&#xff1a; 二、知识回顾&#xff1a; 1.二维数组&#xff1a; 2. C语言中的二维数组访问方式和vector二维数组的访问&#xff0c; 不同区别&#xff1a; &#xff08;1&#xff09;表面是一样的&#xff0c;但底层不同&#xff01; &#xff08;2&#xff09;静…

JAVA 项目<果园之窗>_2

上节主要是理论流程&#xff0c;这次直接用实际例子过一遍整个流程 目标是向数据库添加一个员工 上述是前端页面&#xff0c;点击保存 浏览器向我后端发送http请求 后端这一部分专门接收employee请求 在这里对http post请求进行转换成JAVA数据&#xff0c;并处理数据&#xff…

在linux系统中启动pycharm

1.找到pycharm的安装路径&#xff0c;一般在下载文件夹中 2.进入pycharm的安装路径&#xff0c;进入bin目录 3.右击&#xff0c;打开终端&#xff0c;输入./pycharm.sh

奇妙的探索——偶然发现的bug

今天想在腾讯招聘官网找几个前端的岗位投一下&#xff0c;最近自己也在找工作&#xff0c;结果简历还没有投出去&#xff0c;就发现了腾旭招聘官网的3个前端bug。 1.有时候鼠标hover还没有滑倒下拉选框的菜单上&#xff0c;就消失了&#xff0c;消失的太快了&#xff0c;根本点…

为智算产业高质量发展探寻路径,又一重要生态合作启动

4月22日&#xff0c;由中国工业经济联合会主办的“2024中国工业经济高峰论坛智能算力产业高质量发展论坛”落幕。院士专家、研究机构、以及来自智能算力产业上下游企业代表近180人出席&#xff0c;围绕完善算力基础设施、深化算力赋能行业应用、推动区域数字化发展等热点议题展…

Linux加强篇-Vim编辑器

目录 ⛳️推荐 Vim文本编辑器 编写简单文档 配置主机名称 配置网卡信息 配置软件仓库 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Vim文本编辑器 在Linux系统中一切都…

重磅发布 | 《网络安全专用产品指南》(第一版)

2017年6月1日&#xff0c;《中华人民共和国网络安全法》正式实施&#xff0c;明确规定“网络关键设备和网络安全专用产品应当按照相关国家标准的强制性要求&#xff0c;由具备资格的机构安全认证合格或者安全检测符合要求后&#xff0c;方可销售或者提供。国家网信部门会同国务…

区区几行Python代码,就能实现全面自动探索性数据分析!

探索性数据分析是数据科学模型开发和数据集研究的重要组成部分之一。在拿到一个新数据集时首先就需要花费大量时间进行EDA来研究数据集中内在的信息。自动化的EDA Python包可以用几行Python代码执行EDA。在本文中整理了10个可以自动执行EDA并生成有关数据的见解的Python包&…

ROM修改进阶教程------安卓7_____安卓13去除签名验证操作步骤解析

同类博文: 安卓玩机搞机技巧综合资源-----修改rom 制作rom 解包rom的一些问题解析【二十一】_qcn改区域锁-CSDN博客 安卓系列机型rom修改。如果你删减了系统相关的app。那么严重会导致开机系统卡米 定屏等问题。这类一般都是系统签名验证导致的。而破解签名验证一般都是修改…

API接口的用途以及接入示例

API接口的主要用途是允许不同的软件系统之间进行通信和数据交换。具体来说&#xff0c;API接口可以用于以下几个方面&#xff1a; 数据传输和交换&#xff1a;API接口可以用于不同系统之间的数据传输和交换&#xff0c;例如将数据从一个系统传递到另一个系统&#xff0c;或者从…

【office安装错误1402或1406】

office安装错误1402或1406 错误如图 解决方法 打开autoremove&#xff0c;点击扩展&#xff0c;输入1402&#xff0c;点击搜索 等待修复成功&#xff0c;再尝试安装office 软件每周六选择其他登录方式可以免费使用

Python学习1--变量和简单数据类型

本章练习&#xff1a; Python之禅&#xff1a;

RocketMQ快速入门:namesrv、broker、dashboard的作用及消息发送、消费流程(三)

0. 引言 接触rocketmq之后&#xff0c;大家首当其冲的就会发现需要安装3个组件&#xff1a;namesrv, broker, dashboard&#xff0c;其中dashboard也叫console&#xff0c;为选装。而这几个组件之前的关系是什么呢&#xff0c;消息发送和接收的过程是如何传递的呢&#xff0c;…

如何在Windows 10中打开和自定义搜索?这里提供详细步骤

使用Windows 10中的搜索功能&#xff0c;你可以快速查找计算机上的文件、应用程序或设置。在本文&#xff0c;你可以学习如何在Windows 10中打开和控制搜索。 打开Windows 10搜索面板 打开Windows 10搜索面板很容易。通常&#xff0c;你可以在任务栏上找到搜索图标。只需单击…

如何在PostgreSQL中创建一个新的数据库,并指定所有者?

文章目录 解决方案示例代码 PostgreSQL是一个强大的开源关系型数据库管理系统&#xff0c;它允许用户创建和管理多个数据库。在PostgreSQL中创建一个新的数据库并指定所有者是一个常见的操作。下面&#xff0c;我们将详细解释如何执行这一操作&#xff0c;并提供示例代码。 解…

灭火器检查记录卡模板如何制作

灭火器是常见的消防设备&#xff0c;为确保灭火器正常使用&#xff0c;需要定期对灭火器进行检查和维护&#xff1b;而灭火器检查记录卡就是用来记录灭火器检查的重要工具。然而传统的灭火器检查记录卡都是纸质的&#xff0c;哪怕我们采购多好多贵材质做的检查卡终归记录有限、…

Midjourney如何利用chaos控制生成图片的差异化

hello 小伙伴们&#xff0c;我是你们的老朋友——树下&#xff0c;今天分享Midjourney提示词常用参数——chaos&#xff0c;话不多说&#xff0c;直接开始~ chaos参数什么意思呢&#xff1f; 它可以用来控制我们生成图片之间的差异化程度的一个参数 通常我们在用Midjourney生…

LateX的基础学习

what can i say 在text.tex中写下 \documentclass{article} \begin{document]Hello \LaTeX. \end{document} 关闭记事本&#xff0c;cmd中dir保存&#xff0c;用latex text.tex来编译&#xff0c;可以命令行慢慢编译&#xff0c;这可以做成bat文件 为什么不直接开始在texst…