目录
一、归并排序
1、递归实现:
思路:
首先定义tmp指针指向一个与原数组大小一样的空间,作用是存放拷贝过去的值。
然后再将要分解的序列的中间值给算出来,就知道区间了,就可以进行递归分解了,
最后在返回时进行重新排序合并,再将虚拟数组的值拷贝回去就可以了。
void _MergeSort(int* a, int begin ,int end ,int* tmp)
{
if (begin >= end)
return;
int mid = (begin + end) / 2;
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid + 1, end, tmp);
int begin1 = begin, end1 = mid;
int begin2 = mid + 1, end2 = end;
int i = begin;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] <= a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
if (tmp == NULL)
{
perror("malloc fail\n");
return;
}
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
解析:
1、有一个MergeSort函数,创建一个tmp指针指向一个与原数组大小一样的空间,作用是存放拷贝过去的值。
2、_MergeSort是归并排序的核心,分割后的区间[begin,mid][mid+1,end]所以将上述两个进行递归即可。
3、具体合并操作:
首先定义begin1,end1为一个的区间,begin2,end2为另一个区间,然后进行判断大小,越小的那个放在tmp中,最后如果二者只要有一个走完了,就可以进行判断,将未走完的那个数组剩下的元素放在tmp的后面。
4、最后将tmp中的值拷贝回数组a中即可。
整体思路如下:
2、非递归实现:
非递归实现的时候,定义一个gap(这个是归并过程中,每一组数据的个数)
依然需要嵌套循环来完成这个非递归的实现
所以就需要将上述的begin1,end1,begin2,end2改为
因为是取左闭右闭的区间,所以end1需要i+gap后-1(跳过每组数据的个数)
begin2比end1大1,end就需要跳过两个gap所以乘以2。
特别注意!!!!!!!!
这里因为end1,begin2,end2,是加上gap的,当gap较大时就会发生越界访问,所以要对它们进行越界处理。
三种情况的越界:
1、end1,begin2,end2越界,解决方法:不归并了(修正为一个不存在的区间)
2、begin2,end2越界,解决方法:不归并了
3、end2越界,解决方法:修正end2继续归并
上述为修改方法,begin2 = n,end2 = n-1就会使得这个区间不存在
void MergeSortRenN(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail\n");
return;
}
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
int j = i;
if (end1 >= n)
{
end1 = n - 1;
begin2 = n;
end2 = n - 1;
}
else if (begin2 >= n)
{
begin2 = n;
end2 = n - 1;
}
else if (end2 >= n)
{
end2 = n - 1;
}
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 , tmp , sizeof(int) * n);
gap *= 2;
}
free(tmp);
}
二、计数排序:
思路:
遍历一遍原数组,然后统计每一个元素出现的个数放在计数数组中(计数数组的大小通过原数组的end-begin+1来计算)(计数数组的初始化用calloc来初始化,这样可以将数组中的元素全员设置为0方便计数),在通过遍历计数数组中每一个元素出现的次数情况进行排序。
比如:2出现了3次,3出现了2次,5出现了2次......
而有:2,2,2,3,3,5,5......
注意:要使用相对位置映射计数,这样的话可以避免待排序数组较大时,在前面开辟过大的空间造成空间浪费。
局限性:只适合范围集中且范围不大的整形数组,不适合范围较大或者非整型的排序。
优化:计数时,将所记的数减去这个数组中最小的数,来达到相对位置映射,记得在最后加回那个最小的数字即可
void CountSort(int* a, int n)
{
//初始化
int max = a[0];
int min = a[0];
for (int i = 0; i < n ; i++)
{
if (a[i] > max)
{
max = a[i];
}
if (a[i] < min)
{
min = a[i];
}
}
int range = max - min + 1;
int* countiA = (int*)calloc(range, sizeof(int));
if (countiA == NULL)
{
perror("calloc fail");
return;
}
//计数
for (int i = 0; i < n; i++)
{
countiA[a[i] - min]++;
}
//排序
int j = 0;
for (int i = 0; i < range; i++)
{
while (countiA[i]--)
a[j++] = i + min;
}
free(countiA);
}
三、完整代码:
代码的实现:
void _MergeSort(int* a, int begin ,int end ,int* tmp)
{
if (begin >= end)
return;
int mid = (begin + end) / 2;
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid + 1, end, tmp);
//归并
int begin1 = begin, end1 = mid;
int begin2 = mid + 1, end2 = end;
int i = begin;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] <= a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
if (tmp == NULL)
{
perror("malloc fail\n");
return;
}
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
void MergeSortRenN(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail\n");
return;
}
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
int j = i;
if (end1 >= n)
{
end1 = n - 1;
begin2 = n;
end2 = n - 1;
}
else if (begin2 >= n)
{
begin2 = n;
end2 = n - 1;
}
else if (end2 >= n)
{
end2 = n - 1;
}
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, sizeof(int) * (end2 - i + 1));
}
memcpy(a , tmp , sizeof(int) * n);
gap *= 2;
}
free(tmp);
}
void CountSort(int* a, int n)
{
//初始化
int max = a[0];
int min = a[0];
for (int i = 0; i < n ; i++)
{
if (a[i] > max)
{
max = a[i];
}
if (a[i] < min)
{
min = a[i];
}
}
int range = max - min + 1;
int* countiA = (int*)calloc(range, sizeof(int));
if (countiA == NULL)
{
perror("calloc fail");
return;
}
//计数
for (int i = 0; i < n; i++)
{
countiA[a[i] - min]++;
}
//排序
int j = 0;
for (int i = 0; i < range; i++)
{
while (countiA[i]--)
a[j++] = i + min;
}
free(countiA);
测试:
test.c
void MergeSortTest()
{
int a[] = { 10,6,7,1,3,9,4,2,0 ,11,16,14,13};
int n = sizeof(a) / sizeof(a[0]);
Print(a, n);
MergeSort(a, n);
Print(a, n);
}
void MergeSortRenNTest()
{
int a[] = { 10,6,7,1,3,9,4,2,0 ,11,16,14,13 };
int n = sizeof(a) / sizeof(a[0]);
Print(a, n);
MergeSortRenN(a, n);
Print(a, n);
}
void CountSortTest()
{
int a[] = { 10,6,7,1,3,9,4,2,0 ,11,16,14,-13,12,-7,-5 };
int n = sizeof(a) / sizeof(a[0]);
Print(a, n);
CountSort(a, n);
Print(a, n);
}
int main()
{
MergeSortTest();
MergeSortRenNTest();
CountSortTest();
return 0;
}