归并排序来说更适用于外存(磁盘)排序,也可以用作内存排序
分解是递归的过程:相当于二叉树的后续遍历,递归往回退是归并
归并排序
void MergeArr(int* a, int begin1, int end1, int begin2, int end2, int* tmp)
{
int left = begin1, right = end2;
int index = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
tmp[index++] = a[begin1++];
else
tmp[index++] = a[begin2++];
}
//此时说明有一个组已经走完了
while (begin1 <= end1)
tmp[index++] = a[begin1++];
while (begin2 <= end2)
tmp[index++] = a[begin2++];
// 把归并好的再tmp的数据在拷贝回到原数组
for (int i = left; i <= right; ++i)
a[i] = tmp[i];
}
void _MergeSort(int* a, int left, int right, int* tmp)
{
if (left >= right)
return;
int mid = (left + right) / 2;
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
// 归并[left,mid][mid+1,right]有序
MergeArr(a, left, mid, mid + 1, right, tmp);
}
// 归并排序递归实现
void MergeSort(int* a, int n)
{
assert(a);
int* tmp = malloc(sizeof(int)*n);
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
int main()
{int a[] = { 9,1,2,5,7,4,8,6,3,5 };
MergeSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
return 0;
}
归并排序非递归实现
void MergeSortNonR(int* a, int n)
{
assert(a);
int* tmp = malloc(sizeof(int)*n);
int gap = 1;
while (gap < n)//这层循环控制gap,gap决定几个数据合并
{
for (int i = 0; i < n; i += 2 * gap)//这层循环控制i
{
// [i,i+gap-1] [i+gap, i+2*gap-1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i+gap, end2 = i + 2 * gap - 1;
// 1、合并时只有第一组,第二组不存在,就不需要合并
if (begin2 >= n)
break;
// 2、合并时第二组只有部分数据,需要修正end2边界
if (end2 >= n)
end2 = n - 1;
MergeArr(a, begin1, end1, begin2, end2, tmp);
}
//PrintArray(a, n);
gap *= 2;
}
free(tmp);
}
每个点都要递归,n个点,高度:logn
文件中有10亿个数据需要排序
假设内存中最多只能放1000w个数据。10亿个数组读出来,切分成100份
大文件平均分割成N份,保证每份的大小可以加载到内存,把每个小文件加载到内存中,使用快排排成有序再写回小文件。那么这时我们就文件中归并的先决条件
读写文件时,文件指针是自己往后+1的,EOF=-1=真,
void _MergeFile (const char* file1,const char* file2,{
FILE*fout1 = fopen(filel,"r");
if (foutl == NULL)
{
printf("打开文件朱败\n");exit(-1);
)
FILE*fout2 = fopen(fi1e2,"r") ;if (fout2 == NULL)
{
printf("打开文件朱败\n") ;exit(-1);
)
FILE fin = fopen(mfi1e,"w");if (fin == NULL)
{
printf("打开文件朱败\n") ;exit(-1);
}
void MergeSortFile(const char* file)
{
FILE* fout = fopen(file, "r");
if (fout == NULL)
{
printf("打开文件失败\n");
exit(-1);
}
int num1,num2;
int ret1 = fscanf(fout1,"%din", &num1) ;
int ret2 = fscanf(fout2,"%din", &num2) ;
while (ret1 != EOF && ret2 != EOF)
{
if (numl < num2){
fprintf(fin,"%d\n",num1) ;
ret1 = fscanf(fout1,"%d\n",&num1) ;
}
else{
fprintf(fin,"%din", num2) ;
ret2 = fscanf(fout2,"%d\n",&num2) ;
}
}
while (ret1 != EOF)
fprintf(fin,"%d\n", num1) ;
ret1 = fscanf(fout1,"%d\n",&num1) ;
}
while (ret2 != EOF){
fprintf(fin,“%din",num2) ;
ret2 = fscanf(fout2,"%dn",&num2) ;
}
fclose(fout1) ;fclose(fout2) ;fclose(fin);
}
// 分割成一段一段数据,内存排序后写到小文件,
int n = 10;//切10份,n个文件需要被归并
int a[10];//10个数据1份
int i = 0;
int num = 0;//读到整形int里
char subfile[20];
int filei = 1;memset(a,0,sizeof(int)*n) ;
while (fscanf(fout,"%d\n", &num) != EOF)
{//scanf:从控制台里读; fscanf:从文件里读数据
if (i < n)
{
a[i++] = num;
}
else
{
a[i] = num;
QuickSort(a,0, n - 1) ;
sprintf(subfile,"%d", filei++);
FILE*fin = fopen(subfile,"w") ;if (fin == NULL)
{
printf("打开文件太败\n") ;exit(-1);
}
for (int i = 0; i < n; i++){
fprintf(fin,"%d\n", a[i]);
}
fcilose(fin) ;
i = 0;
memset(a,0,sizeof (int)*n) ;
}
}
char mfile[100]="12";
char file1[100] = "1";
char file2[100] = "2";
for (int i = 2; i <= n; ++i){
//读取file1和file2,进行归并出
mfile_MergeFile(file1,file2,mfile) ;
strcpy(file1,mfile) ;
sprintf(fi1e2,"%d",i+1) ;
sprintf(mfile,"%s%d",mfi1e,i+1) ;
}
fclose(fout);
}
计数排序
时间复杂度O(N+range),空间复杂度O(range),只适用于整形,如果浮点数或者字符串排序,还得用比较排序
void CountSort(int*a, int n)
{ assert(a);
int min = a[0]; int max = a[0];
for (int i = 1; i < n; ++i)
{
if (a[i] > max)
max = a[i];
if (a[i] < min)
min = a[i];
}
int range = max - min + 1;
int* countArr = (int*)malloc(sizeof(int)*range);
memset(countArr, 0, sizeof(int)*range);
//统计次数
for (int i = 0; i < n; ++i){
countArr[a[i] - min]++;
}//排序
int index = 0;
for (int j = 0; j < range; ++j)
{
while (countArr[j]--){//相对位置
a[index++] = j + min;
}
}
free(countArr);
}
int main()
{ int a[] = { 9,1,2,5,7,4,8,6,3,5 };
CountSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
return 0;
}
稳定性
数组中相同值,排完序相对顺序可以做到不变就是稳定的,否则就不稳定
选择排序:把min往左边换时,可能会影响其他数,导致不稳定
任何一个稳定的排序都可以实现为不稳定的排序但是如果它本身就是一个不稳定的排序﹑不可能实现为一个稳定的排序