LeetCode 215. 数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

 

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
 

提示:

1 <= k <= nums.length <= 104
-104 <= nums[i] <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 

题解一

排序 O(n*logn)

        public int findKthLargest(int[] nums, int k) {
            // 调用Arrays.sort()将数组从小到大排序
            Arrays.sort(nums);
            // 由于取第k个最大的数,所以是第n-(k-1)个最小的数
            return nums[nums.length - k];
        }

题解二

仅对前k个数插入排序 O(n*k)

        public int findKthLargest(int[] nums, int k) {
            int[] sorted = new int[k];
            int m = 0;
            sorted[m] = nums[0];
            for (int i = 1; i < nums.length; ++i) {
                if (nums[i] > sorted[m]) {
                    if (m + 1 < k) {
                        m++;
                    }
                    int j = m;
                    for (; j > 0; --j) {
                        if (nums[i] <= sorted[j - 1]) {
                            break;
                        }
                        sorted[j] = sorted[j - 1];
                    }
                    sorted[j] = nums[i];
                } else if (m < k - 1) {
                    sorted[++m] = nums[i];
                }
            }

            return sorted[k - 1];
        }
        public int findKthLargest(int[] nums, int k) {
            for (int i = 1; i < nums.length; i++) {
                int tmp = nums[i];
                if (i < k || tmp > nums[k-1]) {
                    int j = Math.min(i, k - 1);
                    while (j > 0 && tmp > nums[j - 1]) {
                        nums[j] = nums[j - 1];
                        j--;
                    }

                    if (j != i) {
                        nums[j] = tmp;
                    }
                }
            }
            return nums[k - 1];
        }

题解三

仅对前k个数排序 O(n*k*logk)

        public int findKthLargest(int[] nums, int k) {
            Arrays.sort(nums, 0, k);
            for (int i = k; i < nums.length; ++i) {
                if (nums[i] > nums[0]) {
                    nums[0] = nums[i];
                    Arrays.sort(nums, 0, k);
                }
            }
            return nums[0];
        }

题解四

堆排序

使用Java的优先队列排序。

        public int findKthLargest(int[] nums, int k) {
            PriorityQueue<Integer> queue = new PriorityQueue<>();
            for (int n : nums) {
                queue.add(n);
                if (queue.size() > k) {
                    queue.poll();
                }
            }

            return queue.poll();
        }
public int findKthLargest(int[] nums, int k) {
            int heapSize = nums.length;
            build(nums, heapSize);
            for (int i = nums.length - 1; i > nums.length - k; --i) {
                swap(nums, 0, i);
                heapSize--;
                heapify(nums, 0, heapSize);
            }
            return nums[0];
        }

        private void build(int[] arr, int heapSize) {
            for (int i = heapSize / 2; i >= 0; --i) {
                heapify(arr, i, heapSize);
            }
        }

        private void heapify(int[] arr, int index, int heapSize) {
            int left = index << 1;
            int right = left + 1;
            int large = index;

            if (left < heapSize && arr[left] > arr[large]) {
                large = left;
            }

            if (right < heapSize && arr[right] > arr[large]) {
                large = right;
            }

            if (arr[large] > arr[index]) {
                swap(arr, index, large);
                heapify(arr, large, heapSize);
            }
        }

        private void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

题解五

二分,利用快速排序的思想。

    /**
     * 返回数组第k个最大的数。
     *
     * @param nums 目标数组
     * @param k    第k个最大的数
     * @return 返回数组第k个最大的数
     */
    public int findKthLargest(int[] nums, int k) {
      // 从0到n-1区间寻找第k个最大的数,下标-1
      return quickSelect(nums, 0, nums.length - 1, k - 1);
    }

    /**
     * 在数组区间上寻找第k大的数。
     *
     * @param arr    目标数组
     * @param left   数组的左边界
     * @param right  数组的右边界
     * @param target 待寻找的第k大数下标
     * @return 返回第k大数的值
     */
    private int quickSelect(int[] arr, int left, int right, int target) {
      // 分区,得到基准元素坐标
      int q = partition(arr, left, right);
      if (q == target) {  // 基准元素坐标是目标坐标
        // 返回目标值
        return arr[q];
      }

      if (q < target) {  // 基准元素坐标在目标坐标右边
        // 继续在右边区间寻找
        // ...left......   q   ...target....right...
        // ...| <=pivot |pivot|    >=pivot  |...
        //                     |------------|
        return quickSelect(arr, q + 1, right, target);
      } else {  // 基准元素坐标在目标坐标左边
        // 继续在左边区间寻找
        // ...left..target..   q   ..........right...
        // ...|   <=pivot   |pivot| >=pivot |...
        //    |------------|
        return quickSelect(arr, left, q - 1, target);
      }
    }

    /**
     * 将数组分区为3部分:[<=pivot][pivot][>=pivot]。返回pivot的坐标。
     *
     * @param arr   待分区的数组
     * @param left  分区的左边界
     * @param right 分区的右边界
     * @return 返回分区中间pivot的坐标
     */
    private int partition(int[] arr, int left, int right) {
      // 在[left, right]区间随机取一个数当做基准
      int q = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
      int pivot = arr[q];
      // 将基准数交换到区间最右边:[...]pivot
      swap(arr, q, right);
      // >=区间的右边界
      // |>=区间|待处理区间|pivot|
      //       |left.....right|
      int i = left - 1;
      // 从区间最左边元素到最右边-1元素
      for (int j = left; j < right; ++j) {
        // 如果当前数比基准数大
        if (arr[j] > pivot) {
          // 交换当前元素到>=区间的右边界
          // |>=区间|<=区间|待处理区间|pivot|
          // |....i|X.....|j.......|pivot|
          // |......j|.....X|......|pivot|
          swap(arr, ++i, j);
        }
      }
      // 将基准元素交换回>=区间的右边界+1
      swap(arr, i + 1, right);
      // 返回>=区间的右边界+1,即基准元素坐标
      return i + 1;
    }

    /**
     * 交换数组两个元素的值。
     *
     * @param arr 需要交换的数组
     * @param i   需要交换的元素下标1
     * @param j   需要交换的元素下标2
     */
    private void swap(int[] arr, int i, int j) {
      if (i == j) return;
      int temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
    }