一.归并排序
1.1归并排序——递归
主要思路
这是一串数字,在一个数组里面。我们可以第一步把数组均分成两个部分,然后假设两个数组有序我们可以创建一个临时数组去使用,(使用临时数组去存放我们的临时排序好的一个范围然后要记得把排序好的这个临时数组内容拷贝回去)从两个数组的开始进行一个一个比较,小的放在前面放进去之后对应的一部分就向后++。我们就要想怎么可以让我们的这个数组有序?是不是可以把左右两个部分再各自当成一个数组去再一次我们什么内容的操作,如果还是没有分到两个数组都只有一个元素为止!!当每一个数组都分到只有一个元素为止就自己有序,然后在两个单个元素数组去进行比较归并!(注意为什么不可以直接在原来的数组中进行因为有一些数值在我们进行赋值的时候就会被覆盖消失)
过程如下图!
代码实现:
void _merge_sort_d(int *arr , int left , int right ,int *tmp)
{
//考虑递归结束的情况!
//表示数组错位,和一个数组只有一个数的时候不需要递归!
if (left>=right)
{
return;
}
//作为我们左边部分最后的下标
//int mid = (left + right) / 2;
int mid = (left + right) >> 1;
//[left,mid][mid+1,right]
//我们的临时排序的一个过程
_merge_sort_d(arr, left, mid , tmp);
_merge_sort_d(arr, mid+1, right , tmp);
int begin1 = left, end1 = mid;
int begin2 = mid+1, end2 = right;
int index = left;//防止在拷贝的过程中产生位置错误
//当一个数组结束就结束,数组移动结束条件
while (begin1 <= mid && begin2 <= right)
{
if (arr[begin1] < arr[begin2])
{
tmp[index++] = arr[begin1++];
//index++;
//begin1++;//自己向后移动
}
else
{
tmp[index++] = arr[begin2++];
//index++;
//begin2++;//自己向后移动
}
}
//一定有一个数组没有结束,不管是谁下面两个循环只会进入一个
while (begin1 <= mid)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= right)
{
tmp[index++] = arr[begin2++];
}
//拷贝回去
for (int i = left; i <= right; i++)
{
arr[i] = tmp[i];
}
}
void merge_sort_d(int* arr,int n)
{
//1.考虑我们的临时数组去存放我们临时排序的结果
int* tmp = (int*)malloc(sizeof(int)*n);
_merge_sort_d(arr, 0, n - 1, tmp);
free(tmp);
}
1.2.归并排序——优化为非递归。
我们想要对于一个递归优化成非递归化,无非两种方法。第一种我们可以实现通过简单的循环去实现我们递归的一个转换。第二种方法就是通过栈和队列去模拟我们递归的整个过程。
思路如下:
我们递归的思路是从数组最大的情况下,一直向下分割,直到left>=right return 回去;
我们想要通过循环,栈,或者队列的方式去模拟实现递归过程?我们应该使用一个循环使它有序,我们具体应该怎么实现?我们可不可以从最深的地方去向上去有序;相当于模拟了我们递归的归的过程!!我们每一次排序完成之后[0,1][2,3][4,5][6,7]每一个就有序了,然后[0,3][4,7]有序,然后 [0,7]有序了,我们每一次都是把有序的结果放到我们的tmp里面再拷贝回去我们的原来的数组中。如下图:
代码如下:
void _merge_sort_f_D(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
//类似于希尔排序的gap
int gap = 1;
while (gap < n)
{ //最后两个数组排序好了i+=2*gap就进不来了
for (int i = 0; i < n; i += 2*gap)
{
//[i,i+gap-1][i+gap,i+2*gap-1]
//我们每一次的for结束是不同的gap的数值的从前向后的便利
//我们通过改变i去改变我们两个数组的范围然后从前向后就可以把数组归并好
//
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] < arr[begin2])
{
tmp[index++] = arr[begin1++];
//index++;
//begin1++;//自己向后移动
}
else
{
tmp[index++] = arr[begin2++];
//index++;
//begin2++;//自己向后移动
}
}
//一定有一个数组没有结束,不管是谁下面两个循环只会进入一个
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
}
//拷贝回去
for (int i = 0; i < n; i++)
{
arr[i] = tmp[i];
}
gap *= 2;
}
free(tmp);
}
1.3.关于归并排序——非偶数的情况——优化
为什么会有关于非偶数数组的优化呢?
你会发现我们正确的是大小为1,2,4,8,这样大小的数组去相互之间进行归并排序我们的数组范围在循环的时候比较好控制!但是如果我们加入了一个值到我们原来的数组最后会发生什么呢?
在这里有两个情况!
//1.认为它归并过程中右半区间不存在!!
//2. 认为归并过程中它的右半区间存在但是数值不会满足数组正常范围变化
优化后的代码
void _merge_sort_f_D(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
//类似于希尔排序的gap
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2*gap)
{
//[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;
//归并的过程中右半区间不存在
if (begin2 >= n)
{
break;
}
//归并过程中右半区间存在但是比较前面的内容匹配不对,
//进行一个调整
if (end2 >= n)
{
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] < arr[begin2])
{
tmp[index++] = arr[begin1++];
//index++;
//begin1++;//自己向后移动
}
else
{
tmp[index++] = arr[begin2++];
//index++;
//begin2++;//自己向后移动
}
}
//一定有一个数组没有结束,不管是谁下面两个循环只会进入一个
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
//拷贝回去
//这里的拷贝需要注意因为在第二种情况下,
//我们的最后的右边数组的范围需要去调整所以我们的拷贝回去的
//数组范围也需要被调整!!
for (int i = 0; i <=end2 ; i++)
{
arr[i] = tmp[i];
}
}
gap *= 2;
}
free(tmp);
}
二.桶排序
2.1基本思路
就是说我们的一个序列,我们从个位十位百位,第一次按照个位的数值去排序,然后按照十位排序,如果没有对应的位数就用0替代,我们就结束了我们这个排序!(这个排序不是特别重要)
三.计数排序
1.1:主要思路
(他是一种非比较排序!)
1.统计我们每一个数的个数!
前面这个是一种绝对映射
2.使用次数去排序!
1.2有可能的问题。
今天的分享就到这里了,希望可以帮助到大家!