思路:利用分治的思想将大问题转化成若干相同小问题
快速排序:在一组无序的数中,不断选定一个主元,将这个主元放到这组数合适位置(使主元左侧都小于主元,不保证依次有序;使主元右侧都大于主元,不保证依次有序)。然后不断递归调用,直到一组数完全有序。
一.单方向扫描分区的快速排序
单方向扫描分区
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;
}
}
效果: