给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
分析:
通过该题学习堆排序的写法,当然堆排复杂度是nlog(n)。
堆是一种完全二叉树,该树中所有节点都满足节点大小大于左右任意一个子节点的大小。从定义不难看出,根节点是树中最大的。
堆排序的原理是,建立一个大根堆,然后每次取出堆顶的元素,也就是当前最大的元素放到队列中,取出堆顶元素后,再从从堆底部取一个元素放置到堆顶,从堆顶向下迭代到新元素根据其大小应该在的位置(目的是使新的树依然满足大顶堆的定义)。每次取出当前最大值,自然就能完成排序,算法重点在于一个函数maxHeapify。
这个函数的用处是,对于堆,也就是树中的一个节点a,每次比较这个节点a和其左右子节点(b,c)的大小,把最大的节点放到a的位置,如果发生了这种交换,比如a被换到了b的位置,就要从b的位置再次向下交换。因为没有发生改变的c 和其子树依然满足大顶堆的定义,但是b因为变小了,可能不会再满足大顶堆的定义,也就是可能不满足比它的任意子节点大,此时需要再递归调用函数maxHeapify处理b节点,直到处理到堆底。
其中,二叉树是用数组存储的,规定下标为i的数组元素对应的节点,其左孩子节点放在数组中下标为2i+1的位置,右孩子放置在数组中下标为2i+2的位置。
堆排序的顺序如下:
1,建堆,具体方法是从堆底部(倒数第二层)开始,调用maxHeapify方法,即比较节点和其子节点大小,子节点大则把父节点向下放。这样底部的两层节点满足大顶堆的要求,然后用maxHeapify处理上一层的节点,直到顶部。
2,此时堆中的元素全部存储到数组中,堆顶存储在下标0的位置,把下标0和下标len-1交换,即把堆顶的最大元素取出堆,作为当前最大元素,放到数组末尾,此时堆大小减一,即堆元素存储在0~len-2的位置。然后从堆顶调用maxHeapify函数向下处理元素,使得该堆再次满足大顶堆的定义,再从堆顶部取出当前堆的最大值,放到len-2的位置,原来len-2的元素放到0上作为堆顶,堆元素此时是0到len-3的元素,重复上述过程最后数组中元素就是排序好的元素。