快速排序算法【双指针法和填空法】(c++)

快速排序:

双指针法:

方法是找到一个数作为基准数,(一般将第一个数作为基准数),将序列中比基准数大的放在基准数的右边,比基准数小的放在基准数的左边。怎样实现呢?既然左右都找,那就从两边进行探测,大的放右边,小的放左边,就反过来找,一个从右边找小的,一个从左边找大的。如 5  1 4 7 6 2 8  3 ,这里用两个探测器,变量 i 和 j ,分别指向序列的最左边和最右边,我们将第一个数 5 作为基准数,让左边 j 出动,找到比 5 小的数 3 停下,然后再让右边 i 出动,找到比 5 大的数 7 就停,现在交换  i 和 j 所指向元素的值 3 和 7,第一次交换实现后,序列中的数是这样子的 5 1 4  3 6  2 8 7  ,  j 继续向左挪动,找到比 5 小的数 2 ,停,看 i ,i向右走,找到比 5 大的数 6 ,好停住,交换2 ,6,现在数的顺序为 5 1 4 3 2 6 8 7 ,现在 j 指向 6,j再继续往左走时,找到比5 小的数 2 ,此时,i 与 j 都指向 2,两个相遇了,相遇不能交换了,那就将  j 找到比 5 小的数 2 与基准数 5 交换,现在顺序是 2 1 4 3 5 6 8 7 ,现在基准数左边比5 小,右边比 5 大,第一轮交换完毕,左边数是 2 1 4 3 ,右边数是 6 8 7,之后再模拟第一轮交换,分别处理这两边的数。2 1 4 3,首先找基准数2 ,使得 基准数 左边的数小于等于 基准数,基准数右边的数大于等于基准数 ,其次变量 i 和 j 分别指向最左边 2 和 最右边 3,j 先走,找到了 1 ,i 往右走,i 指向 1 处时,和 j 碰撞上了,止步,将j 指向的数1和基准数2交换,此时顺序为 1 2 4 3 ,基准数 2 归位后,处理,基准数左边的 1 和右边的4 3 ,左边的 1 不用排序,右边 4 3 ,还是之前的方法,找出基准数 4 ,i和j 分别指向 4 3 ,j指向3 ,比 4 小,不用再向左走,j先停,看 i,i向右走,与j碰撞,停,将j指向的数3与基准数交换。此时得到1 2 3 4,5 右边的数也是以此类推。最后排完序,1 2 3 4 5 6 7 8 .

总结:

1.找基准数,将 变量 i和 j 分别指向 序列的最左边和最右边

2. 先从最右边 j 开始往左找比基准数小的,找到后 j 停,看 i,i从最左边向右开始找比基准数大的 ,找到后,i止步,交换 i和j指向的数。如果没找到,j 找到大的数,i没找到小的数,i 和 j走到一处了,停住,将j指向的数与基准数交换,基准数归位。一轮交换结束。

3.   基准数左边是比基准数大的数,右边比基准数小的数,之后分别处理这两边的数,重新找基准数,这里用递归的思想,以此类推,当左边变量大于等于右边变量,排序完毕,返回。

这里思考一个问题,为什么刚开始就强调先从右边开始找?

就拿上面的 2 1 4 3 来说,如果先从最左边开始找,i向右走,走走走,找到了比 2 大的数4,停下,之后j 向右走,没找到比 2 小的就和 i碰上了,同时在4 处停下来,4 比 2 小吗,不,4 比 2 大,那按照上面的方法来做,要和基准数交换啦,就变成 4 1 2 3 ,此时 基准数左边的并不是全部比它大的数,所以入场的顺序还是很重要的。就像人生的入场顺序,很多人换一个时间认识,就会有不同的结局,当一个人在你的生命里先出现,心里装的全是他。后来者,再怎么好,也不及之前的千万分之一,心里容下的只有一个人,后者已无法再去将就。

不弃一心念念的只有陈煜

奈何东方炻苦苦追不到不弃,无奈,他来晚了,媳妇已经被莲衣客拐走咯,眉毛扭成一团也不行喽。

快速排序的核心就是将基准数归位。

快速排序是基于“二分”思想,它是跳跃式的,最差时间复杂度与冒泡排序一样,O(n2),它的平均复杂度是O(nlogn).

C++:

#include<iostream>
using namespace std;

int a[101];

void quicksort(int left ,int right)
{
	if (left >= right)//left = rigth 时,还排啥嘞 
	return ;
	int i,j,base,temp;
	base = a[left];
	i = left;
	j = right;
	while (i<j)
	{
		while (a[j] >= base && j>i)//先从右向左找小的 
		{
			j--;
		}
		while (a[i] <= base && i<j)//再从左向右找大的 
		{
			i++;
		}
		if (i<j)//在两者相遇之前,大小数交换 
		{
			temp = a[i];
			a[i] = a[j];
			a[j] = temp;
		}
	}
	//当两者相遇将基准数归位
	a[left] = a[j];
	a[j] = base;
	quicksort(left, j-1);
	quicksort(j+1,right); 
}

int main()
{
	int n;
	cin>>n;
	for (int i=0; i<n; i++)
	cin>>a[i];
	
	quicksort(0,n-1);
	
	for (int i=0; i<n; i++)
	cout<<a[i]<<endl;
	
	return 0;	
}

