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

原题Medium):

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

  

说明:

  你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

 

思路:小根堆排序

  这题就是著名的TopK问题,无论校招社招,这题都是互联网公司高频面试题之一。由于我在做这题之前,刚好在学习STL的priority_queue,对堆排序的实现了然于心。这题刚好也能用堆排序来解决,能遇到一题能完全靠自己解决且效率不错的算法题,这无疑是很令人兴奋的。

  总体思路就是,我们可以先利用数组的前k个元素构建一个小根堆树,然后再从数组的第k+1个元素开始,与这个小根堆树的根节点比较,如果发现是比根节点大,就用它来代替根节点的值,然后对根节点执行一次下沉操作,调整它到树的正确位置以符合小根堆的特性。直至到达数组结尾后,此时小根堆树里就已经存储好了这个数组的前K大个元素,而根节点刚好是该树最小的节点,所以根节点就是该数组第K大的元素。

  如果对什么是小根堆不理解,可以去网上搜一下相关资料,也可以参考我对STL heap的一些愚见,虽然STL heap是大根堆,但触类旁通应该不是什么难事。

  这里我是利用数组的前K个元素原地构建一个小根堆的。并没有额外分配空间去存储小根堆。

 1    void adjust_heap(vector<int>& nums, int first, int len){
 2         //从first节点开始,执行下沉操作
 3         //获取first节点的右子节点
 4         int secondChild = 2*first + 2;
 5         while(secondChild<len)
 6         {
 7             //取左右子节点中最小那个
 8             if(nums[secondChild]>nums[secondChild-1])
 9                 secondChild--;
10             //最小子节点与父节点比较,若前者较小就交换双方的值
11             if(nums[secondChild]<nums[first])
12             {
13                 int temp = nums[first];
14                 nums[first] = nums[secondChild];
15                 nums[secondChild] = temp;
16             }
17             //令子节点作为父节点,获取其右子节点
18             first = secondChild;
19             secondChild = 2*(first + 1);
20         }
21         //如果子节点索引值等于长度值,说明当前节点无右子节点,只有左子节点,比较决定是否交换
22         if(secondChild == len)
23         {
24             secondChild--;
25             if(nums[secondChild]<nums[first])
26             {
27                 int temp = nums[first];
28                 nums[first] = nums[secondChild];
29                 nums[secondChild] = temp;
30             }
31         }
32         
33     }
34     int findKthLargest(vector<int>& nums, int k) {
35         //找到小根堆中最后一个拥有子节点的节点
36         int temp = k/2-1;
37         //从该节点开始构建小根堆,自底向上
38         for(int i = temp;i>=0;i--)
39             adjust_heap(nums, i, k);
40         //然后再从数组的第k+1个元素开始,与这个小根堆树的根节点比较,直至到达数组结尾
41         for(int i = k;i<nums.size();i++)
42         {
43             //如果发现是比根节点大,就用它来代替根节点的值,然后对根节点执行一次下沉操作
44             if(nums[i]>nums[0])
45             {
46                 nums[0] = nums[i];
47                 adjust_heap(nums, 0, k);
48             }
49         }
50         //至此,小根堆树里就已经存储好了这个数组的前K大个元素
51         //根节点刚好是该树最小的节点,亦即是该数组第K大的元素
52         return nums[0];
53     }

posted @ 2019-11-03 17:11  羽园原华  阅读(177)  评论(0编辑  收藏  举报