数据结构加餐:三路划分、自省排序、文件归并排序

news2024/11/19 17:42:17

数据结构加餐1

  • 1.快排之三路划分
  • 2.快排之自省排序
  • 3.文件归并排序
    • 3.1外排序
    • 3.2归并排序的实现
      • 3.2.1归并排序思想
      • 3.2.2文件归并排序代码实现

1.快排之三路划分

在之前完成的快排仍然存在这一些问题,当重复数据较多时,快速排序选择的基值也会较不恰当,比如在一组全为2的数组中,选择的基值也会是2,但是这样就会触发快排的最坏情况,导致时间的增加,下面我们要介绍一个方法来解决这种情况

我们先实现基本的快排(Hoare 版本):

//快速排序--Hoare版本
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//寻找基值
int Quick_Sort(int* arr, int left, int right)
{
	//假设第一个值为基值
	int mid = left;
	left++;

	while (left <= right)
	{
		//left负责寻找比基值大的元素
		while (left <= right && arr[left] < arr[mid])
		{
			left++;
		}
		//right负责寻找比基值小的元素
		while (left <= right && arr[right] > arr[mid])
		{
			right--;
		}
		//将左右数据交换,大数据在右边,小数据在左边
		if(left <= right)
		{
			Swap(&arr[left++], &arr[right--]);
		}
	}

	Swap(&arr[mid], &arr[right]);
	return mid;
}

void QuickSort(int* arr, int left, int right)
{
	//快速排序需要递归寻找基值
	//递归结束条件
	if (left >= right)
	{
		return;
	}
	
	//寻找基值
	int mid = Quick_Sort(arr, left, right);

	//递归区间
	//左区间:[left, mid-1], 右区间:[mid+1, right] 
	QuickSort(arr, left, mid-1);
	QuickSort(arr, mid+1, right);
}

void Print(int* arr, int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
}

int main()
{
	int arr[] = { 1, 22, 3, 43, 66, 27, 38, 49 };
	QuickSort(arr, 0, 7);

	Print(arr, 8);
	return 0;
}

更改后的代码为:

在这里插入图片描述

void QuickSort(int* arr, int left, int right)
{
	//递归结束条件
	if (left >= right)
	{
		return;
	}

	//寻找基值
	int begin = left;
	int end = right;

	int cur = left + 1;
	int key = arr[left];
	while (cur <= right)
	{
		if (arr[cur] < key)
		{
			Swap(&arr[cur], &arr[left]);
			cur++;
			left++;
		}
		else if (arr[cur] > key)
		{
			Swap(&arr[cur], &arr[right]);
			right--;
		}
		else
		{
			cur++;
		}
	}

	QuickSort(arr, begin, left-1);
	QuickSort(arr, right+1, end);
}

int main()
{
	int arr[] = { 1, 22, 3, 43, 66, 27, 38, 49 };
	QuickSort(arr, 0, 7);

	Print(arr, 8);
	return 0;
}

2.快排之自省排序

自省排序值的就是在排序函数之中加上一个检查的功能,使得排序算法能够针对不同的环境做出不同的反应,这一部分较为简单,因为都是已经学过的内容,所以我们直接看改变即可

void IntroSort(int* a, int left, int right, int depth, int defaultDepth)
{
	if (left >= right)
		return;

	// 数组⻓度⼩于16的⼩数组,换为插⼊排序,简单递归次数
	if (right - left + 1 < 16)
	{
		//此时使用插入排序
		InsertSort(a + left, right - left + 1);
		return;
	}
	// 当深度超过2*logN时改⽤堆排序
	if (depth > defaultDepth)
	{
		HeapSort(a + left, right - left + 1);
		return;
	}

	depth++;//每次完成一次递归,将递归深度++

	int begin = left;
	int end = right;
	// 随机选key
	int randi = left + (rand() % (right - left + 1));
	Swap(&a[left], &a[randi]);
	int prev = left;
	int cur = prev + 1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	// [begin, keyi-1] keyi [keyi+1, end]
	IntroSort(a, begin, keyi - 1, depth, defaultDepth);
	IntroSort(a, keyi + 1, end, depth, defaultDepth);
}

void QuickSort(int* a, int left, int right)
{
	int depth = 0;
	int logn = 0;
	int N = right - left + 1;
	//计算logN的值
	for (int i = 1; i < N; i *= 2)
	{
		logn++;
	}
	// introspective sort -- ⾃省排序
	// 一般来说,当深度大于2倍的logN时,最合理,但也不是必须要使用2倍当作结点
	IntroSort(a, left, right, depth, logn * 2);
}

