快速排序法详解 不懂不要钱

参考自:【每日算法】C语言8大经典排序算法(1)


1、算法思想


设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j -- ),找到第一个小于key的值A[j],A[i]与A[j]交换;
4)从i开始向后搜索,即由前开始向后搜索(i ++ ),找到第一个大于key的A[i],A[i]与A[j]交换;
5)重复第3、4、5步,直到 I=J; (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。找到并交换的时候i, j指针位置不变。另外当i=j这过程一定正好是i+或j-完成的最后令循环结束。)[摘自【每日算法】C语言8大经典排序算法(1)]

上面的内容刚开始我也不是全部懂,就知道它每一趟的目的是把小于关键数据的数据都排到它的左边(对于升序排序而言),大于关键数据的数据都排到关键数据的右边。但3)4)5)步看不太明白,不知道它到底是怎么实现的,我现在将我如何理解明白的方法分享一下。


2、核心代码


void quickSort(int *data, int low, int high)
{
	if (data == NULL || low >= high)
	{
		return;
	}
	int pivotLoc;
	pivotLoc = partition(data, low, high);
	if (pivotLoc == -1)
		cout << "Wrong input" << endl;
	quickSort(data, low, pivotLoc - 1);
	quickSort(data, pivotLoc + 1, high);
}
int partition(int *data, int low, int high)
{
	if (data == NULL || low >= high)
	{
		return -1;
	}
	int keyData = data[low];
	while (low<high)
	{
		while ((low < high) && (data[high] >= keyData))
			high--;
		data[low] = data[high];
		while ((low < high) && (data[low] <= keyData))
			low++;
		data[high] = data[low];
	}
	data[low] = keyData;
	return low;
}


主要是partition函数,以下面 的数组为例,假设data指向了下面数组的第一个数7,并且low=0,high=7。partition要实现的结果,就是将
7,6,1,4,5,9,10,2
变成了
2,6,1,4,5,7,10,9
是怎么实现的呢?跑两遍程序就透彻了,我带着大家跑一遍。
首先if (data == NULL || low >= high)预先判断输入参数合不合法。然后int keyData = data[low];就是选取data[low],这里low=0,也就是7,即keyData=7。进循环之前,数组状态如下,绿色代表low所指向位置,黄色代表high所指向的位置。
7,6,1,4,5,9,10,2
再然后进循环,通过了low<high条件,再判断data[high] >= keyData?如果大于就high--,代表符合规则,暂时不需要动,而现在2<7,则data[low]=data[high];第一个7变成了2,如下
2,6,1,4,5,9,10,2
接着通过low<high条件,判断data[low] <= keyData?是的,2<7,那么low++,变成了这样
2,6,1,4,5,9,10,2
6<7,继续low++ 得到
2,6,1,4,5,9,10,2
1<7,继续low++ 得到
2,6,1,4,5,9,10,2
4<7,继续low++ 得到
2,6,1,4,5,9,10,2
5<7,继续low++ 得到
2,6,1,4,5,9,10,2
9>7,停止这个小循环,data[high] = data[low];
2,6,1,4,5,9,10,9
完成一次循环回来,判断data[high] >= keyData?9>7,high--,得到
2,6,1,4,5,9,10,9
10>7,继续high--,得到
2,6,1,4,5,9,10,9
此时low==high,因此后面循环都结束了。其中后面运行的data[low] = data[high];以及data[high] = data[low];都相当于自己赋值给自己,没改变任何状态。
最后到data[low] = keyData;那里,得到
2,6,1,4,5,7,10,9
一次partition结束。
我开始的时候有个疑问就是,他们这样赋值来赋值去的,会不会把某个data[high]或者data[low]给挤掉,最后自己再跑几遍发现这个算法巧妙就巧妙在这里,它首先把开始的data[low]给了keyData,然后用data[high]去赋给data[low],再把新的data[low]赋值给已赋值给data[low]data[high],也就是没有任何数据被挤掉,都在自己被别人赋值的之前,已经将值赋给别人了。

还有值得一提的是,一趟结束后,关键数据就处在了应该在的正确位置

比如 

7,6,1,4,5,9,10,2

按这样的原则,关键数据就是7,一次运算后变成了

2,6,1,4,5,7,10,9

左边都是小于7的,右边都是大于7的,且排在第6位,而这组排列最终排好的顺序的结果应该是

1,2,4,5,6,7,9,10

7就是排在第6位,也就是说,快速排序的每一趟,都让关键数据排在了正确的位置

可能你光看我这走一遍还一思半解的,我当初也是,自己跑第一遍的时候还是模模糊糊的,但自己用笔在纸上像这样再跑了两遍后,就体会到了这算法的妙处了。这个算法非常巧妙,就这样看着实现了要完成的功能,可你如果要我为了实现这个功能,来自己设计出这个算法啊?那我也青史留名了。。所以我们只需要学习,然后记住天才们想出的算法,以后会用就足够了。

那么partition这个函数就搞定了。我已经授之以渔了,至于quickSort函数,简而言之就是对关键数据的左边和右边再重复做partition(递归哟),直到最终顺序都顺了就结束,相信你自己跑一遍肯定也很快会弄懂了。

3、Test程序

#include<iostream>
#include<string>
using namespace std;

void quickSort(int *data, int low, int high);
int partition(int *data, int low, int high);

void main(void)
{
	int array[10] = {9,8,7,6,5,4,3,2,1,0};
	int num = 10;

	//step1: 遍历数组,输出初始排列
	for (int arN = 0; arN < num; arN++)
	{
		cout << array[arN]<<' ';
	}
	cout << endl;

	//step2: 快速排序
	quickSort(array, 0, num-1);

	//step3: 遍历数组,输出排序后结果
	for (int arN = 0; arN < num; arN++)
	{
		cout << array[arN]<<' ';
	}
}


void quickSort(int *data, int low, int high)
{
	if (data == NULL || low >= high)
	{
		return;
	}

	int pivotLoc;
	pivotLoc = partition(data, low, high);

	if (pivotLoc == -1)
		cout << "Wrong input" << endl;
	quickSort(data, low, pivotLoc - 1);
	quickSort(data, pivotLoc + 1, high);
}

int partition(int *data, int low, int high)
{
	if (data == NULL || low >= high)
	{
		return -1;
	}

	int keyData = data[low];
	while (low<high)
	{
		while ((low < high) && (data[high] >= keyData))
			high--;
		data[low] = data[high];
		while ((low < high) && (data[low] <= keyData))
			low++;
		data[high] = data[low];
	}
	data[low] = keyData;
	return low;
}

运行结果:




欢迎评论交流,觉得好就顶个吧,写了老半天了不容易啊。。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值