1.题目描述
2.过程
主要分享小根堆的构建和调整过程,解题逻辑
1.在原数组的基础上建立一个大小为k的小根堆;
2.顺序遍历数组,遇到大于堆顶的元素,替换堆顶元素并向下调整堆;
3.最终返回堆顶元素nums[0], 即为第k大元素。
建堆和调整函数可以复用,可以改为大根堆,删除k-1个元素,堆顶即为题解
3.小顶堆代码
1 class Solution { 2 public: 3 int findKthLargest(vector<int>& nums, int k) { 4 buildheap(nums,k); //小根堆,大小为k,存储最大的k个元素 5 for(int i=k;i<nums.size();i++){ 6 cout<<nums[0]<<endl; 7 if(nums[i]>nums[0]){ 8 swap(nums[0],nums[i]); //更换堆顶元素 9 downadjust(nums,k); //下降调整堆 10 } 11 } 12 cout<<endl; 13 return nums[0]; 14 } 15 16 void buildheap(vector<int> &nums,int k){ //建小顶堆 17 for(int i=1;i<=k;i++){ //插入k个元素到堆中,第1~K个元素 18 int n=i; 19 while(n!=1 &&nums[n-1]<nums[n/2-1]){ //第n个元素的下标是i-1(为了整齐,i的左儿子是2i,右儿子是2i+1) 20 swap(nums[n-1],nums[n/2-1]); //根节点如果是0,则要用2i+1和2i+2 代表左右儿子 21 n=n/2; 22 } 23 } 24 } 25 void downadjust(vector<int>&nums,int k){//删除堆顶元素,插入新的元素,两个步骤合成为一个函数更简单 26 int i=1; //第一个元素,对应nums[0] 27 while(true){ //删除堆顶和插入部分在主函数,这里直接下降调整堆顶元素 28 if(2*i>k){ //第i个元素的左儿子不存在 29 break; 30 }else if(2*i==k){ //只有一个左儿子, 31 if(nums[i-1]>nums[2*i-1]){ 32 swap(nums[i-1],nums[2*i-1]); 33 } 34 break; 35 }else{ //两个儿子都在 36 int f=nums[2*i-1]<nums[2*i] ? 0 : 1; 37 if(nums[2*i-1+f]<nums[i-1]){ 38 swap(nums[i-1],nums[2*i-1+f]); 39 i=2*i+f; 40 }else{ 41 break; 42 } 43 } 44 } 45 } 46 };
4.大顶堆实现
1 class Solution { 2 public: 3 int findKthLargest(vector<int>& nums, int k) { 4 int n=nums.size(); 5 buildheap(nums,n); //大根堆,大小为nums.size() 6 for(int i=1;i<k;i++){ //删除k-1个元素 7 swap(nums[0],nums[n-i]); //更换堆顶,堆尾元素,相当于删除堆顶 8 downadjust(nums,n-i); //下降调整堆,size-- 9 } 10 return nums[0]; 11 } 12 13 void buildheap(vector<int> &nums,int k){ //建大顶堆 14 for(int i=1;i<=k;i++){ //插入k个元素到堆中,第1~K个元素 15 int n=i; 16 while(n!=1 &&nums[n-1]>nums[n/2-1]){ //第n个元素的下标是i-1(为了整齐,i的左儿子是2i,右儿子是2i+1) 17 swap(nums[n-1],nums[n/2-1]); //根节点如果是0,则要用2i+1和2i+2 代表左右儿子 18 n=n/2; 19 } 20 } 21 } 22 void downadjust(vector<int>&nums,int k){//删除堆顶:堆顶和堆尾元素交换,直接下降调整堆顶元素 23 int i=1; //第一个元素,对应nums[0] 24 while(true){ 25 if(2*i>k){ //第i个元素的左儿子不存在,说明到底了 26 break; 27 }else if(2*i==k){ //只有一个左儿子, 28 if(nums[i-1]<nums[2*i-1]){ 29 swap(nums[i-1],nums[2*i-1]); 30 } 31 break; 32 }else{ //两个儿子都在,选一个大的交换 33 int f=nums[2*i-1]>nums[2*i] ? 0 : 1; 34 if(nums[2*i-1+f]>nums[i-1]){ 35 swap(nums[i-1],nums[2*i-1+f]); 36 i=2*i+f; 37 }else{ 38 break; 39 } 40 } 41 } 42 } 43 }; 44 /* 45 1.删除堆顶 :堆顶和堆尾元素交换,然后从堆顶 下降调整,堆大小-1; 46 2.插入元素 :插入元素放到堆尾,堆大小+1,向上调整堆即可; 47 */ 48 49 作者:xi-jiu 50 链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/solution/-by-xi-jiu-9dtr/ 51 来源:力扣(LeetCode) 52 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
5.快速选择
快速选择:每次选择一个关键字(通常范围内第一个),利用快排的思想找到它在有序数组中的位置index,比较和k的大小进一步缩小查找范围,
快排一次后:左侧的数字都比关键字小,右侧都比关键字大;
1 //快排的思想,降序 2 class Solution { 3 public: 4 int findKthLargest(vector<int>& nums, int k) { 5 int l=0,r=nums.size()-1; 6 swap(nums[0],nums[nums.size()/2]); //防止有序测例,退化为线性 7 while(l<r){ 8 int mid =partition(nums,l,r); 9 if(mid==k-1){ //找到第k大的数 10 return nums[mid]; 11 }else if(mid>=k){ 12 r=mid-1; 13 }else{ 14 l=mid+1; 15 } 16 } 17 return nums[l]; 18 } 19 int partition(vector<int> &nums,int l,int r){ //降序快排 20 int pivot=l; 21 while(l<r){ 22 while(l<r && nums[r] < nums[pivot] ) r--; //右侧的数只能小于 支点 23 while(l<r && nums[l] >= nums[pivot] ) l++; //左侧的数可以大于等于 24 swap(nums[l],nums[r]); 25 } 26 swap(nums[pivot],nums[l]); 27 return l; 28 } 29 };
分类:
LeetCode
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!