【Leetcode 热题 100】215. 数组中的第K个最大元素

打印 上一主题 下一主题

主题 821|帖子 821|积分 2465

问题配景

给定整数数组                                    n                         u                         m                         s                              nums                  nums 和整数                                    k                              k                  k,请返回数组中第                                    k                              k                  k 个最大的元素。
请注意,你需要找的是数组排序后的第                                    k                              k                  k 个最大的元素,而不是第                                    k                              k                  k 个不同的元素。
你必须计划并实现时间复杂度为                                    O                         (                         n                         )                              O(n)                  O(n) 的算法办理此问题。
数据约束



  •                                         1                            ≤                            k                            ≤                            n                            u                            m                            s                            .                            l                            e                            n                            g                            t                            h                            ≤                            1                                       0                               5                                            1 \le k \le nums.length \le 10 ^ 5                     1≤k≤nums.length≤105
  •                                         −                            1                                       0                               4                                      ≤                            n                            u                            m                            s                            [                            i                            ]                            ≤                            1                                       0                               4                                            -10 ^ 4 \le nums \le 10 ^ 4                     −104≤nums≤104
解题过程

经典求第                                   k                              k                  k大,最佳方案是用随机选择算法,时间复杂度大致是                                    O                         (                         N                         )                              O(N)                  O(N) 这个量级。
考虑到标题分类在堆,那除了调用 API 快速处理的方法之外,再写一下手撕堆看成练习。
干系详细的先容,可以参考 随机快排与随机选择 以及 堆排序与堆操作。
具体实现

使用堆 API

  1. class Solution {
  2.     public int findKthLargest(int[] nums, int k) {
  3.         Queue<Integer> queue = new PriorityQueue<>();
  4.         for(int num : nums) {
  5.             queue.offer(num);
  6.         }
  7.         for(int i = 0; i < nums.length - k; i++) {
  8.             queue.poll();
  9.         }
  10.         return queue.peek();
  11.     }
  12. }
复制代码
自己实现堆

  1. class Solution {
  2.     public int findKthLargest(int[] nums, int k) {
  3.         int n = nums.length;
  4.         buildHeap(nums);
  5.         // 注意这里要修正索引
  6.         while(k - 1 > 0) {
  7.             swap(nums, 0, --n);
  8.             downAdjust(nums, 0, n);
  9.             k--;
  10.         }
  11.         return nums[0];
  12.     }
  13.     // 交换数组中的两个元素
  14.     private void swap(int[] arr, int i, int j) {
  15.         int temp = arr[i];
  16.         arr[i] = arr[j];
  17.         arr[j] = temp;
  18.     }
  19.    
  20.     // 由上而下调整元素
  21.     private void downAdjust(int[] arr, int cur, int size) {
  22.         // 数组下标从零开始,当前节点的左孩子下标为 2 * cur + 1
  23.         int child = 2 * cur + 1;
  24.         while (child < size) {
  25.             // 如果当前节点有右孩子,那么比较两个子节点的值确定潜在的交换对象
  26.             int target = child + 1 < size && arr[child + 1] > arr[child] ? child + 1 : child;
  27.             // 再与当前节点比较大小
  28.             target = arr[target] > arr[cur] ? target : cur;
  29.             // 一旦发现此次操作中无需交换,立即停止流程
  30.             if (target == cur) {
  31.                 break;
  32.             }
  33.             // 交换父子节点
  34.             swap(arr, target, cur);
  35.             // 移动工作指针
  36.             cur = target;
  37.             child = 2 * cur + 1;
  38.         }
  39.     }
  40.    
  41.     // 自底向顶建堆
  42.     private void buildHeap(int[] arr) {
  43.         int n = arr.length;
  44.         for (int i = n - 1; i >= 0; i--) {
  45.             downAdjust(arr, i, n);
  46.         }
  47.     }
  48. }
复制代码
随机选择

  1. class Solution {
  2.     // 全局变量 first 表示等于当前元素的第一个下标位置,last 表示最后一个
  3.     private static int first, last;
  4.    
  5.     public int findKthLargest(int[] nums, int k) {
  6.         return randomizedSelect(nums, nums.length - k);
  7.     }
  8.     // 交换数组中的两个元素
  9.     private void swap(int[] arr, int i, int j) {
  10.         int temp = arr[i];
  11.         arr[i] = arr[j];
  12.         arr[j] = temp;
  13.     }
  14.     private void partition(int[] arr, int left, int right, int pivot) {
  15.         // 初始化维护的指针
  16.         first = left;
  17.         last = right;
  18.         int cur = left;
  19.         // 注意循环到工作指针超越 last 即可结束流程
  20.         while (cur <= last) {
  21.             if (arr[cur] == pivot) {
  22.                 cur++; // 当前元素等于划分标准,不作处理并后移指针
  23.             } else if (arr[cur] < pivot) {
  24.                 swap(arr, first++, cur++); // 小于划分标准,交换且分别后移两个指针
  25.             } else {
  26.                 swap(arr, cur, last--); // 大于划分标准,交换并前移记录最后一个当前元素的指针
  27.             }
  28.         }
  29.     }
  30.    
  31.     private int randomizedSelect(int[] arr, int target) {
  32.         int res = 0;
  33.         for (int left = 0, right = arr.length - 1; left <= right;) {
  34.             partition(arr, left, right, arr[left + (int) (Math.random() * (right - left + 1))]);
  35.             if (target < first) {
  36.                 right = first - 1; // 当前下标小于维护的第一个下标,到左边区间中找
  37.             } else if (target > last) {
  38.                 left = last + 1; // 当前下标大于维护的最后一个下标,到右边区间中找
  39.             } else {
  40.                 res = arr[target]; // 当前下标在维护的范围内,记录结果
  41.                 break;
  42.             }
  43.         }
  44.         return res;
  45.     }
  46. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表