快速排序练习题(Java)

10 篇文章 0 订阅

快速排序

1、算法实现

快速排序的核心方法:partition,它首先随机选择一个数,然后以这个数为轴,小于它的放在它前面,大于它的放在后面,然后放回这个轴数的排序位置。有很多种实现方法,选择我比较熟悉的一种:

private int partition(int[] nums, int begin, int end){
      	int pivot = nums[end];
        int pivotIndex = begin - 1;

        for(int i = begin; i <= end; i++){
            if(nums[i] <= pivot){
                pivotIndex++;
                int tmp = nums[i];
                nums[i] = nums[pivotIndex];
                nums[pivotIndex] = tmp;
            }
        }
        
        return pivotIndex;
    }

然后是轴心的这个数的位置已经是正确的(左边都比它小,右边都比它大)然后利用分治的思想,将左右两边用同样的方法处理,这里有递归和非递归的版本,非递归的版本利用栈实现。

递归:

public void quickSort(int[] nums, int begin, int end){
        if(nums == null || begin < 0 || end <= 0 || begin > end)
            return;
        int pivotIndex = partition(nums, begin, end);
        if(pivotIndex > begin){
            quickSort(nums, begin, pivotIndex-1);
        }
        if(pivotIndex < end){
            quickSort(nums, pivotIndex+1, end);
        }
    }

非递归:
用栈模拟递归,根据后进先出的特点,先压入end再压入begin

public void quickSortStack(int[] nums, int begin, int end){
        if(nums == null || begin < 0 || end <= 0 || begin > end)
            return;
        Stack<Integer> stack = new Stack<>();
        stack.push(end);
        stack.push(begin);
        int beginIndex, endIndex;

        while(!stack.empty()){
            beginIndex = stack.pop();
            endIndex = stack.pop();
            if(beginIndex < endIndex){
                int pivotIndex = partition(nums, beginIndex, endIndex);
                if(pivotIndex > beginIndex){
                    stack.push(pivotIndex-1);
                    stack.push(beginIndex);
                }
                if(pivotIndex < endIndex){
                    stack.push(endIndex);
                    stack.push(pivotIndex+1);
                }
            }
        }
    }

2、复杂度分析

时间复杂度:

每一层递归的运算次数是 n
每一层递归,都将一个区间按一定比例划分成两个,所以递归的层数是 O(logn)
整体的时间复杂度是:O(nlogn)

而最差的情况是每一次都是取到最大或最小,也就是等于冒泡排序,时间复杂度是O(n^2)

空间复杂度:

空间复杂度是 O(1),没有额外申请空间
但是无论是递归方式,还是用栈的方式,都有额外的空间保存状态,所以真实的空间消耗是:

一般情况:O(logn)
最差的情况:O(n^2)

习题:

解题思路:
利用快排的思想,每一次partition,可以排对一个数的位置,且大于它的在它后面,小于它的在它前面,所以我们只需要递归地在数多的部分进行partition的操作,就能用分治的方法找出中位数。

时间复杂度:
第一次处理的数量是n,然后后面处理的数量成指数减少
递归的次数是logn
根据等比数列的运算公式,估计算出时间复杂度是 O(n)

另外的解法:
如果不能改变数组的顺序,且要求用的额外空间较少,这题可以用bitmap做

1、lintcode 80 找中位数

public class FindMedian{

    public int findMedian(int[] nums){
        return recursion(nums, 0, nums.length-1);
    }

    private int recursion(int[] nums, int begin, int end){

        int pivotIndex = partition(nums, begin, end);

        if(pivotIndex == (nums.length-1) / 2){
            return nums[pivotIndex];
        }else if(pivotIndex > (nums.length-1) / 2){
            return recursion(nums, begin, pivotIndex-1);
        }else{
            return recursion(nums, pivotIndex+1, end);
        }
    }

    private int partition(int[] nums, int begin, int end){
        int pivot = nums[end];
        int pivotIndex = begin - 1;

        for(int i = begin; i < end; i++){
            if(nums[i] <= pivot){
                pivotIndex++;
                int tmp = nums[i];
                nums[i] = nums[pivotIndex];
                nums[pivotIndex] = tmp;
            }
        }
        nums[end] = nums[pivotIndex+1];
        nums[pivotIndex+1] = pivot;

        return pivotIndex+1;
    }


    public static void main(String[] args) {
        int[] arr = {6, 5, 2, 3, 1, 7, 9, 10, 8, 4};
        FindMedian qs = new FindMedian();
        int res = qs.findMedian(arr);
        System.out.println(res);
    }
}

2、lintcode 5 第k大元素

解题思路:
跟找中位数类似,只是要找位置为K的数,我们只要在每次划分的两个区间中,含有K的那个区间去做partition就可以了。
时间复杂度是 O(n)

另外的解法:
如果K值比较小,或者要找出前k大的所有数,可以维护一个小顶堆,然后遍历数组 : 海量数据编程常见问题及编程题

public class KthLargestElement {

    public int kthLargestElement(int k, int[] nums){
        return recursion(nums, 0, nums.length-1, nums.length+1-k);
    }

    private int recursion(int[] nums, int begin, int end, int k){

        int pivotIndex = partition(nums, begin, end);

        if(pivotIndex == k-1){
            return nums[pivotIndex];
        }else if(pivotIndex > k-1){
            return recursion(nums, begin, pivotIndex-1, k);
        }else{
            return recursion(nums, pivotIndex+1, end, k);
        }
    }


    private int partition(int[] nums, int begin, int end){

        int pivot = nums[end];
        int pivotIndex = begin - 1;

        for(int i = begin; i <= end; i++){
            if(nums[i] <= pivot){
                pivotIndex++;
                int tmp = nums[i];
                nums[i] = nums[pivotIndex];
                nums[pivotIndex] = tmp;
            }
        }

        return pivotIndex;
    }

    public static void main(String[] args) {
        int[] arr = {6, 4, 2, 5, 3, 1};
        KthLargestElement ke = new KthLargestElement();
        int res = ke.kthLargestElement(2, arr);
        System.out.println(res);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值