第三节课:8.13:常见排序算法下

第三节课: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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值