第三节课:8.13:常见排序算法下
文章目录
一、基数排序(桶排序):
1.特点:
优点:不用做比较就能够进行排序(无比较次数自然也就无交换次数)
对后面学习哈希结构也有重要帮助
缺点:空间开销非常大(需要用到辅助的二维数组)
2.流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-weObbrCk-1630465976341)(第三节课:8.13:常见排序算法下.assets/桶排序流程图.png)]
1.一个循环控制个位,十位,百位放桶的大循环
2.小循环内先个位数放桶
3.按照从上-下;从左-右出桶
3.code:
(1).一开始写代码的时候出现的问题
遇到的问题:结果反复的调试,装桶的步骤没有问题;
接着调试:将元素取出桶放回原数组没有问题;
上一步调试结束后出现了问题:temp[][]出现了两个21;
分析:前面的初始化辅助数组时,只初始化一次,进行下一个十位数装桶时,数组中便有两个21,所以结果出错;
解决问题:在下一个求十位,百位的开始时候将temp【】【】重新初始化;
调试成功!!
void radix_sort(int arr[], int len, int count_wei)//元素的位数(个位数或者十位数或者百位数)且数组的长度不会大于10
{
int temp[10][10] = {};//已经造成不必要的空间浪费了,假设len = 5的话
for (int i = 0; i < 10; ++i)//辅助空间元素初始化为-1
{
for (int j = 0; j < 10; ++j)
{
temp[i][j] = -1;
}
}
int n = 1;
for (int t = 1; t <= count_wei; t++, n*= 10)//把各个位置上的数拆分开来
{
int index = 0;//下标(对应放在哪一列)
for (int i = 0; i < len; ++i)//装桶
{
index = (arr[i] / n) % 10;
temp[i][index] = arr[i];
}
int k = 0;
for (int i = 0; i < 10; ++i)//按上-下;左-右取出桶内数据放回原数组
{
for (int j = 0; j < 10; ++j)
{
//注意:区分二维数组初始化元素和原数组元素应该区分
if (temp[j][i] != -1)
{
arr[k++] = temp[j][i];
}
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s02DZ0wn-1630465976345)(第三节课:8.13:常见排序算法下.assets/image-20210813132646539.png)]
(2).修改后的代码:
void radix_sort(int arr[], int len, int count_wei)//元素的位数(个位数或者十位数或者百位数)且数组的长度不会大于10
{
int temp[10][10] = {};//已经造成不必要的空间浪费了,假设len = 5的话
int n = 1;
for (int t = 1; t <= count_wei; t++, n*= 10)//把各个位置上的数拆分开来
{
int index = 0;//下标(对应放在哪一列)
for (int i = 0; i < 10; ++i)//辅助空间元素初始化为-1
{
for (int j = 0; j < 10; ++j)
{
temp[i][j] = -1;
}
}
for (int i = 0; i < len; ++i)//装桶
{
index = (arr[i] / n) % 10;
temp[i][index] = arr[i];
}
int k = 0;
for (int i = 0; i < 10; ++i)//按上-下;左-右取出桶内数据放回原数组
{
for (int j = 0; j < 10; ++j)
{
//注意:区分二维数组初始化元素和原数组元素应该区分
if (temp[j][i] != -1)
{
arr[k++] = temp[j][i];
}
}
}
}
}
二、归并排序:
1.算法流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bm8qCxzm-1630465976346)(第三节课:8.13:常见排序算法下.assets/归并排序.png)]
1.先进行递归,二分法,左递归
2.左递归完成:跳出该层递归函数,返回上一层递归函数,对一个元素一组的分层进行合并
3.合并完成之后为两个元素一组,该层函数执行完毕,返回上一递归层函数
4.直到把整个mid的左区间的排序完毕
5.再进行右递归,将右边区间的递归完毕
6.将右区间进行合并
7.最后将两半区间合并,排序完成
2.code:
static void _merge_in_arr(int arr[], int left, int mid, int right)//{ 5,4,3,7,6};返回上一级左递归: mid = 0; left = 0; right = 1; 5-4比较;
{ //low = 0; high = 1;
//先定义辅助数组
int length = right - left + 1;
int *pData = new int[length];
memset(pData, 0, sizeof(int) * length);//初始化数组
//开始排序合并
int low = left;
int high = mid + 1;
int pdata_index = 0;
while (low <= mid && high <= right)//mid的左边区间合并完或者mid的右边区间合并完即可
{
while (low <= mid && arr[low] <= arr[high])
{
pData[pdata_index] = arr[low];
pdata_index++;
low++;
}
while (high <= right && arr[high] <= arr[low])
{
pData[pdata_index] = arr[high];
pdata_index++;
high++;
}
}
//到这一步,已经有一个区间的合并完成,还需将剩余的元素放到辅助数组中
//先判断哪里区间的元素合并完成
if (low <= mid) memmove(&pData[pdata_index], &arr[low], sizeof(int)* (mid - low + 1));//左区间没有合并完
if (high <= right) memmove(&pData[pdata_index], &arr[high], sizeof(int)* (right - high + 1));//左区间没有合并完
//所有元素已经全部移动到辅助数组里面;下一步将辅助数据里面的数据放回元素数组
memmove(&arr[left], pData, sizeof(int)* length);//第一个参数是:&arr[left]而不是arr因为有可能是有区间,所以只能是left为起始下标
//用完之后记得释放动态数组的堆区内存
delete[] pData;
}
static void _merge(int arr[], int left, int right)//left和right作用有二:1、控制递归结束2、左右端点,为一个区间,一个区间内进行排序
{
//递归
int mid = left + ((right - left) >> 1);//优先级的问题 //mid防止越界的作用
if (left >= right)
return;
_merge(arr, left, mid);//递归左区间:递归左半部分;这里原来对递归的返回流程理解有误,需注意
_merge(arr, mid + 1, right);//递归右区间; 隐患,返回上一层递归函数时候,总是会执行无效的右递归;在保证正确之后进行修正
//合并
_merge_in_arr(arr, left, mid, right);
}
void merge_sort(int arr[], int len)
{
_merge(arr, 0, len - 1);
}
3.复习知识点:
#include<string.h>
memset(动态数组首地址, 初始化值, 初始化的size = 元素字节长度 * 元素个数)//函数动态数组初始化为一个相同的数值
memmove(新数组的首地址,原数组的起始地址,size)
4.原来递归调用的流程错误:
//一、错误:左半大区间递归完之后,就可以开始合并了;左半大区间合并完之后,再合并右半大区间,最后再合并一次
//实际:如果是八个元素排序:
/*
1.首先左递归函数递归到最深处(左三)返回,左递归函数的上一层(左二);至此为止,区间是第一二两个元素
2.返回后,执行右递归函数,(无效操作),然后执行合并;第二层函数执行完毕;返回左递归函数的上一层(左1),区间是左大半区间的前四个元素
3.但是返回后,执行右递归函数,此时的右递归函数层层深入层层返回后,会将第三四两个元素合并排序,然后返回右递归(右0),
4.接着执行前四个元素的合并,合并完成之后,返回左递归函数的上一层(左0)
5.至此为止,左大半区间执行完毕,接着执行右大半区间同样的流程
*/
//二、中间流程复杂,需要打断点调试
//三、易错:
memmove(&arr[left], pData, sizeof(int)* length);//第一个参数是:&arr[left]而不是arr因为有可能是有区间,所以只能是left为起始下标
三、学习算法的方法:
把算法的流程先弄明白
再去写code