保姆级讲解——快速排序和归并排序

前言

关于快速排序和归并排序的思路,讲得好的那可多了,但是在我自己的学习过程中遇到的问题并不是弄不清思路,而是一敲代码就瞬间不会,看别人写好的吧,还该死的看不懂【哭泣】
所以!!出现了这篇保姆级讲解(妈妈再也不怕我看不懂了嘿嘿)

快速排序

思路

虽说思路讲得好的大有人在,但大家的思路还是有一点点小不同的。所以呢,思路这玩意儿该提还是要提一下的。

  • 大致思路如下
  1. 在每一段小数组中,设最左边的数为基数(pivot)。令j从右往左遍历,找到第一个小于基数的数,停下。再令i从左遍历,找到第一个大于基数的数,停下。(注意一定要j先开始遍历哦)交换i和j指向的数,继续向中间遍历、寻找、交换,直到i和j相遇。

  2. 交换相遇处的数和最左边(基点),以基点为界,把左右分成两个小数组(不包括基点哦),再次传入函数,遍历、寻找、交换(递归思想就是这个啦)。直到小数组只剩下一个数,结束递归。

  3. 然后!数组就很神奇地,有序了!!

(其实我理解的快速排序,就是每一次函数的调用,都为该轮pivot找到正确位置的过程)

  • 关于形参和实参问题,因为传入函数的是数组名(也就是数组首地址),所以在对数组进行改动的时候是直接在原数组地址上进行的,不用担心修改的时候,改的是形参而不是实参了。

代码部分

void quick_sort(int* a, int left, int right) {
	int i = left, j = right;
	//left和right其实就是框定了本轮要在原数组上改动的范围
	int pivot = *(a + left);   //基点
	int t = 0;                 //t用于交换数
	if (left >= right)  return;//递归退出的条件,即当本轮要修改的只剩下一个数的时候退出
	while (i < j) {
		while (a[j] >= pivot && i<j)  j--;
		while (a[i] <= pivot && i<j)  i++;
		//到这个地方,有两种可能性
		//第一种,i和j都找到了要交换的数(即i<j),那么交换i和j指向的数
		//第二种,i和j在找到数之前相遇了,则要将pivot换到中间。这个时候,pivot的位置就是最后有序时,pivot这个数应该在的位置(即为pivot找到正确位置)
		if (i < j) {
			//如果是因为两个都找到才退出循环,那么交换两个数据
			t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}
	//把pivot移到正确位置
	a[left] = a[i];
	a[i] = pivot;
	//把pivot两边的数,再次传入函数进行排序(不含pivot哦)
	quick_sort(a, left, i - 1);
	quick_sort(a, i + 1, right);
}

归并排序

思路

是一个结合了递归和分治思想的玩意儿。通过递归把一个待排序的数组分成只有一个数字的小数组,再通过类似合并有序数列的方法,一层一层的结合,最后合成一个有序数组。

在整个过程中,实际上并没有新数组的形成,所谓“分”“合成”,都是通过传递和改变left和right两个变量做到的,和快速排序类似,left和right都是框定了一个对原数组进行改动的范围。

  1. 先递归,一层一层地分成若干个小数组,递归的结束条件是小数组里只有一个元素。
  2. 然后将前一层的两个有序数组合并,存放至临时数组中,待两个有序数组合并完成,将其按顺序复制到原数组的对应位置,这样原数组的对应位置上的元素就有序了。
  3. 通过每一次调用修改原数组不同范围上的数,范围逐步扩大,最后整个数组有序。

(如果觉得我思路讲不明白,可以移步这个视频:排序算法:归并排序【图解+代码】

代码部分

void merge_sort(int a[],int left,int right){
		//这个函数其实就是一个中转站
		//在分的时候,接收上一层的数,对半分了往下传
		//在合并过程,接收排序完成的信息然后关闭本层
		//至于原数组怎么修改的,就是merge函数建立临时数组,排好序后,直接将排好序的数覆盖到原数组对应位置上
	if(left<right){//如果只剩一个数,就不用再分,也不用接收,直接把分好的信息往回传
		int mid = (left + right) / 2;//为了砍半做标记
		//这两个是分的过程
		merge_sort(a,left, mid);
		merge_sort(a, mid + 1, right);
		//接收下层治完成的信息,关闭本层
		merge(a, left, right, mid);
	}
}
void merge(int a[],int left,int right,int mid) {
	//left 和 right是左右边界,指在原数组的left到right位进行排序覆盖
	//mid是两个待排序数组的分界线,从left和mid右边第一个开始合并有序数组
	int s[100];//s是临时数组
	int i = left, j = mid + 1;//i,j分别指向代插数组的第一个
	int sor = left;//sor是指向临时数组待放入的位置
	while (i <= mid && j <= right) {
		if (a[i] < a[j]) {
			s[sor++] = a[i++];
		}
		else {
			s[sor++] = a[j++];
		}
	}
	while (i <= mid) s[sor++] = a[i++];
	while (j <= right)  s[sor++] = a[j++];
	//将合并完成的临时数组里的数,复制到原数组里面
	sor = left;
	while (sor <= right) {
		a[sor] = s[sor];
		sor++;
	}

}
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值