目录
一.归并排序的非递归写法
1.思想应用
采用思想:一个数肯定是有序的,就每一个数和另一个数归并成一个含有两个数的有序序列,然后一个含有两个数的有序序列在和另一个含有两个数的有序序列归并成一个含有四个数的有序序列,依次类推,最后整体有序。
- 图示
2.代码基本实现
(1)单趟归并逻辑
- 图示
(2)多趟(循环)的控制条件
① 迭代条件:i+=2*gap
- 图示
② 结束条件:i<n(或i<=n-2*gap)
(3)代码展示
① 单趟逻辑
int* tmp = (int*)malloc(sizeof(int) * n);//开辟一个新数组
if (tmp == NULL)
{
perror("malloc fail");
return;
}
int gap = 1;
for (size_t i = 0;i<n;i+=2*gap)
{
int begin1 = i,end1=i+gap-1;//第一组数据的首尾
int begin2 = i + gap,end2=i+2*gap-1;//第二组数据的首尾
//[begin1,end1][begin2,end2]--归并区间
int j = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
//小数向前调(尾插)
if (a[begin1] < a[begin2])
{
tmp[j++] = a[begin1++];
}
//大数向后调(尾插)
else
{
tmp[j++] = a[begin2++];
}
}
//由于上面的begin1和begin2是分别向后走,一定有一个没走完
//这里控制没走完的继续走
while (begin1 <= end1)
{
tmp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[j++] = a[begin2++];
}
memcpy(a + i, tmp + i, sizeof(int) *2*gap);//每归并两个数就拷贝回去
}
gap *= 2;
- 效果
②整体逻辑
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
int gap = 1;
while (gap < n)//控制多趟
{
for (size_t i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
//[begin1,end1][begin2,end2]--归并区间
int j = begin1;
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) * 2 * gap);//每归并两个数就拷贝回去
}
gap *= 2;
}
free(tmp);
}
- 效果演示
- 存在问题
此样例给的数组数据个数恰好可以通过测试,但是代码本身存在问题导致其并不适用于所有样例,例如:
我们观察一下分割的区间来看一看问题所在:
下面对代码进行改进。
3.优化代码
(1)end1和begin2越界
由于begin1=i,所以可以判断begin1是不可能越界的,那么越界的就可能是end1和begin2。(这里先不考虑begin2不越界而end2越界的情况。)
- 解决方式 :发生越界时直接跳出循环
if (end1 >= n || begin2 >= n)//begin2或end1越界
{
break;
}
(2)begin2不越界而end2越界
- 解决方式:修改end2的值,并且调整拷贝数据的大小
//优化部分
if (end1 >= n || begin2 >= n)//begin2或end1越界
{
break;
}
if (end2 >= n)
{
end2 = n - 1;
}
memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));
- 完整代码
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
int gap = 1;
while (gap < n)
{
for (size_t i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
//[begin1,end1][begin2,end2]--归并区间
int j = begin1;
if (end1 >= n || begin2 >= n)//begin2或end1越界
{
break;
}
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));//每归并两个数就拷贝回去
}
gap *= 2;
}
free(tmp);
}
- 效果
二.计数排序
1.思想应用
思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
操作步骤:
- 统计相同元素出现次数
- 根据统计的结果将序列回收到原来的序列中
2.(直接映射)逻辑图示
3.优点以及局限性
优点:
- 效率极高
- 时间复杂度为O(aN+countN(有一个范围))
局限性:
1.不适合分散的数据,更适合集中的数据
2.不适合浮点数、字符串、结构体数据排序,只适合整数
4.针对分散的数据进行优化
(1)(相对映射)图示解析
(2)代码实现
void CountSort(int* a, int n)
{
int min = a[0], max = a[0];
for (int i = 1; i < n; i++)
{
if (a[i] < min)
min = a[i];
if (a[i] > max)
max = a[i];
}
int range = max - min + 1;//相对映射开的数组大小
int* count = (int*)calloc(range, sizeof(int));
if (count == NULL)
{
printf("calloc fail\n");
return;
}
//统计次数
for (int i = 0; i < n; i++)
{
count[a[i] - min]++;
}
//排序
int i = 0;
for (int j = 0; j < range; j++)
{
while (count[j]--)
{
a[i++] = j + min;//把相对映射还原回去
}
}
}
- 结果