【LeetCode】常用算法之Quick Select

之前刚刚实现了快速排序算法。现在还有一个要求就是找到一个序列中第K大的数。

我们当然可以用先排序再取值的方法来做,这样的时间复杂度为O(NlogN)。或者使用heap_sort,或者优先队列,则复杂度是O(NlogK)。

那么有没有一种更加高效的方式呢?答案是肯定的。可以使用快速排序的一个变种quick_select,则平均复杂度为O(N),最坏复杂度为O(N^2)。

算法思想

通过一趟快排过后,序列将被分为(比key小的数,key,比key大的数)三部分,对这一点有疑问的同学可以参考http://blog.csdn.net/morewindows/article/details/6684558

那么假设key的下标为i,如果k < i,则第K大的数必然在快排左边的区域;如果k = i,则key就是第k大的数;如果k > i,则k必然在快排的右边的区域。

接下来递归即可得到第k大的数。

算法实现

在快排的基础上修改一下即可。代码没有做输入有效性验证,需要的时候请自行添加。

package tech.mrbcy.algorithm.quickselect;

import java.util.Scanner;

public class QuickSelect {

    // 获得第K大的数
    public int getTopK(int[] nums, int k){
        // 这里稍微注意一下k-1,由于数组下标从0开始,第k大的数下标应该是k-1,所以修改了一下
        return helper(nums, k - 1, 0, nums.length - 1);
    }

    private int helper(int[] nums, int k, int left, int right){
        if(left == right){
            // 只有1个数,那么就是这个数了
            return nums[left];
        }

        int i = left;   // 左边标志位
        int j = right;  // 右边标志位
        int key = nums[i]; // key值

        while(i < j){ 
            // j向前搜索,找到第一个小于key的值
            while(nums[j] >= key && i < j){
                j--;
            }
            // 交换nums[i]和nums[j]
            if(nums[j] < key && i < j){
                nums[i] = nums[j];
                i++; // 填坑后移
            }

            // i向后搜索,找到第一个大于key的值
            while(nums[i] <= key && i < j){
                i++;
            }
            // 交换nums[i]和nums[j]
            if(nums[i] > key && i < j){
                nums[j] = nums[i];
                j--;
            }
        }
        // 现在i 和 j相同,把key填进来
        nums[i] = key;
        // 递归左边部分和右边部分
        if(left < i && k < i){
            return helper(nums, k, left, i - 1);
        }
        if(right > j && k > j){
            return helper(nums, k, j + 1, right);
        }
        return nums[i]; // i == k
    }


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String line = scanner.nextLine();
        String[] strs = line.split(",");
        int[] nums = new int[strs.length];

        int len = strs.length;
        for(int i = 0; i < len; i++){
            nums[i] = Integer.parseInt(strs[i]);
        }

        QuickSelect qs = new QuickSelect();
        int num = qs.getTopK(nums,3);

        // 输出结果
        System.out.println(num);
    }

}

参考资料

http://blog.csdn.net/hustyangju/article/details/25399937

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值