明耀

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
import java.util.*;
public class Main {

	public static void main(String[] args)
	{
		int[] num={38,5,18,45,8,62,19,27};
		quickSort(num,0,num.length-1);
		for(int i=0;i<num.length;i++)
			System.out.println(num[i]);
	}
	public static void quickSort(int[] num,int left,int right)
	{
		if(left<right)
		{
			int middle=sort(num,left,right);
			quickSort(num,left,middle-1);
			quickSort(num,middle+1,right);
		}
	}
	public static int sort(int[] num,int left,int right)
	{
		int tmp=num[left];
		while(left<right)
		{
			while(left<right&&num[right]>=tmp)
				right--;
			if(left<right)
				num[left++]=num[right];
			while(left<right&&num[left]<=tmp)
				left++;
			if(left<right)
				num[right--]=num[left];
		}
		num[left]=tmp;
		return left;
	}
}

快速排序的三个步骤:

1.选择基准,作为分割序列的比较依据。

2.进行分割操作,将序列以改基准在序列中的位置分成两个子序列,左边序列元素值均小于基准,右边序列元素值均大于基准。

3.递归对两个子序列进行快速排序直到序列为空或只有一个元素。

选择基准的方式: 
对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列。

三种选择基准的方法:

1、取序列的第一个或者最后一个元素作为基准

缺点:若数组有序,此时的分割效果非常差,每次划分只能使待排序序列减一,快速排序沦为冒泡排序,时间复杂度O(N2

2、随机选取基准:取待排序列中任意一个元素作为基准,将选择好的基准元素与low位置元素互换位置,此时就可以和普通的快排一样调用划分函数。

缺点:数字全相等的情况下,时间复杂度依然是O(n2)。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。

3、三数取中

一般是取low、middle、high三个位置上元素的中值作为基准

缺点:同样处理不了重复数组

三中优化方式:

1、当待排序序列长度分割到一定大小后,使用插入排序,因为对于很小和部分有序的数组,快排不如插排好。一般当待排序序列长度等于10时,就使用插入排序。

2、一次分割结束后,将于基准元素相等的元素聚在一起,继续下次分割时,不用再对与key相等的元素分割

public class Main {
	public static void main(String[] args)
	{
		int[] num={1,5,4,8,3,6,7,2,12,9,34,27,54,43,18,20,-1,-5,13,17,-22};
		quickSort(num,0,num.length-1);
		for(int i=0;i<num.length;i++)
			System.out.println(num[i]);
	}
	public static void quickSort(int arr[],int low,int hight)
	{
		int first=low;
		int last=hight;
		
		int left=low;
		int right=hight;
		int leftLen=0;
		int rightLen=0;
        //待排序序列长度小于10时,直接使用插入排序
		if(hight-low+1<10)
		{
			InsertSort(arr,low,hight);
			return;
		}
		int key = SelectPivotMedianOfThree(arr,low,hight);//使用三数取中法选择枢轴 
		while(low<hight)
		{
			while(low<hight&&arr[hight]>=key)
			{
				if(arr[hight]==key)
				{
					swap(arr[hight],arr[right]);
					right--;
					rightLen++;
				}
				hight--;
			}
			arr[low]=arr[hight];
			while(low<hight&&arr[low]<=key)
			{
				if(arr[low]==key)
				{
					swap(arr[low],arr[left]);
					left++;
					leftLen++;
				}
				low++;
			}
			arr[hight]=arr[low];
		}
		arr[low]=key;
		int i=low-1;
		int j=first;
		while(j<left&&arr[i]!=key)
		{
			swap(arr[i],arr[j]);
			i--;
			j++;
		}
		i=low+1;
		j=last;
		while(j>right&&arr[i]!=key)
		{
			swap(arr[i],arr[j]);
			j--;
			i++;
		}
		quickSort(arr,first,low-1-leftLen);
		quickSort(arr,low+1+rightLen,last);
	}
	public static void InsertSort(int[] arr,int low,int height)
	{
		for(int i=low+1;i<=height;i++)
		{
			int tmp=arr[i];
			int j;
			for(j=i;j>low&&arr[j-1]>tmp;j--)
			{
				arr[j]=arr[j-1];
			}
			arr[j]=tmp;
		}
	}
	public static int SelectPivotMedianOfThree(int[] arr,int low,int hight)
	{
		int middle=(low+(hight-low)/2);
		if(arr[middle]>arr[hight])
			swap(arr[middle],arr[hight]);
		if(arr[low]>arr[hight])
			swap(arr[low],arr[hight]);
		if(arr[low]<arr[middle])
			swap(arr[low],arr[middle]);
		return arr[low];//low存放中间元素的值
	}
	public static void swap(int a,int b)
	{
		int tmp=a;
		a=b;
		b=tmp;
	}

}

3、优化递归操作

快排函数在函数尾部有两次递归操作,我们可以对其使用尾递归优化

void QSort(int arr[],int low,int high)  
{   
    int pivotPos = -1;  
    if (high - low + 1 < 10)  
    {  
        InsertSort(arr,low,high);  
        return;  
    }  
    while(low < high)  
    {  
        pivotPos = Partition(arr,low,high);  
        QSort(arr,low,pivot-1);  
        low = pivot + 1;  
    }  
}  

  

posted on 2017-08-12 09:34  明耀  阅读(381)  评论(0编辑  收藏  举报