数据结构与算法 第八天常见排序+冒泡排序+快速排序+文件IO+大数据排序+文件合并

如果你真的看不懂,那就死记硬背,如果你不想背,那就珍藏吧

第一章 冒泡排序

在这里插入图片描述
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

【1】Bubble_Sort.c

/*
 * @Author: your name
 * @Date: 2021-09-03 14:09:13
 * @LastEditTime: 2021-09-03 15:35:44
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \Desktop\bb.c
 */
#include <stdio.h>
#include <time.h>
#include <stdlib.h>


#define SIZE    10000

//冒泡排序   从小到大
void bubble_sort(int *arr, int len)//100
{
    
    
    int sort_count, pos;
    int tmp;

    //排序多少次:sort_count:计数已经排好了几个数据,len-1:最后一个不用排序
    for(sort_count=0; sort_count<len-1; sort_count++)
    {
    
    
        //pos:从哪个位置开始比较,len-sort_count-1:总的数据长度-已经排好的数据个数-1(这个-1是因为pos下面的操作中会访问到pos+1)
        for(pos=0; pos<len-sort_count-1; pos++)
        {
    
    
            if(arr[pos] > arr[pos+1])//前面的数据
            {
    
    
                //交换数据
                tmp = arr[pos];
                arr[pos] = arr[pos+1];
                arr[pos+1] = tmp;
            }
        }

    }

}

int main(void)
{
    
    
    int *array = malloc(SIZE*sizeof(int));
    int i;

    srand(time(NULL));//随机数种子

    for(i=0; i<SIZE; i++)
    {
    
    
        array[i] = rand()%SIZE;//开始种子
    }

    bubble_sort(array, SIZE);//排序函数


    for(i=0; i<SIZE; i++)//打印
    {
    
    
        printf("%d ", array[i]);
    }

    printf("\n");

    return 0;
}

第二章 快速排序

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
步骤为:
挑选基准值:从数列中挑出一个元素,称为"基准"(pivot);

分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;

递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。
选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。
在这里插入图片描述

【1】quick_sort.c

/*
 * @Author: your name
 * @Date: 2021-09-03 15:21:41
 * @LastEditTime: 2021-09-03 15:34:54
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \Desktop\quick.c
 */
/**********************************************
 *  File Name: quick.c
 *  Created  @ 2016-08-03 21:20
 *  Author   : Gec
 *  E-mail   : [email protected]
 **********************************************/
#include <stdio.h>
#include <time.h>
#include <stdlib.h>


#define SIZE    100000

void quick(int arr[],  int left, int right)//left=0   right=100000-1
{
    
    
	int i;
	
	if(left < right)
	{
    
    
			int tmp = arr[left];//第一个值给临时变量,腾出第一个位置
        	int low = left;//低位0
        	int high = right;//高位99999
			
			//快速排序交替比较
        	while(low < high)//循环在左下标小于右下标的基础上
    		{
    
    
//从右到左比较,实时判断左下标有没有小于右下标的值,并且右值有没有小于中间的交换值
                while(low < high && arr[high] >= tmp)
				{
    
    
                	high--;//直到减到不满足条件,这个值arr[high]要么==tmp  or<tmp 
                }//当跳出循环时,也就是第一位:后面的数字都和第一位一一比较过了
				//证明,第一位真的是最小的了,此时就该记录一下,

            	arr[low] = arr[high];//把右值放到腾出来的空间中

//从左到右比较,实时判断左下标有没有大于或者等于右下标的值,并且右值有没有小于中间的交换值
                while(low < high && arr[low] < tmp)//注意,此时high值上面已经改变了
				{
    
    
                	low++;
                }

                arr[high] = arr[low];

			}

//最后剩下的位置,就是空出来的那个,然后就排序好了
        arr[low] = tmp;
		
		quick(arr,left,low-1);//左
		quick(arr,low+1,right);//右
		
    }
}

