【针对性复习】选择排序和归并排序

之前相关内容的博客,只有简略的思路和代码,起不到复习的作用
现在把思路整理清楚,过程图示也画出来,再把代码重新写一遍,争取把排序遗留的问题彻底解决


选择排序的思路:

从头到尾遍历,找最大的元素的下标,找到之后把最大的元素和最后一个位置的元素交换

易错点:

  • max作为保存最大元素下标的存在,每次使用完应该清零
  • 找到值去交换,最大的元素位置是没有发生变化的

过程图示:

在这里插入图片描述

代码示例:

//普通的未经过优化的选择排序
void selectSort(int* array, int size)
{
	int end = size - 1;

	while (end != 0)
	{
		int max = 0;
		int begin =0;

		for (begin; begin <= end; ++begin)
		{
			if (array[max] < array[begin])
			{
				max = begin;
			}
			continue;
		}

		swap(array[max], array[end]);
		end--;
	}
}

优化后的选择排序:

将一次选择一个最大值变为一次选择一个最大值和最小值

最大值放在最后面的end位置,最小值放在最前面的begin位置

普通选择排序缺陷:

  • 存在大量的重复比较

易错点:

  • index的值每次遍历查找前要重置
  • minpos和maxpos使用完之后的值也要重置

过程图示:

在这里插入图片描述

代码示例:

void selectSortD(int* array, int size)
{
	int begin = 0;
	int end = size - 1;

	while (begin < end)
	{
		int index = begin + 1;
		int minpos = begin;
		int maxpos = begin;

		for (index; index <= end; ++index)
		{
			if (array[index]>array[maxpos])
			{
				maxpos = index;
			}

			if (array[index] < array[minpos])
			{
				minpos = index;
			}
		}

		//如果minpos在最后的位置,把minpos的位置与maxpos互换即可
		if (minpos == end)
		{
			minpos = maxpos;
		}

		swap(array[begin], array[minpos]);

		swap(array[end], array[maxpos]);

		begin++;
		end--;
	}
}

归并排序(递归)的思路:

对一组数据,一直均分到一组只有一个元素,然后将其归并,逐步变为有序

易错点:

  • 只有元素有序才可以归并
  • begin2是以mid开始,如果以mid+1开始,会越界
  • 先处理左半部分,在处理右半部分,都有序之后,归并,写回原数组
  • 归并的时候申请辅助空间如果是在函数里,函数调用结束会释放,所以使用参数传递进去
  • 空间只需要一份,所以不能当作mergeData的参数,因为递归进来每次都会创建

归并数据过程图示:

在这里插入图片描述
长短不一时,一方写完,另一方还未完,直接把另一方直接写入到后面

归并数据思路:

  • 使用两个指针来依次对比左半部分和右半部分的第一个元素,先把小的放进去

  • 放进去的那部分指针后移,继续比较,一直比到放完为止,长度不一致的情况循环结束单独处理

void mergeData(int* array,int left,int mid,int right,int* tmp)
{
	int begin1 = left;
	int end1 = mid;
	int begin2 = mid;
	int end2 = right;

	int index = left;

	while (begin1 < end1 && begin2<end2)
	{
		if (array[begin1] < array[begin2])
		{
			tmp[index] = array[begin1];
			begin1++;
			index++;
		}
		else
		{
			tmp[index] = array[begin2];
			begin2++;
			index++;
		}
	}

	//处理长度不一致的情况

	while (begin1 < end1)
	{
		tmp[index] = array[begin1];
		begin1++;
		index++;
	}

	while (begin2<end2)
	{
		tmp[index] = array[begin2];
		begin2++;
		index++;
	}
}

为了方便调用,进行一下封装,其实不封装也无所谓,那时候还没学C++所以用malloc,现在自然用new

void mergeSort(int* array, int size)
{
	int* tmp = new int[size];

	_mergeSort(array, 0, size, tmp);

	delete[] tmp;
}

说真的,我不是很喜欢递归的归并排序代码,我觉得memcpy那里需要加left真的很难理解

void _mergeSort(int* array, int left, int right,int* tmp)
{
	if (right - left > 1)
	{
		int mid = left + ((right - left) >> 1);

		_mergeSort(array, left, mid, tmp);
		_mergeSort(array, mid, right, tmp);

		mergeData(array, left, mid, right, tmp);

		memcpy(array + left, tmp + left, sizeof(int)*(right - left));

	}
}

不加left每次拷贝都会拷贝到左半部分,右半部分永远是空的
在这里插入图片描述

  • 0-5这一整体,分组0-1 2-5,处理右边的部分的时候left是2,如果处理完了往回拷贝的时候不加left
  • array默认指向数组第一个元素,等于2-5这边的数据没有接在前两个元素后面,而是有两个把之前的左半部分覆盖了

归并排序(递归)过程图示:

其实应该34一组,291一组,但是看思路就行了,这里画的严格按照代码来说是有问题的
在这里插入图片描述


归并排序(循环)的思路:

与递归不同的是,循环不需要先分组了,因为每个元素都是单独的,可以看作已经分好组了,直接开始归并

易错点:

  • 需要在循环里手动赋予left的值
  • i应该+=2*gap
  • gap可以看作一组元素的个数,所以left,mid,right的值用gap表示
  • mid和right有可能会超出size的范围造成越界,需要单独处理

在这里插入图片描述
在这里插入图片描述

归并排序(循环)过程图示:

在这里插入图片描述

归并排序(循环)代码:

void mergeSortN(int* array, int size)
{
	int* tmp = new int[size];

	int gap = 1;

	while (gap < size)
	{
		for (int i = 0; i < size; i += 2 * gap)
		{
			int left = i;
			int mid = left + gap;
			int right = mid + gap;

			if (mid > size)
			{
				mid = size;
			}

			if (right > size)
			{
				right = size;
			}

			mergeData(array, left, mid, right, tmp);
		}

		memcpy(array, tmp, sizeof(int)*size);

		gap *= 2;
	}

	delete[] tmp;
}

至此,排序所有的内容基本都搞清楚了,短时间也不太可能会忘,忘了再过来看看就行了,开始新的篇章~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值