快速排序 (随机选择pivot)

快速排序也是分而治之的思想,是所有的内排中最好的一种。

快排


public class FastSort {

    public static int partition(int[] arr, int left, int right){
        int i = left, j = right;
        int pivot = arr[i];
        while(i < j){
            while(i < j && pivot <= arr[j]) j--;
            if(i < j) arr[i++] = arr[j];
            while(i < j && pivot >= arr[i]) i++;
            if(i < j) arr[j--] = arr[i];
        }
        arr[i] = pivot;
        return i;
    }
    // 递归实现
    public static void sort(int[] arr, int left, int right){
        if(left >= right) return;
        int p = partition(arr, left, right);
        sort(arr, left, p-1);
        sort(arr, p+1, right);
    }
// 非递归实现
    public static void NonRecruitSort(int[] arr, int left, int right){
        Stack<Integer> stack = new Stack<>();
        stack.push(left);
        stack.push(right);
        while(!stack.isEmpty()){
            int high = stack.pop();
            int low = stack.pop();
            int part = partition(arr, low, high);
            if(low < part-1){
                stack.push(low);
                stack.push(part-1);
            }
            if(part+1 < high){
                stack.push(part+1);
                stack.push(high);
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {12,34,11,45,25};
        NonRecruitSort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
}

快排的趟数取决于递归的深度,最好的情况下是 O ( l o g 2 n ) O(log_2n) O(log2n),最差的情况下是 O ( n 2 ) O(n^2) O(n2,平均 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n,这里注明一下最坏的情况是正序或者倒序,最好的情况是每次都能划分相等的两份,怎么优化这个问题,避免最坏的情况呢?这里斯坦福提出了一种选择pivot的方法:median-of-three.

选取第一个、最后一个以及中间的元素的中位数,如4 5 6 7, 第一个4, 最后一个7, 中间的为5, 这三个数的中位数为5, 所以选择5作为pivot,8 2 5 4 7, 三个元素分别为8 5 7, 中位数为7, 所以选择最后一个元素7作为pivot,其实现如下:

private static int choosePivotMedianOfThree(int[] a, int left, int right) {	
	int mid = 0;
	if((right-left+1) % 2 == 0) mid = left + (right-left+1)/2 - 1;
	else mid = left + (right-left+1)/2;
	//只需要找出中位数即可,不需要交换
    //有的版本也可以进行交换
	if(((a[left]-a[mid]) * (a[left]-a[right])) <= 0) return left;
	else if (((a[mid]-a[left]) * (a[mid]-a[right])) <= 0) return mid;
	else return right;
}

然后我们在划分的时候,只需要把 choosePivotMedianOfThree 得到的下标和left进行交换相应下标位置的元素,划分依旧没有变化。类似于这样子:

int pivot_index = choosePivotMedianOfThree(a, left, right);
	//始终将第一个元素作为pivot, 若不是, 则与之交换
if (pivot_index != left) {
	swap(a, pivot_index, left);
}
int pivot = a[left];

举一个牛客上快排的例子,这题用堆也是可以做到的,小根堆,但是我们用快排做:
在这里插入图片描述

import java.util.*;

public class Finder {
    public int findKth(int[] a, int n, int K) {
        // write code here
        int left = 0, right = n-1;
        if(right <= left || n == 0) return 0;
        int p = partition(a, left, right);
        while(p != n-K){
            if(p > n-K){
                right = p-1;
                p = partition(a, left, right);
            }
            if(p < n-K){
                left = p+1;
                p = partition(a, left, right);
            }
        }
        return a[p];
    }
    public int partition(int[] a, int low, int high){
        int i = low, j = high;
        int pivot = a[low];
        while(i<j){
            while(j > i && pivot <= a[j]) j--;
            a[i] = a[j];
            while(j > i && pivot >= a[i]) i++;
            a[j] = a[i];
        }
        a[i] = pivot;
        return i;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值