int main(int argc, char **argv)
{
    
    
	
	int *array = malloc(SIZE*sizeof(int));
    int i;

    srand(time(NULL));//种子

    for(i=0; i<SIZE; i++)
    {
    
    
        array[i] = rand()%SIZE;//产生随机数
    }
	
	quick(array, 0, SIZE-1);// 0--8 是下标
	

	for(i=0; i<SIZE; i++)//打印
    {
    
    
        printf("%d ", array[i]);
    }

    printf("\n");


	return 0;
}


第三章 大数据排序

这里需要运用文件io的方法

【1】calloc

calloc函数的功能与malloc函数的功能相似,都是从堆分配内存。其函数声明如下:
void *calloc(int n,int size);
参数释义:
size:每块多少大小
n:申请的块数
注意:最后申请空间大小为: n和size相乘

【2】malloc

malloc函数可以从堆上获得指定字节的内存空间,其函数声明如下:
void * malloc(int n);
参数释义:
n:申请空间大小(单个类型大小*总个数)

【3】readlloc

适用情况一般是mallloc申请的大小不够用了,需要扩充
原型:extern void *realloc(void mem_address, unsigned int newsize);
语法:指针名=(数据类型
)realloc(要改变内存大小的指针名,新的大小)。
//新的大小若小于原来的大小,原数据的末尾可能丢失(被其他使用内存的数据覆盖等)
头文件:#include <stdlib.h> 有些编译器需要#include <malloc.h>

【4】文件IO操作

【4.1】fprintf

【4.2】fscanf

【4.3】fnprintf

// 打开一个包含百万数据级别的文件
    FILE *src = fopen("numbers.txt", "r");//只读方式打开
    if(src == NULL)
    {
    
    
        perror("打开文件失败");
        exit(0);//结束程序
    }

int fscanf ( FILE *fp, char * format, … );
int fprintf ( FILE *fp, char * format, … );

输入文件操作:src是文件描述符
%u:是从文件取的格式,显然是unsigned int类型
&data[i]:数组地址
返回值:失败返回EOF=-1
fscanf(src, "%u", &data[i])

原型:int snprintf(char *str, size_t size, const char *format, …);

描述:The functions snprintf() write at most size bytes (including the terminating null byte (‘\0’)) to str. 即snprintf这个函数按照format格式最多将size个字符写入到str中,其中size个字符已经包括了结束符。

【4.4】rename 和remove函数

rename 命令通过字符串替换的方式批量修改文件名。
格式:rename from to file
from : 表示需要替换或需要处理的字符,一般是文件名称的一部分,
to :将 from 内容,替换成 to 的内容。
file :待处理的文件。
remove(f1);//删除f1文件描述符
/*
 * @Author: your name
 * @Date: 2021-09-03 15:43:04
 * @LastEditTime: 2021-09-03 15:45:06
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \Desktop\big_data_sort.c
 */
#include <stdio.h>
#include <stdbool.h>