int* sortArray(int* nums, int numsSize, int* returnSize) {
	srand(time(0));
	QuickSort(nums, 0, numsSize - 1);
	*returnSize = numsSize;
	return nums;
}

3.文件归并排序

3.1外排序

外排序是指能够处理大量数据的排序算法,比如说10G的数据,外排序的数据不能够一次性地写入内存,只能放在读写比较慢的硬盘上,也就是写入文件中,通常会采用“排序-归并”的思想。先读取能够放在内存中的数据量,将这些数据排成有序后放在一个临时文件中,然后在归并阶段将一个个的小文件归并成一个文件即可

3.2归并排序的实现

3.2.1归并排序思想

先将一个一个的思想列出,最后有一个总截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.2.2文件归并排序代码实现

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <time.h>
#include <stdlib.h>


//创建随机数,并将数据写到文件中
void CreateNDate()
{
    //造数据
    int n = 100;
    srand(time(0));
    const char* file = "data.txt";
    FILE* fin = fopen(file, "w");
    if (fin == NULL)
    {
        perror("fopen error");
        return;
    }

    for (int i = 0; i < n; ++i)
    {
        int x = rand() + i;
        fprintf(fin, "%d\n", x);
    }

    fclose(fin);
}

//qsort函数比较方法
int compare(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}

//读取数据,将数据排序后写入文件中
int ReadDataSortToFile(FILE* fout, int m, const char* file2)    
{
    //这个函数为将排序后的数据写到文件中,所以肯定要先进行排序
    //排序思路为先创建出一个数组,存储m个数据,对数组中的数据进行排序
    int* a = (int*)malloc(m * sizeof(int));
    if (a == NULL)
    {
        perror("malloc fail");
        return 0;
    }

    int x = 0;
    int j = 0;
    for (int i = 0; i < m; i++)
    {
        //这里要对最后一组数据读取进行特殊处理(最后一组数据读取可能读取不了m个数据):
        //如果读取不到数据了,就停止读取数据,也就是说实际上读取的数据个数其实只有j个
        if (fscanf(fout, "%d", &x) == EOF)
            break;
        a[j++] = x;
    }
    if (j == 0)//当没有数据读取时,直接返回即可
    {
        free(a);
        return 0;
    }

    //有了数组之后,就可以对数组中的数据进行排序
    qsort(a, j, sizeof(int), compare);//既然读取了j个数据,那么就对j个数据进行排序

    //排序完成,将数据写入文件中,这时只需要创建一个文件,然后写入数据即可
    FILE* fin = fopen(file2, "w");
    if (fin == NULL)
    {
        //注意要释放空间
        free(a);
        perror("fopen fail");
        return 0;
    }
    for (int i = 0; i < j; i++)
    {
        fprintf(fin, "%d\n", a[i]);//这里写入时,需要加上\n
    }

    //最后注意要释放空间
    free(a);
    a = NULL;
    fclose(fin);
    return j;
}

//将file1和file2文件中的数据排序着写入mfile文件中
void MergeFile(const char* file1, const char* file2, const char* mfile)
{
    //对于两个文件的排序,只需要一个数据一个数据地读取,然后将读取的数据进行比较,小的数据写入mfile文件即可
    //先打开三个文件
    FILE* fout1 = fopen(file1, "r");
    if (fout1 == NULL)
    {
        perror("fopen error");
        return;
    }
    FILE* fout2 = fopen(file2, "r");
    if (fout2 == NULL)
    {
        perror("fopen error");
        return;
    }
    FILE* mfin = fopen(mfile, "w");
    if (mfin == NULL)
    {
        perror("fopen error");
        return;
    }

    //然后是排序写入文件过程
    int x1 = 0;
    int x2 = 0;
    int ret1 = fscanf(fout1, "%d", &x1);//fscanf函数,如果读取失败或已经没有数据读取了,返回EOF
    int ret2 = fscanf(fout2, "%d", &x2);
    while (ret1 != EOF && ret2 != EOF)
    {
        if (x1 < x2)
        {
            fprintf(mfin, "%d\n", x1);
            ret1 = fscanf(fout1, "%d", &x1);
        }
        else
        {
            fprintf(mfin, "%d\n", x2);
            ret2 = fscanf(fout2, "%d", &x2);
        }
    }
    while (ret1 != EOF)
    {
        fprintf(mfin, "%d\n", x1);
        ret1 = fscanf(fout1, "%d", &x1);
    }

    while (ret2 != EOF)
    {
        fprintf(mfin, "%d\n", x2);
        ret2 = fscanf(fout2, "%d", &x2);
    }

    //最后要注意关闭文件
    fclose(fout1);
    fclose(fout2);
    fclose(mfin);
}

