前言:在前面我们讲了各种常见的排序,今天我们就来对排序部分收个尾,再来对归并排序通过递归和非递归的方法进行实现,与对计数排序进行简单的学习。
💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:数据结构 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤。
代码实现:
void _MergeSort(int* a, int begin,int end,int* tmp)//归并排序
{
if (begin >= end)
return;
int mid = (begin + end) / 2;
_MergeSort(a, begin, mid, tmp);//分为左半边 a[begin] | a[mid]
_MergeSort(a, mid + 1, end, tmp);
int begin1 = begin;
int begin2 = mid + 1;
int i = begin1;
int end1 = mid;
int end2 = end;
while (begin1 <= end1 && begin2 <= end)//排序
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}//此时可能出现没有走完的情况
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end)
{
tmp[i++] = a[begin2++];
}
memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));//拷贝回去
}
void MergeSort(int* a, int n)
{
assert(a);
int* tmp = (int*)malloc(sizeof(int) * n);//开辟临时空间
if (tmp == NULL)
{
printf("malloc fail\n");
exit(-1);
}
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
函数测试:
void Test_MergeSort()
{
int a[] = { 1,2,30,0,99,1,7,8,2,11,0,3,13 };
MergeSort(a, sizeof(a) / sizeof(a[0]));
PrintArray(a, sizeof(a) / sizeof(a[0]));
}
int main()
{
Test_MergeSort();
}
运行结果:
归并排序的非递归实现是非常复杂的一个算法,在快速排序中我们使用栈来存储待排数组的左右区间,模拟递归的过程,然而在归并排序中我们无非用栈来实现,因为我们无非将分解后的区间在通过栈来合并,因此我们在归并排序中不能这么实现。
实现思路:
代码实现:
void MergeSortNoR(int* a, int n)//归并排序 -- 非递归
{
int* tmp = (int*)malloc( n * sizeof(int));
int gap = 1;
while (gap < n)
{
int i = 0;
for (i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;//第一个数组的区间
int begin2 = i + gap, end2 = i + 2 * gap - 1;//此时第二个数组的区间
if (end1 >= n)
break;
if (begin2 >= n)
break;
if (end2 >= n)
end2 = n - 1;
int j = i;//此时合并数组
while (begin1 <= end1 && begin2 <= end2)//排序
{
if (a[begin1] <= a[begin2])
{
tmp[j++] = a[begin1++];
}
else
{
tmp[j++] = a[begin2++];
}
}//此时可能出现没有走完的情况
while (begin1 <= end1)
{
tmp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[j++] = a[begin2++];
}
memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));//拷贝回去
}
gap *= 2;
}
}
函数测试:
void Test_MergeSortNoR()
{
int a[] = { 1,2,30,0,99,1,7,8,2,3 };
MergeSortNoR(a, sizeof(a) / sizeof(a[0]));
PrintArray(a, sizeof(a) / sizeof(a[0]));
}
int main()
{
Test_MergeSortNoR();
return 0;
}
运行结果:
计数排序是一种非比较排序算法,它的基本思想是统计每个元素在待排序序列中出现的次数,然后依次按照元素值的大小,将元素按照出现的次数放入有序序列中。计数排序是一种稳定排序算法,但是它只适用于元素取值范围较小且分布较均匀的情况
统计元素出现的次数:遍历待排序序列,统计每个元素出现的次数,并记录在一个辅助数组中(通常辅助数组的大小为最大元素的大小)。
计算元素的位置:根据元素出现次数的累加值,计算出每个元素在有序序列中的位置。
将元素放入有序序列中:根据元素的位置,将元素依次放入有序序列中。
将辅助数组中计数不为0的数依次传给原数组,此时即可完成排序.
代码实现:
void CountSort(int* a,int n)//计数排序
{
int max = a[0];
int min = a[0];
int i = 0;
for (i = 0; i < n; i++)
{
if (max < a[i])
max = a[i];
if (min > a[i])
min = a[i];
}
int* tmp = (int*)calloc(max+1 ,4);
if (tmp == NULL)//如果空间申请失败,报个错,并中止程序
{
perror("calloc");
return;
}
for (i = 0; i < n; i++)
{
tmp[a[i]]++;
}
int j = 0;
for (i = 0; i < max + 1; i++)
{
while (tmp[i])
{
a[j++] = i;
tmp[i]--;
}
}
}
函数测试:
void Test_CountSort()
{
int a[] = { 1,0,0,2,333,3,0,99 };
CountSort(a, sizeof(a) / sizeof(a[0]));
PrintArray(a, sizeof(a) / sizeof(a[0]));
}
int main()
{
Test_CountSort();
}
运行结果:
我们思考一下,如果数组每次都是开辟最大的元素的个数的时候是不是会照成空间上的一种浪费?并且如果我们需要存储有负数的时候我们又该怎么办?解决办法
代码优化实现:
void CountSort(int* a,int n)//计数排序
{
int max = a[0];
int min = a[0];
int i = 0;
for (i = 0; i < n; i++)
{
if (max < a[i])
max = a[i];
if (min > a[i])
min = a[i];
}
int range = max - min + 1;
int* tmp = (int*)calloc(range ,4);
if (tmp == NULL)//如果空间申请失败,报个错,并中止程序
{
perror("calloc");
return;
}
for (i = 0; i < n; i++)
{
tmp[a[i] - min]++;
}
int j = 0;
for (i = 0; i < range; i++)
{
while (tmp[i])
{
a[j++] = i + min;
tmp[i]--;
}
}
}
函数测试:
void Test_CountSort()
{
int a[] = { 1,-1,-3,-10,0,2,333,3,0,-3,99 };
CountSort(a, sizeof(a) / sizeof(a[0]));
PrintArray(a, sizeof(a) / sizeof(a[0]));
}
运行结果
结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。