快速排序完美优化版【JAVA】

普通优化快排

相比较原始快排,这里优化了两个地方:

优化1:为防止在近乎有序的数组,partition分割极度不平衡,可将v元素随机化选取

优化2:递归到一定程度可使用插入排序

另外为了方便大家理解partition过程,附图一张:

    public void quickSort(int[] nums){
    	if(nums == null || nums.length <=0)
    		return;
    	
    	quickSort(nums , 0 , nums.length-1);
    }
    
    public void quickSort(int[] nums , int l , int r){  //对nums[l...r]部分进行快排
    	//if(l >= r) //原始递归到底方法
    		//return;
    	if(r-l <= 15){	//优化2:递归到一定程度可使用插入排序
    		insertSort(nums , l , r);
    		return;
    	}
    	
    	int p = partition(nums , l , r);

    	quickSort(nums , l , p-1);
    	quickSort(nums , p+1 , r);
    }
    
    public int partition(int[] nums , int l , int r){
    	swap(nums , l , l+(int)(Math.random()*(r+1-l)));//优化1:为防止在近乎有序的数组,partition分割极度不平衡,可将v元素随机化选取
    	
    	int v = nums[l];
    	int j = l;
    	
    	for(int i = j+1 ; i<=r ; i++)
    		if(nums[i]<v)
    			swap(nums , ++j , i);
    	
    	swap(nums , l , j);
    	
    	return j;
    }
    
    public void insertSort(int[] nums , int l , int r){
    	for(int i=l+1 ; i<=r ; i++){
    		int temp = nums[i];
    		int j=i;
    		
    		for(; j>l && nums[j-1]>temp ; j--){
    			nums[j] = nums[j-1];
    		}
    		nums[j] = temp;
    	}
    }
    
    public void swap(int[] nums , int i , int j){
    	int temp = nums[i];
    	nums[i] = nums[j];
    	nums[j] = temp;
    }

双路快排

但此时对于有大量重复元素的情况,依然会造成partition不平衡的情况,所以,可以考虑使用双路快排。如下图所示:i,j分别从两边向中间靠拢,每次交换i,j时,总是arr[i] >= v,arr[j]<=v的情况,这样就将==v的情况在两边平分

具体到代码,只有partition有改动:

    int partition(int[] arr, int l, int r){
        swap(arr, l, l + (int)(Math.random()*(r-l+1)));
        int v = arr[l];

        //arr[l+1...i) <= v, arr(j...r] >= v
        int i = l+1, j=r;
        while(true){
            while(i <= r && arr[i] < v)
                i++;
            while(j >= l+1 && arr[j] > v)
                j--;
            if(i > j)
                break;
            swap(arr, i, j);
            i++;
            j--;
        }

        swap(arr, l, j);

        return j;
    }

三路快排

当存在大量重复元素时,三路快排比双路快排更快,因为==v的区间元素直接就不需要再次运算了,效率很高。这里将数组分为三个部分<v, ==v, >v,新建lt与gt进行区分,如图所示:

当运行完毕后,继续快排[l, lt-1]与[gt, r]之间的元素即可,如图所示:

由于partition函数传递lt与gt两个变量不太方便,所以这里将前面partition函数整合至quickSort递归函数中:

private void quickSort(int[] nums, int l, int r){
        if(l >= r)
            return;
        
        swap(nums , l , l+(int)(Math.random()*(r+1-l)));
        int v = nums[l];
        int lt = l, gt = r+1, i = l+1;
        
        while(i < gt){
            if(nums[i] == v)
                i++;
            else if(nums[i] < v)
                swap(nums, i++, ++lt);
            else
                swap(nums, i, --gt);
        }

        swap(nums, l, lt);
        quickSort(nums, l, lt-1);
        quickSort(nums, gt, r);
    }

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值