//交换两个数
void swap(int *a, int *b)//当参数是指针时,你对a,b所有的操作都会保留值,而且不用return 形式
{
    
    
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

// 融合begin到end的所有文件,并入文件begin=1
void merge(int begin, int end)//end=20
{
    
    
    if(end-begin <= 0)
        return;

    // 融合begin+1到end的所有文件,并入文件begin+1
    merge(begin+1, end);//递归自己


    // 合并begin和begin+1
    char f1[10], f2[10];
    bzero(f1, 10);
    bzero(f2, 10);
	//将begin.txt个文件存入fx
    snprintf(f1, 10, "%d.txt", begin);
    snprintf(f2, 10, "%d.txt", begin+1);

    //printf("正在合并%s和%s...\n", f1, f2);

    FILE *fp1 = fopen(f1, "r");
    FILE *fp2 = fopen(f2, "r");

    unsigned n1, n2;
    if(fscanf(fp1, "%u", &n1) == EOF)
    {
    
    
        fclose(fp1);
        fclose(fp2);

        remove(f1);//删除
		
        rename(f2, f1);//重命名f2为f1
        return;
    }
    if(fscanf(fp2, "%u", &n2) == EOF)
    {
    
    
        fclose(fp1);
        fclose(fp2);

        remove(f2);
        return;
    }

    // 临时存放合并的数据,最后更名为begin
    FILE *tmp = fopen("tmp.txt", "w+");//读写方式打开文件
    int fp1_done = false;
    int fp2_done = false;
    while(1)
    {
    
    
        if(n1 < n2)//两个文件描述符的比较
        {
    
    
            fprintf(tmp, "%u\n", n1);//tmp文件描述符=n1(fp1)文件内容
            if(fscanf(fp1, "%u", &n1) == EOF)//
            {
    
    
                fprintf(tmp, "%u\n", n2);
                fp1_done = true;
                break;
            }
        }
        else
        {
    
    
            fprintf(tmp, "%u\n", n2);
            if(fscanf(fp2, "%u", &n2) == EOF)
            {
    
    
                fprintf(tmp, "%u\n", n1);
                fp2_done = true;
                break;
            }
        }
    }

    // 将剩余的数据置入tmp中
    if(fp1_done)
    {
    
    
        while(fscanf(fp2, "%u", &n2) != EOF)
        {
    
    
            fprintf(tmp, "%u\n", n2);
        }
    }
    else
    {
    
    
        while(fscanf(fp1, "%u", &n1) != EOF)
        {
    
    
            fprintf(tmp, "%u\n", n1);
        }
    }

    // 删除原有文件,并将tmp更名为begin
    fclose(fp1);
    fclose(fp2);

    if(remove(f1) == -1)
    {
    
    
        printf("删除文件%s失败:%s\n", f1, strerror(errno));
        exit(0);
    }
    if(remove(f2) == -1)
    {
    
    
        printf("删除文件%s失败:%s\n", f2, strerror(errno));
        exit(0);
    }

    fclose(tmp);
    rename("tmp.txt", f1);
}

int main(int argc, char **argv)
{
    
    
    // 打开一个包含百万数据级别的文件
    FILE *src = fopen("numbers.txt", "r");//只读方式打开
    if(src == NULL)
    {
    
    
        perror("打开文件失败");
        exit(0);//结束程序
    }

    // 1,将原始数据文件,分割成N个有序的子文件
    bool done = false;
    char file[20];
    int N = 0;
    int wanted = 10*10000; // 假设每次只能读取10万个数据
    int infact = wanted;//狂铁:10万伏特
    while(1)
    {
    
    
        // 试图从文件读取 wanted 个数据
        unsigned *data = calloc(wanted, sizeof(unsigned));//申请10万块,每块4字节
        for(int i=0; i<wanted; i++)
        {
    
    
            if(fscanf(src, "%u", &data[i]) == EOF)//从文件numbers.txt取出无符号类型的数据存入data数组
            {
    
    
                done = true;
                infact = i;
                break;
            }
        }

        // 对这读到的 infact 个数据进行排序
        quickSort(data, infact);//快速排序


        // 创建临时文件,存放部分数据
        bzero(file, 20);
		//file数组名,20数组大小,格式,存入数据  举例:file[0]=0.txt
        snprintf(file, 20, "%d.txt", ++N);//用来装文件名,最多存20个.txt的文件名
		
        FILE *fp = fopen(file, "w");//只写方式

        // 将排好序的部分数据写入临时文件,保存起来
        for(int i=0; i<infact; i++)
        {
    
    
            fprintf(fp, "%u\n", data[i]);
        }
        free(data);
        fclose(fp);

        if(done)
            break;
    }
    fclose(src);

    // 2,将N个有序子文件,合并成一个有序文件
    merge(1, N);

    return 0;
}

创作真的不容易,但能帮助人,腰酸一点又有什么关系
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_45463480/article/details/124516648