快排优化2种方法找主元使得分区的时候均分提高效率

起因:前面我们写的快排,在划分(分区)的时候无论是一遍单向扫描,双向扫描还是三指针分区法都是把第一个元素定义为主元,所以说划分的时候可能并不均匀(不是一半一半划分的),最坏的情况下可能导致时间复杂度变为O(n^2),而快排理想的是O(n*lgn).

法一:三点中值法

public static int patitiion(int[] arr,int start,int end) {
       //优化,在start,end,mid中找主元
		int midindexof = start+((end-start)>>1);
    	int midindexvalueof = -1;
    	int zhuyuan;
    	if(arr[start]>arr[end]&&arr[end]>arr[midindexof]) {
    		zhuyuan = arr[end];
    		Swap.swap(arr, start, end);
    	}else if(arr[end]>arr[start]&&arr[start]>arr[midindexof]) {
    		zhuyuan = arr[start];
    	}else {
    		zhuyuan = arr[midindexof];
    		Swap.swap(arr, start, midindexof);
    	}
        	//左指针
        	int left = start+1;
        	int right = end;//右指针
        	
        	while(left<=right) {
        	//左侧大于主元就停下,特殊情况会越界
        	while(left<=right&&arr[left]<=zhuyuan) {
        		left++;
        	}
        	//右侧小于等于主元就停下
        	while(left<=right&&arr[right]>zhuyuan) {
        		right--;
        	}
        	if(left<right)
        	Swap.swap(arr, left, right);//停下后元素交换
        }
        	Swap.swap(arr, start, right);
        	return right;
    }

法二:绝对中值法

找这个数组的真正的中间值:通过把这个数组每五个元素一个区间,进行插入排序,然后找到每个区间的中间值存放到一个数组中去,然后对这个数组进行插入排序,找到中间值,就是整个数组的中间值了,也是主元。

import java.util.Arrays;

public class 快排优化之绝对中值法 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] brr = new int[]{9,5,6,8,4};
        quicksort(brr,0,brr.length-1);
        System.out.println(Arrays.toString(brr));
	}

	public static void quicksort(int[] arr, int start, int end) {
    	if(start<end) {//递归的出口
    		int q = partition(arr,start,end);//找到主元数组下标
    		quicksort(arr,start,q-1);//对左边进行快排,
    		quicksort(arr,q+1,end);//对右边进行快排
    	}
    }
	//找到主元的下标,并把小于主元的放左边,大于主元的放右边
	  public static int partition(int arr[],int start,int end) {
		  int tz = zhuyuan(arr,start,end);
		  for(int i = start;i<=end;i++) {
			  if(arr[i]==tz) {
				 swap(arr, start, i);
			  }
		  }
	    	int zuyuan = arr[start];//主元初始化
	    	int sp = start+1;//左侧扫描指针
	    	int bigger = end;//右侧末指针
	    	//两个指针交错说明数组已经扫描完了
	    	while(sp<=bigger) {
	    		if(arr[sp]<=zuyuan) {//扫描指针所指元素比主元小或者相等
	    			sp++;
	    		}
	    		else {//扫描指针所指元素比主元大
	    			swap(arr,sp,bigger);
	    			bigger--;
	    		}
	    	}
	    	swap(arr, start, bigger);//把主元放到bigger这个位置,这样bigger左边都小于主元,右边都大于主元
	    	return bigger;
	    }
	
	
	
	//找主元的大小
	public static int zhuyuan(int[] arr,int start,int end) {
		int size = end - start+1;//参与排序这个区间的长度
		//每五个元素一组
		int groupSize = (size%5==0)?(size/5):(size/5+1);
		//存储各个小组的中值
		int midValue[] = new int[groupSize];
		
		//对每一组进行插入排序,找到每一组中的中值,并存入数组中去
		for(int i = 0;i<groupSize;i++) {
			//因为最后一组不知道有多少个元素,所以要分开讨论
			if(i == groupSize-1) {//最后一组
				insertSort(arr,start+5*i,end);//对最后一组进行插入排序
				midValue[i] = arr[(start+5*i+end)/2];
			}else {
				insertSort(arr,start+5*i,start+5*i+4);
				midValue[i] = arr[(start+5*i+end)/2];
			}
		}
		
		insertSort(midValue,0,midValue.length-1);
		return midValue[midValue.length/2];
		
	}
	//插入排序
	public static void insertSort(int[] arr,int start, int end) {
		for(int i=start;i<end+1;i++) {
	  		int jilu = i;//用来记录i的值,以便防止while循环过后I的值改变
	  		while(i>=0&&i<end&&arr[i]>arr[i+1]) {//通过while循环逐步把去调整要插入的位置
	  		swap(arr,i,i+1);
	  		i--;
	  		}
	        i=jilu;//用来恢复i的取值
	  	}
	}
	
	public static void swap(int[] arr,int a,int b) {
		int temp = arr[a];
		arr[a] = arr[b];
		arr[b] = temp;
	}
}

注意:

当待排序列较短的的时候,插入排序比快排更好

经过分析我们知道(假设序列长度为n)

插入排序要执行n*(n-1)/2次

而快排执行n*lgn+n次

所以当元素个数小于等于8的时候插入排序更快

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值