LeetCode | 215. 数组中的第K个最大元素
Posted 羽园原华
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }
以上是关于LeetCode | 215. 数组中的第K个最大元素的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 215. 数组中的第K个最大元素 | Python