int main()
{
    //创建数据函数,也就是创建file数据文件
    //CreateNDate();

    //创建file1和file2文件,并且读取数据进行排序后写入file1和file2文件中
    //既然要多次使用一个方法,那么我们就将这个方法写成一个函数
    const char* file1 = "file1.txt";
    const char* file2 = "file2.txt";
    const char* mfile = "mfile.txt";

    FILE* fout = fopen("data.txt", "r");//fopen函数返回一个指向打开文件的指针,错误时返回NULL
    if (fout == NULL)
    {
        perror("fopen fail");
        return 0;
    }

    int m = 10;
    //函数参数的意思是:向fout文件中读取m个数据排序后写入file1文件中
    ReadDataSortToFile(fout, m, file1);
    ReadDataSortToFile(fout, m, file2);

    //将file1和file2文件中的数据排序着放到mfile文件中
    while (1)
    {
        //文件写入函数
        MergeFile(file1, file2, mfile);

        //关闭file1和file2文件
        remove(file1);//remove函数作用为删除一个文件,成功返回0,不成功返回-1,并设置错误原因
        remove(file2);
    
        //将mfile文件名称改为file1
        rename(mfile, file1);//remane函数:将文件重命名,成功时,返回0,失败时,返回非0

        //读取m个数据写到file2文件中
        //当读取不出数据时,也就是文件中的数据读取完了之后,就结束循环
        int n = 0;
        if ((n = ReadDataSortToFile(fout, m, file2)) == 0)
            break;
    }

	return 0;
}

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

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

相关文章

SaaS化多租户实现的两种方法

SaaS化多租户实现的两种方法 SaaS系统的定义 SaaS&#xff0c;全称为Software-as-a-Service&#xff08;软件即服务&#xff09;&#xff0c;是一种基于云计算的软件交付模式。而SaaS系统&#xff0c;即是通过这种模式提供给用户的软件系统。即多租户系统&#xff0c;每个租户…

MySQL 日志篇:Redo 文件和自适应检查点

MySQL 的 InnoDB 存储引擎使用 Redo Log 记录事务对数据的更改&#xff0c;以便在系统崩溃恢复时能够重做这些更改&#xff0c;从而保证事务的持久性。对于产生的 Redo Log&#xff0c;InnoDB 存储引擎首先将其写入内存中的 Log Buffer&#xff0c;随后再将 Log Buffer 中的 Re…

力扣337-打家劫舍 III(Java详细题解)

题目链接&#xff1a;337. 打家劫舍 III - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 本体是打家劫舍的一个变形题&#xff0c;希望大家能先做198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09;&#xff0c;并看一下我上题的讲解力扣198-打家劫舍&…

【刷题】Day 3--错误的集合

hello&#xff01;又见面啦~~~ 一道习题&#xff0c;要长脑子了...... 【. - 力扣&#xff08;LeetCode&#xff09;】 【思路】 /*** Note: The returned array must be malloced, assume caller calls free().*/void Bubble_sort(int arr[], int size) {int temp;for (int i…

多速率信号处理-CIC滤波器

基本原理 级联积分梳状滤波器&#xff08;Cascade Intergrator Comb&#xff09;是多速率信号处理中一种十分高效的数字滤波器。CIC滤波器具有低通滤波器的特性&#xff0c;同时具有以下优势&#xff1a; 滤波器系数全为1&#xff0c;设计时不需要存储滤波器系数&#xff0c;…

拖放WORD文件朗读全文

把WORD拖放到tkinter的窗口&#xff0c;就可以朗读整改word文件的内容。 代码&#xff1a; # -*- coding: utf-8 -*- """ Created on Tue Sep 10 17:09:35 2024author: YBK """ import pyttsx3 import comtypes.client import os import tkint…

按包边(边框)尺寸分类异形创意圆形(饼/盘)LED显示屏有哪些种类

在LED显示屏技术日新月异的今天&#xff0c;异形创意圆形&#xff08;饼/盘&#xff09;LED显示屏凭借其独特的形态设计与广泛的应用场景&#xff0c;成为了商业展示、舞台表演、艺术装置以及户外广告等领域的宠儿。其中&#xff0c;按包边&#xff08;边框&#xff09;尺寸的不…

