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

144 阅读2分钟

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

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

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

 

示例 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 <= 105 -104 <= nums[i] <= 104

思路 :对数组排序,然后根据升序或降序选择倒数第k个或第k个元素返回即可

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


复杂度分析:

  • 时间复杂度:O(nlogn),这里 n 是数组的长度,算法的性能消耗主要在排序,JDK 默认使用快速排序,因此时间复杂度为 O(nlogn);
  • 空间复杂度:O(logn),这里认为编程语言使用的排序方法是「快速排序」,空间复杂度为递归调用栈的高度,为 logn。

思路2 :基于堆排序的选择方法

  • 建立一个大根堆,做 k - 1 次删除操作后堆顶元素就是我们要找的答案
class Solution {
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);
        //建堆完毕后,nums【0】为最大元素。逐个删除堆顶元素,直到删除了k-1个。
        for (int i = nums.length - 1; i > nums.length - k ; i--) {
            //先将堆的最后一个元素与堆顶元素交换,由于此时堆的性质被破坏,需对此时的根节点进行向下调整操作。
            swap(nums, 0, i);
            //相当于删除堆顶元素,此时长度变为nums.length-2。即下次循环的i
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] a, int heapSize) {
        //从最后一个父节点位置开始调整每一个节点的子树。数组长度为heasize,因此最后一个节点的位置为heapsize-1,所以父节点的位置为heapsize-1-1/2。
        int parent = (heapSize-2)/ 2;
        for (int i = parent; i >= 0; i--) {
            maxHeapify(a, i, heapSize);
        } 
    }

    public void maxHeapify(int[] a, int i, int heapSize) {      //调整当前结点和子节点的顺序。
        //leftright表示当前父节点i的两个左右子节点。
        int left = i * 2 + 1, right = i * 2 + 2, largest = i;
        //如果左子点在数组内,且比当前父节点大,则将最大值的指针指向左子点。
        if (left < heapSize && a[left] > a[largest]) {
            largest = left;
        } 
        //如果右子点在数组内,且比当前父节点大,则将最大值的指针指向右子点。
        if (right < heapSize && a[right] > a[largest]) {
            largest = right;
        }
        //如果最大值的指针不是父节点,则交换父节点和当前最大值指针指向的子节点。
        if (largest != i) {
            swap(a, i, largest);
            //由于交换了父节点和子节点,因此可能对子节点的子树造成影响,所以对子节点的子树进行调整。
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

复杂度分析

  • 时间复杂度:O(nlogn),建堆的时间代价是 O(n),删除的总代价是O(klogn),因为 k < n,故渐进时间复杂为 O(n+klogn)=O(nlogn)。
  • 空间复杂度:O(logn),即递归使用栈空间的空间代价。