215 数组中第k大的数

例1. 215 返回数组中第k大的数
练习1-1. 703 数据流中第k大元素
练习1-2. 414 返回数组中第3大的数

一、返回数组中第k大的数

【题目】

在这里插入图片描述
在这里插入图片描述

【方法一】

排序,返回第k个数即可。

代码:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        Arrays.sort(nums);
        return nums[nums.length-k];
    }
}

【方法二】

【方法三】

【方法四】

在这里插入图片描述

class Solution {
    public int findKthLargest(int[] nums, int k) {
        buildMinHeap(nums, k);  //对前k个元素建立小根堆

        for(int i = k;i<nums.length;i++){
            if(nums[i]> nums[0]){
                nums[0] = nums[i]; //堆顶换成nums[i]
                adjustDown(nums, 0 , k); //重新调整堆
            }
        }
        return nums[0];
    }

    public void buildMinHeap(int[] nums, int k){
        //对数组nums的前k个元素建立小根堆
        for(int i=(k-2)/2; i>=0; i--){
            adjustDown(nums,i, k);
        }
    }

    public void adjustDown(int[] nums, int i, int len){
        //调整第i个元素,数组长度为len
        int tmp = nums[i];
        for(int j=2*i+1; j<len; j = 2*j +1){
            if(j+1 < len && nums[j+1] < nums[j]){
                j = j+1;
            }
            if(nums[j] >= tmp){
                break;
            }
            nums[i] = nums[j];
            i = j;
        }
        nums[i] = tmp;
    }
}

在这里插入图片描述
如果使用PriorityQueue来做:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        Queue<Integer> minHeap = new PriorityQueue<Integer>();

        int i=0;
        for(;i<k;i++){
            minHeap.offer(nums[i]);
        }
        for(;i<nums.length;i++){
            if(nums[i]>minHeap.peek()){
                minHeap.poll();
                minHeap.offer(nums[i]);
            }
        }
        return minHeap.peek();
    }
}

结果:
在这里插入图片描述

【方法五】

分治法。借用快排的思想。
在这里插入图片描述
代码:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        return findKth(nums, 0, nums.length-1 , k);
    }

    public int findKth(int[] nums, int low, int high, int k){
        int pos = partition(nums, low, high);
        if(pos == low+k-1){
            return nums[pos];
        }else if(pos < low+k-1){
            return findKth(nums, pos+1, high, k-(pos+1-low));
        }else{
            return findKth(nums, low, pos-1, k);
        }
    }

    public int partition(int[] nums, int low, int high){
        int pivot = nums[low];
        while(low < high){
            while(low< high && nums[high] <= pivot){
                high--;
            }
            nums[low] = nums[high];
            
            while(low< high && nums[low] >= pivot){
                low++;
            }
            nums[high] = nums[low];
        }
        nums[low] = pivot;
        return low;
    }
}

结果:
在这里插入图片描述
效果不是太满意!
为什么呢?
可能的原因是,数组不够乱,导致快排的效果变差。

改进:
在partition的时候,不是以nums[low]作为pivot,而是随机找一个数作为pivot。再进行划分。

class Solution {
    Random ran = new Random(); //全局变量

    public int findKthLargest(int[] nums, int k) {
        return findKth(nums, 0, nums.length-1 , k);
    }

    public int findKth(int[] nums, int low, int high, int k){
        int pos = partition(nums, low, high);
        if(pos == low+k-1){
            return nums[pos];
        }else if(pos < low+k-1){
            return findKth(nums, pos+1, high, k-(pos+1-low));
        }else{
            return findKth(nums, low, pos-1, k);
        }
    }

    public int partition(int[] nums, int low, int high){
        //随机找一个点,交换到最前面,作为pivot
        int x = ran.nextInt(high-low+1);   
        int pivot = nums[low+x];
        nums[low+x] = nums[low];
 
        while(low < high){
            while(low< high && nums[high] <= pivot){
                high--;
            }
            nums[low] = nums[high];
            
            while(low< high && nums[low] >= pivot){
                low++;
            }
            nums[high] = nums[low];
        }
        nums[low] = pivot;
        return low;
    }
}

在这里插入图片描述
分析:
在这里插入图片描述

二、703 数据流中第k大元素

【题目】

在这里插入图片描述
在这里插入图片描述

【分析】

跟据上一个题,只有最小堆的方法可以处理流数据。
思路完全相同。

【代码】

class KthLargest {
    PriorityQueue<Integer> minHeap;
    int k;

    public KthLargest(int k, int[] nums) {
        this.k = k;
        minHeap = new PriorityQueue<Integer>(k);
        for(int num : nums){
            add(num);        //调用的是下面的add()函数
        }
    }
    
    public int add(int val) {
        if(minHeap.size() < k){
            minHeap.offer(val);
        }else if(val > minHeap.peek()){
            minHeap.poll();
            minHeap.offer(val);
        }
        return minHeap.peek();
    }
}

这个代码写的比较简洁。因为nums[]的元素个数可能是k-1个,也可能是k个,也可能是>k个。所以不好KthLargest()函数中建立一个k大小的堆。因此通过add()来做统一处理。

结果:
在这里插入图片描述
我之前跑出过20ms的结果,击败90%的用户,但忘记截图了。另外,我看别人18ms的写法跟我完全一样。
这应该就是最优解了,除非自己实现堆可能更快点。

三、414 返回数组中第3大的数

【题目】

在这里插入图片描述
在这里插入图片描述

【分析】

要求时间复杂度为o(n)。
从大到小排序,然后找第三个大的数,不行,超时。
3趟冒泡或选择排序,不大行,因为这里第三大的数指不重复的第三大的数。
维护一个大小为k的堆,不大行,同上。
分治。不太行。因为经过partition之后,得到的pos是第几大的数还是不好确定。

方法一:三趟遍历。第一趟找最大的;第二趟找次大的;第三趟找第三大的。
方法二:一趟遍历,设置三个变量,来回倒腾。
方法三:红黑树。

方法一代码如下。方法二、三以后再补充。

【代码】

class Solution {
    public int thirdMax(int[] nums) {
        int len = nums.length;
        if(len == 1) return nums[0];
        if(len == 2) return Math.max(nums[0], nums[1]);

        int max = 0;
        int max2 = 0;
        int max3 = 0;

        //第一遍,找最大的。
        max = nums[0];
        for(int num :nums){
            if(num > max){
                max = num;
            }
        }

        //第二遍,找第二大的。
        boolean mark = false;
        int i=0;
        for(;i<len;i++){
            if(nums[i]<max){
                max2 = nums[i];
                mark = true;
                break;
            }
        }
        if(mark == false){    //如果没有第二大的,肯定就没有第三大的了。
            return max;
        }
        for(;i<len;i++){  //找到第二大的。
            if(nums[i]<max && nums[i]>max2){
                max2 = nums[i];
            }
        }

        //第三遍,找第三大的。
        mark = false;
        i=0;
        for(;i<len;i++){
            if(nums[i]<max2){
                max3 = nums[i];
                mark = true;
                break;
            }
        }
        if(mark == false){    //如果没有第三大的。
            return max;
        }
        for(;i<len;i++){  //找到第三大的。
            if(nums[i]<max2 && nums[i]>max3){
                max3 = nums[i];
            }
        }
        return max3;
    }
}

结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值