快速排序(三种分区方式)

思路:利用分治的思想将大问题转化成若干相同小问题
快速排序:在一组无序的数中,不断选定一个主元,将这个主元放到这组数合适位置(使主元左侧都小于主元,不保证依次有序;使主元右侧都大于主元,不保证依次有序)。然后不断递归调用,直到一组数完全有序。

一.单方向扫描分区的快速排序
单方向扫描分区
1.先定主元(就是要按他为中间值进行划分,其左侧小于他,不一定有序;其右侧大于他,不一定有序),主元一般为数组的第一个值
2.scan指针(指向主元的下一个值),移动scan指针,小于等于主元,scan指针继续往右移动
3.bigger指针(指向数组的最后一个值),如果scan指针遇到大于主元的值,将scan指针所指向的值与bigger指针所指向的值,进行交换,并且将bigger指针往前移动
4.指针移动的前提条件是scan<=bigger
最后:利用递归不断缩小分区,实现快速排序

public class 单向扫描分区快排 {
public static void main(String[] args) {
	int []a= {5,3,6,2,8,9,10,0};
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
	QuickSort(a,0,a.length-1);
	System.out.println();
	System.out.println("-----------");
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
}

private static void QuickSort(int[] a,int p,int r) {
	// TODO Auto-generated method stub
	//利用分治思想,不断划分为小的区间,进行排序
	if(p<r)
	{	
	int q=index(a,p,r);
	QuickSort(a, p, q-1);
	QuickSort(a, q+1, r);
	}
}

private static int index(int[] a,int p,int r) {
	// TODO Auto-generated method stub
	int scan=p+1;
	int bigger=r;
	int x=a[p];//定主元
	while(scan<=bigger)
	{
		if(a[scan]<=x)
		{
			scan++;
		}
		else 
		{
			int term=a[scan];
			a[scan]=a[bigger];
			a[bigger]=term;
			bigger--;
		}
	}
	//最后将bigger所指向的元素与主元交换,这样就完成了分区
	int term=a[bigger];
	a[bigger]=a[p];
	a[p]=term;
	return bigger;//返回分区之后,在其左边有序并且小于他,在其右边有序并且大于他的下标值
}
}

效果:
在这里插入图片描述
二:双向扫描分区快排
双向扫描分区
1.定主元,一般以一组数的第0个为主元
2.begin指针指向主元下一个数,end指针指向数组的最后一个数。
3.工作原理非常类似单向扫描分区,不同之处在于:双向扫描中的end指针每次也判断并移动,而单向扫描中的bigger指针只在scan指针遇到比主元更大的数时与bigger指针所指的数交换之后才向前移动;
双向扫描的工作原理:
(1)begin指针所指向的数小于等于主元时,begin++即向后移动。
(2)end指针所指向的数大于主元时,end—即向前移动。
(3)当begin遇到比主元大的数,停止移动;当end遇到小于等于主元的数,停止移动;交换两个指针所指的数。
一直循环,截止条件是:begin>end
(4)将end所指的数与主元交换,就完成了划分,将本次主元放到了合适位置。

public class 双向扫描快排 {
	public static void main(String[] args) {
		int []a= {1,3,5,6,1,3};
		for(int i=0;i<a.length;i++)
		{
			System.out.print(a[i]+" ");
		}
		System.out.println();
		System.out.println("--------------");
		QuickSort(a,0,a.length-1);
		for(int i=0;i<a.length;i++)
		{
			System.out.print(a[i]+" ");
		}
		
	}

	private static void QuickSort(int[] a,int p,int r) {
		// TODO Auto-generated method stub
		if(p<r)
		{
			int q=index(a,p,r);
			QuickSort(a, p, q-1);
			QuickSort(a, q+1, r);
		}
	}

	private static int index(int[] a, int p, int r) {
		// TODO Auto-generated method stub
		int x=a[p];//定主元
		int begin=p+1;//头指针
		int end=r;//尾指针
		while(begin<=end)
		{
			if(a[begin]<=x)
			{
				begin++;
			}
			if(a[end]>x)
			{
				end--;
			}
			if(begin>end)//一旦完成交错,立马终止
			{
				break;
			}
			if(a[begin]>x&&a[end]<=x)
			{
				int term=a[begin];
				a[begin]=a[end];
				a[end]=term;
			}
		}
		//最后将主元放到求得的位置,将一次排序排好
		int term=a[p];
		a[p]=a[end];
		a[end]=term;
		
		return end;
	}
}

效果:
在这里插入图片描述

三:三指针扫描分区快排
在排序过程中容易出现:数组中存在大量相同的相同元素,当这样的元素作为主元时,为了进一步提高效率,减小下一步的递归区间,可以将与主元相同的元素划分出去。也就是分成三个区间:第一个区间是小于主元的值,第二个区间是等于主元的值,第三个区间是大于主元的值。
过程:
1.定主元,一般是以数组第0个值为主元
2.scan指针和e指针初始状况都是指向第1个元素,,bigger指针指向数组最后一个元素。
当scan所指的值小于主元时,scan所指元素与e指针所指元素交换,然后scan++即后移,e++即后移。当scan所指值等于主元时,scan++即后移;当scan所指值大于主元时,scan所指值与bigger所指值交换,bigger–即后移;
3.最后e–即前移,移动到第一个等于主元的元素前,然后将主元与此时e指针所指的值交换;这样次趟就完成了将主元放到合适位置的任务
4.反复递归剩下的无序区间,直到有序为止。

public class 三指针扫描分区快排 {
public static void main(String[] args) {
	int []a= {8,2,1,2,3,0,8,5,6};
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
	QuickSort(a,0,a.length-1);
	System.out.println();
	System.out.println("--------------");
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
}

private static void QuickSort(int[] a, int p, int r) {
	// TODO Auto-generated method stub
	if(p<r)
	{
		int []arr=new int[2];
		arr=index(a,p,r);
		int q1=arr[0];
		int q2=arr[1];
		QuickSort(a, p, q1-1);
		QuickSort(a, q2+1, r);
	}
}


private static int[]  index(int[] a, int p, int r) {
	// TODO Auto-generated method stub
	int x=a[p];//主元
	int e=p+1;//相等指针
	int scan=p+1;//扫描指针
	int bigger=r;
	while(scan<=bigger)
	{
		if(a[scan]<x)
		{
			int term=a[scan];
			a[scan]=a[e];
			a[e]=term;
			e++;
			scan++;
		}
		if(scan>bigger)//已经出现截止,但依然执行下面的移动
		{
			break;
		}
		if(a[scan]==x)
		{
			scan++;
		}
		if(scan>bigger)
		{
			break;
		}
		if(a[scan]>x)
		{
			int term=a[scan];
			a[scan]=a[bigger];
			a[bigger]=term;
			bigger--;
		}
	}
	e--;
	//交换主元和bigger所指
	int term=a[e];
	a[e]=a[p];
	a[p]=term;
	//因为要返回两个值,所以将两个值放到数组中返回
	int []arr=new int[2];
	arr[0]=e;
	arr[1]=bigger;
	return arr;
}
}

效果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值