LeetCode 215 Kth Largest Element in an Array (快速选择 或 堆排 或 二分)

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

Example 1:

Input: [3,2,1,5,6,4] and k = 2
Output: 5

Example 2:

Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4

Note: 
You may assume k is always valid, 1 ≤ k ≤ array's length.

题目链接:https://leetcode.com/problems/kth-largest-element-in-an-array/

题目分析:求第k大

方法1:快速选择,基于快速排序的思想,时间复杂度O(n)

哨兵选左端点,31ms,时间击败32.5%

哨兵选中点,1ms,时间击败99%

class Solution {
    
    public void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    
    public int quickSelect(int[] nums, int left, int right, int k) {
        if (left > right) {
            return 0;
        }
        int j = right, i = left;
        swap(nums, left, (left + right) >> 1);
        while (i < j) {
            while (i < j && nums[j] >= nums[left]) {
                j--;
            }
            while (i < j && nums[i] <= nums[left]) {
                i++;
            }
            if (i < j) {
                swap(nums, i, j);
            }
        }
        swap(nums, i, left);
        int cnt = i - left + 1;
        if (cnt == k) {
            return nums[i];
        } else if (cnt < k) {
            return quickSelect(nums, i + 1, right, k - cnt);
        } else {
            return quickSelect(nums, left, i - 1, k);
        }
    }
    
    public int findKthLargest(int[] nums, int k) {
        return quickSelect(nums, 0, nums.length - 1, nums.length - k + 1);
    }
}

方法2:堆排,先构造大顶堆,堆顶和堆尾交换k-1次后的堆顶即为第k大

3ms,时间击败88%

class Solution {
    
    public void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    
    public int heapSelect(int[] nums, int k) {
        int n = nums.length;
        for (int i = n / 2 - 1; i >= 0; i--) {
            ajustHeap(nums, i, n);   
        }
        int ansPos = n - 1;
        for (; ansPos > 0 && k > 0; ansPos--, k--) {
            swap(nums, 0, ansPos);
            ajustHeap(nums, 0, ansPos);
        }
        return nums[0];
    }
    
    public void ajustHeap(int[] nums, int pos, int n) {
        int tmp = nums[pos];
        for (int i = pos << 1 | 1; i < n; i = i << 1 | 1) {
            if (i + 1 < n && nums[i + 1] > nums[i]) {
                i++;
            }
            if (nums[i] > tmp) {
                nums[pos] = nums[i];
                pos = i;
            } else {
                break;
            }
        }
        nums[pos] = tmp;
    }
    
    public int findKthLargest(int[] nums, int k) {
        return heapSelect(nums, k - 1);
    }
}

方法3:二分,本题的输入均为32位整型,故可以直接对数字二分,先找到一个最小的x使得小于等于它的数字个数大于等于k,然后再找原数组中大于等于且与x最接近的数,即为答案,时间复杂度O(nlog2(常数)) => O(n),5ms,击败70%

class Solution {
    public int n;
    public int getLess(int x, int[] nums) {
        int lessNum = 0;
        for (int i = 0; i < n; i++) {
            if (nums[i] <= x) {
                lessNum++;
            }
        }
        return lessNum;
    }
    
    public int getMid(int[] nums, int ma, int mi, int k) {
        int l = mi, r = ma, mid = 0, ans = 0, lessNum = 0;
        while (l <= r) {
            mid = (l >> 1) + (r >> 1) + (l & r & 1);
            lessNum = getLess(mid, nums);
            if (lessNum <= k) {
                l = mid + 1;
            } else {
                r = mid - 1;
                ans = mid;
            }
        }
        return ans;
    }
    
    public int findKthLargest(int[] nums, int k) {
        n = nums.length;
        int ma = nums[0], mi = nums[0];
        for (int i = 0; i < n; i++) {
            ma = Math.max(ma, nums[i]);
            mi = Math.min(mi, nums[i]);
        }
        int tmid = getMid(nums, ma, mi, n - k);
        int mid = nums[0], minDiff = ma - mi;
        for (int i = 0; i < n; i++) {
            if (nums[i] >= tmid && nums[i] - tmid < minDiff) {
                minDiff = nums[i] - tmid;
                mid = nums[i];
            }
        }
        return mid;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值