椋鸟数据结构笔记#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://xiahunao.cn/news/2980197.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

贪吃蛇(C语言版)

在我们学习完C语言 和单链表知识点后 我们开始写个贪吃蛇的代码 目标&#xff1a;使用C语言在Windows环境的控制台模拟实现经典小游戏贪吃蛇 贪吃蛇代码实现的基本功能&#xff1a; 地图的绘制 蛇、食物的创建 蛇的状态&#xff08;正常 撞墙 撞到自己 正常退出&#xf…

SpringCloud系列(11)--将微服务注册进Eureka集群

前言&#xff1a;在上一章节中我们介绍并成功搭建了Eureka集群&#xff0c;本章节则介绍如何把微服务注册进Eureka集群&#xff0c;使服务达到高可用的目的 Eureka架构原理图 1、分别修改consumer-order80模块和provider-payment8001模块的application.yml文件&#xff0c;使这…

刷题之Leetcode242题(超级详细)

242.有效的字母异位词 力扣题目链接(opens new window)https://leetcode.cn/problems/valid-anagram/ 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2…

使用kali进行DDos攻击

使用kali进行DDos攻击 1、打开命令提示符&#xff0c;下载DDos-Attack python脚本 git clone https://github.com/Elsa-zlt/DDos-Attack 2、下载好之后&#xff0c;cd到DDos-Attack文件夹下 cd DDos-Attack 3、修改&#xff08;设置&#xff09;对ddos-attack.py文件执行的权…

抖音小店现在还能做吗?未来还有多大的发展空间?聊聊我的看法

大家好&#xff0c;我是电商笨笨熊 关于“抖店还能做吗”这样的问题&#xff0c;每年都有人在问&#xff1b; 尤其是今年来说&#xff0c;抖店已经走过了四五年的时间&#xff0c;很多人担心抖店还能走多远&#xff0c;还能做多久&#xff1b; 一些一直未进入抖店但持续在观…

【从零开始学习IO机制 | 第一篇】I/O的演进之路

前言&#xff1a; 自诞生以来&#xff0c;Java 一直是软件开发领域的重要一环。作为一种广泛应用于各种应用程序和系统的编程语言&#xff0c;Java 一直致力于提供高效、可靠的 I/O&#xff08;输入/输出&#xff09;操作&#xff0c;以满足不断增长的软件需求和用户期望。 Ja…

javaweb-数据库

数据库管理系统&#xff08;DataBase Management System&#xff0c;简称DBMS&#xff09; MySQL 官网&#xff1a;MySQL :: Developer Zone 安装 官网下载地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions) 图形化工具 通常为了提高开发效…

仓库管理存在的问题及改进对策?

大部分人都指导仓库问题会影响一个仓库操作或与之相关的整个流程链的速度、效率和生产力。但在大多数情况下&#xff0c;只有在流程开始甚至完成后才能识别这些错误。 到那时通常已经来不及阻止错误了&#xff0c;甚至可能来不及减少造成的损害。 所以这也是我写这篇内容的目…

【计算机毕业设计】理发店管理系统产品功能说明——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…

Linux部署MySQL8.0—手把手保姆级教程

&#x1f469;&#x1f3fd;‍&#x1f4bb;个人主页&#xff1a;阿木木AEcru &#x1f525; 系列专栏&#xff1a;《Docker容器化部署系列》 《Java每日面筋》 &#x1f4b9;每一次技术突破&#xff0c;都是对自我能力的挑战和超越。 目录 一、下载MySQL8.0安装包二、安装MySQ…

顺序栈着三种结构定义及其初始化

定义 顺序堆栈这三种结构定义及其初始化 - 知乎 (zhihu.com) 根据以上链接得到&#xff1a; 1.理解为数组&#xff0c;top是这个数组的索引值&#xff1b;定义这个结构体类型时&#xff0c;系统不分配空间 在主函数声明时&#xff0c;定义了关于这个结构体的变量&#xff0c…

python基础知识三(运算符、while循环、for循环)

目录 运算符&#xff1a; 算术运算符&#xff1a; 比较运算符&#xff1a; 赋值运算符&#xff1a; 逻辑运算符&#xff1a; 位运算符&#xff1a; 成员运算符&#xff1a; while循环&#xff1a; 1. while循环的语法&#xff1a; 2. while循环的执行过程&#xff1a…

Docker搭建Maven仓库Nexus

文章目录 一、简介二、Docker部署三、仓库配置四、用户使用Maven五、管理Docker镜像 一、简介 Nexus Repository Manager&#xff08;简称Nexus&#xff09;是一个强大的仓库管理器。 Nexus3支持maven、docker、npm、yum、apt等多种仓库的管理。 建立了 Maven 私服后&#xf…

新技术前沿-2024-国内主流AI大模型架构及应用场景深度分析

参考国内主流AI 大模型架构及应用场景深度分析 2024 1 厂商总览 1.1 国外 (1)Open AI:GPT-4【美国旧金山的人工智能研究公司】 GPT-4于2023年3月14日发布,是千亿级参数的多模态预训练模型,能够支持图像和文本的输入。 (2)Anthropic(人类的):Claude【美国人工智能初创公司…

【前端技术】HTML基础入门篇

1.1 HTML简介 ​ HTML&#xff08;HyperText Markup Language&#xff1a;超文本标记语言&#xff09;是一种标识性的语言。它包括一系列标签&#xff0e;通过这些标签可以将网络上的文档格式统一&#xff0c;使分散的Internet资源连接为一个逻辑整体。HTML文本是由HTML命令组…

【嵌入式AI部署神经网络】STM32CubeIDE上部署神经网络之指纹识别(Pytorch)——篇一|环境搭建与模型初步部署篇

前言&#xff1a;本篇主要讲解搭建所需环境&#xff0c;以及基于pytorch框架在stm32cubeide上部署神经网络&#xff0c;部署神经网络到STM32单片机&#xff0c;本篇实现初步部署模型&#xff0c;没有加入训练集与验证集&#xff0c;将在第二篇加入。篇二详细讲解STM32CubeIDE上…

NestJS必备:TypeORM对DB的操作

文章概叙 本文大概1300字&#xff0c;讲的是一些关于TypeORM的基础知识以及在NestJS中使用TypeORM操作DB的例子。 关于TypeORM TypeORM 是一个ORM框架&#xff0c;它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上&#xff0…

Kubectl常见排查pod问题命令

一.查看命名空间pod及其日志 #查看命名空间pod kubectl get pods -n <命名空间名称> #该命令不加-n命名空间名称&#xff0c;默认是查看default命名空间的pod#查看对应pod的日志kubectl logs -f <pod-name> -n <namespace>#同样的如果查看的是default命名空…

jasypt组件死锁bug案例分享

事故描述 1、上午9.55发布了一个Apollo动态配置参数&#xff1b; 2、片刻后&#xff0c;服务器接口开始出现大量的超时告警&#xff0c;似乎是某资源被耗尽不足分配&#xff1b; 3、正值业务请求高峰的上午十点&#xff08;平台上午10点会有一些活动会拉一波用户流量&#x…

使用eNSP进行路由策略与引入实验

一、实验拓扑 二、实验要求 1、按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 上使用 oopback 口模拟业务网段&#xff0c; 2、R2&#xff0c;R3 和 R4 运行 OSPF&#xff0c;各自协议内部互通2R1和R2运丁RIPv2 3、在 RIP 和 OSPF 间配黑双向路由引入&#…