还有一点是贯穿整个算法始终的:左边指针小于右边指针,要时刻控制

填空法:

我们还是以 5 1 4 7 6 2 8 3 为例子,取最左边的数 5 为基准数,i指向最左边的数,j 指向最右边的数,j向左挪,寻找比基准数大的数,找到之后停,将j指向的数3 放到i 指向的地方 ,此时是 5,现在就是 3 1 4 7 6 2 8 3 ,咦,怎么少了一个基准数,别担心,基准数一开始就定义一个变量存下来了,之后 i 开始走,向右挪,找到比基准数大的数 7 之后,停下,将i 指向的数放到j 指向的地方 ,3 1 4 7 6 2 8 7。看过 i之后,再来看 j, j挪挪挪,找到了比基准数小的数 2 ,将它放到 i 指向的地方,就将之前的 7 覆盖掉了,3 1 4 2  6 2 8 7。再回头看i ,i 向右挪,找到比基准数大的 6 ,将 6 放到 j 所指向的2处,现在是 3 1 4 2 6 6 8 7 。看过i 之后,看 j,j向左挪,挪到了 i处,与i 碰撞,将 基准数 5 放在 j 所指的地方,现在是 3 1 4 2 5 6 8 7 。基准数左边的数都比 5 小, 右边的数都比5 大,第一轮结束。

接下来将 5 左边和右边的数分别 像第一轮一样做处理,下面来看左边 比5 小的数 , 3 1 4 2 。第一步,找基准数 3,将基准数用一个变量存起来,将变量 i 和 j 分别指向 最左边 的数 3 和 最右边的数 2。第二步,从最左边开始找比 基准数 小的数 ,现在 j 指向的 最右边的数 2 就是比 3 小的数,找到了将 它填到 i 所指向的地方 3 处,2 1 4 2  。看完 右边 j 再看 左边 的 i, i现在指向 2 ,2 比 3 小,i向右挪,1 比3 小,继续向右挪,挪到了 4 处,4 比 3 大,好停,将现在 i所指的 4 填到 j  所指的地方,现在是 2 1 4 4,j 和 i指的数都为 4。看 j,j向右挪,找比基准数小的数,挪到了 i处 ,没找到,与 i 碰撞了,将 j指向的地方填上 基准数,此时是 2 1 3 4 ,基准数3左边的全部比它小,右边比它大。此轮结束,再分别处理 基准数两边的 2  1 和  4。 2 1 找基准数 2 ,先将基准数 2 村下来,i和 j分别指向 2 1 ,先来看最左边的 j,j指向的 1 比基准数小,将 j所指的数 1 填到 i所指向的地方,此时就是 1 1 。再来看i,i指向的 1 比基准数 2 小,向右挪,没找到 ,挪到了j处,与j碰撞,将j所指的地方填上基准数,就是1 2 了,3左边的数好排完了,之后看右边的数 4 ,就一个数不需要再排了。5 右边的数与5左边的数排法一样,最后就得到了1 2 3 4 5 6 7 8 。

总结:

1.找基准数,将基准数存起来,变量i 和 j 分别指向一个序列最左边和最右边的数

2.先从最右边开始找比基准数小的数,从最右边第一个开始比较,没找到的,j向左挪,找到就停,将j 指向的数填到 i所指的地方。之后回过来看i,从左边找比基准数大的数,没找到i向右挪,找到之后就将 i所指的数填到 j上。再来看j,如果j 没找到比基准数小的数,向左挪,与i相遇了,i和j同时指向一个地方,就将基准数填到j所指向的地方。

3.基准数归位之后,将序列分为两组,一组是基准数左边全是比基准数小的数,一组是基准数右边全都比基准数大的数。分别处理这两边的数,再像开始那样找基准数,利用递归思想,以此类推。

C++:

#include<iostream>
using namespace std;

int a[101];

void quicksort(int left , int right)
{
	if (left >= right)
	return ;
	int base,i,j;
	base = a[left];
	i = left;
	j = right;
	while (i<j)
	{//要保持i<j ,如果基准数右边没找到比基准数小的就要继续往左挪,
	//没有 i<j这个条件,i和j相遇了还会往左挪,就会溢出 
	// a[j] >= base,没有等于也可以,有等于号时遇到与基准数一样的数就放在了右边
	//没有等于号时遇到与基准数一样大的数,就填过去,放在了基准数的左边 
		while (a[j] >= base && j>i)
		{
			j--;
		}
		
		if (i < j)
		{
			a[i] = a[j];
		}
		
		while (a[i] <= base && j>i)
		{
			i++;
		}
		if (i < j)
		{
			a[j] = a[i]; 
		}
		
	}
	a[j] = base;
	quicksort (left , j-1);
	quicksort (j+1 , right);
	
}
int main()
{
	int n;
	cin>>n;
	
	for (int i=0; i<n; i++)
	{
		cin>>a[i];	
	}
	quicksort (0,n-1);
	for (int i=0; i<n; i++)
	{
		cout<<a[i]<<endl;	
	}		
	
	return 0;
} 

如有错误,敬请指出~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值