数据结构与算法 第八天常见排序+冒泡排序+快速排序+文件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 : 2034294993@qq.com
**********************************************/
#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;
}
创作真的不容易,但能帮助人,腰酸一点又有什么关系