数组中第K个最大元素

题目

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

策略

  1. 将已给数组构建为最大堆(大顶堆,即堆顶的元素是最大的,左右子堆也满足这个条件)
  2. 堆顶删除k-1个元素
  3. 此时堆顶元素为数组中第k大的元素

代码

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        // 建立最大堆,建堆之后nums[0]是最大值
        buildMaxHeap(nums, heapSize);
        // 删掉最大的k-1个数,剩下的最大的即为所求
	// 删除的方式是将nums[0]与数组最后一个数交换,然后heapSize--
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] nums, int heapSize) {
	// 堆从0开始计算,这样与数组的下标一一对应,i的左孩子为2i+1,右孩子为2i+2
	// 堆化过程是自底向上的,此时从第一个非叶节点开始堆化
        for (int i = (heapSize / 2) - 1; i >= 0; --i) {
            maxHeapify(nums, i, heapSize);
        } 
    }

    public void maxHeapify(int[] nums, int i, int heapSize) {
	// 因为是自底向上的堆化,所以此时nums[i]的左右孩子已经是各自子堆的最大值,只需要将这三个值进行比较即可,
	// 而当发生交换(即当前节点不是三个值中最大值时),假设与左孩子交换,那么就会改变左子堆的堆化状态,此时需要再对左子堆进行堆化
        // 找到i的左右孩子,从0开始的
        int left = i * 2 + 1, right = i * 2 + 2, largest = i;
        // 先和左孩子比
        if (left < heapSize && nums[left] > nums[largest]) {
            largest = left;
        }
		// 较大的再跟右孩子比
        if (right < heapSize && nums[right] > nums[largest]) {
            largest = right;
        }
        // 发生了交换,则需要继续堆化替换的节点
        if (largest != i) {
            swap(nums, i, largest);
            maxHeapify(nums, 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), 即递归使用栈空间的空间代价。

拓展:BFPRT算法(loading...)

posted @ 2021-07-08 17:16  佑佐  阅读(223)  评论(0)    收藏  举报