holynix靶机详解

靶机配置 加一个网络适配器&#xff08;网卡&#xff09; 修改MAC地址 00:0C:29:BC:05:DE 原来的网卡设置为桥接&#xff0c;随机生成MAC地址 重启靶机即可扫到靶机IP 主机探测与端口扫描 arp-scan -l 发现开放80端口 nmap -sV -A -T4 192.168.229.153 访问网站 http://1…

OpenAI O1:人工智能推理能力的新里程碑

引言 北京时间9月13日凌晨&#xff0c;OpenAI在没有任何预告的情况下&#xff0c;正式发布了其首款具有推理能力的模型——OpenAI O1。这一模型的发布&#xff0c;不仅标志着人工智能能力的新水平&#xff0c;也预示着AI技术发展的新范式。本文将详细解析OpenAI O1模型的技术特…

【计网】数据链路层:概述之位置|地位|链路|数据链路|帧

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山岗&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ 1. 在OSI体系结构中的位置 1. 位置&#xff1a;数…

每日一练:K个一组翻转链表

25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#x…

时间复杂度计算 递归

我们先拿出 2021 csp-s 程序题中一道看着就头大的程序题&#xff0c;要求分析 solve1 的复杂度。 设 T(n) ⁡ \operatorname{T(n)} T(n) 表示数组长度为 n n n 时的复杂度&#xff08;即 m − h 1 n m-h1n m−h1n&#xff09;。 T ( 1 ) 1 T(1)1 T(1)1&#xff0c;根据…

计算机毕业设计 酷听音乐系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【SQL】百题计划:SQL排序Order by的使用。

简述&#xff1a; 排序函数&#xff1a;Order by&#xff1b;升序 ASC&#xff1b;降序 DESC&#xff1b; 答案&#xff1a; Select distinct author_id as id from Views where author_id viewer_id order by id Asc;

关于华大/小华 HC32F460 在IAR环境中,无法启用FPU 硬件浮点运算单元的解决方案

需求&#xff1a;要使用浮点FFT功能&#xff0c;面开启M4的 FPU功能 问题&#xff1a;无法开启 FPU&#xff0c;如下图所示&#xff1a;此栏为灰色&#xff0c;无法选择 尝试强制增加 __ARMVFP__&#xff1a; 编译出错&#xff0c;无法内链FPU&#xff1a; 解决方案&#xff1…

[000-01-008].第05节:OpenFeign高级特性-日志打印功能

我的后端学习大纲 SpringCloud学习大纲 1、日志打印功能&#xff1a; 1.Feign 提供了日志打印功能&#xff0c;我们可以通过配置来调整日志级别&#xff0c;从而了解 Feign 中 Http 请求的细节&#xff0c;说白了就是对Feign接口的调用情况进行监控和输出 2、日志级别: NONE&…

vue3【实战-组件封装】图文卡片

效果预览 技术要点 图片宽高比固定为 16:9&#xff0c;展示方式为 object-fit: cover通过 v-bind 实现父组件向子组件的批量传参单行文本超长显示省略号 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; title 属性实现鼠标悬浮显示文本完整内容 范例代码 …

HarmonyOS开发之使用Picker(从相册选择图片),并且通过Swiper组件实现图片预览

一&#xff1a;效果图&#xff1a; 二&#xff1a;添加依赖 import picker from ohos.file.picker; 三&#xff1a;创建showDialog showDialog() {AlertDialog.show({message: 从相册选择,alignment: DialogAlignment.Bottom,offset: { dx: 0, dy: -12 },primaryButton: {val…

Java面试、技巧、问题、回复,资源面面观

入门 先了解一下面试流程 复习 Java 基础知识&#xff1a; 温习 Java 编程的核心概念&#xff0c;包括数据类型、变量、循环、数组和面向对象的编程原则。数据结构和算法&#xff1a; 加强您对 Java 编程中使用的基本数据结构和算法的理解。练习编码&#xff1a; 在各种平台上解…

PHP一键约课高效健身智能健身管理系统小程序源码

一键约课&#xff0c;高效健身 —— 智能健身管理系统让健康触手可及 &#x1f3cb;️‍♀️ 告别繁琐&#xff0c;一键开启健身之旅 你还在为每次去健身房前的繁琐预约流程而烦恼吗&#xff1f;现在有了“一键约课高效健身智能健身管理系统”&#xff0c;所有问题都迎刃而解…