• 快速排序及其优化


    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;  
        }  
    }  
    

      

  • 相关阅读:
    Redis面试题
    redis基本操作
    pwd命令和cd命令
    ls命令详解
    Python时间操作所相关
    Nginx
    网络相关知识
    LeetCode 刷题记录(6-10题)
    绕过校园网Web认证
    Java相关知识
  • 原文地址:https://www.cnblogs.com/mingyao123/p/7348957.html
Copyright © 2020-2023  润新知