052.堆结构与堆排序

堆结构

编号

在数组上模拟完全二叉树

对于下标为i的节点

父节点为 (i-1)/2

左孩子为 2*i + 1

右孩子为 2*i + 2

根节点编号为0,这样就会发现根节点的父节点就是(0-1)/2 = 0自己

大根堆,小根堆

大根堆的每一个子结构的最大值都在堆顶

小根堆的每一个子结构的最小值都在堆顶

下面以大根堆为例,用数组实现堆结构核心函数

sink(下沉)swim(上浮)

//  arr[] : 原始数组   
//  i : 操作节点编号  
void swim(int arr[],int i){
    while(arr[i]>arr[(i-1)/2]){
        swap(arr[i],swap((i-1)/2));
        i=(i-1)/2;
    }
}
//  arr[] : 原始数组 
//  i : 操作节点编号 
//  siz : 堆的大小
void sink(int arr[],int i,int siz){
    int l=2*i+1;
    while(l<siz){
        int best=l+1<siz&&arr[l+1]>arr[l]?l+1:l;
        if(arr[best]<=arr[i])return;
        swap(arr[i],arr[best]);
        i=best;
        l=i*2+1;
    } 
}

堆排序

我们可以在一个数组上原地建堆,实现O(N*logN)原地排序

leetcode 912 数组排序

class Solution {
    void sink(vector<int>&nums,int i,int siz){
        int l=i*2+1;
        while(l<siz){
            int best=l+1<siz&&nums[l+1]>nums[l]?l+1:l;
            if(nums[best]<=nums[i])return;
            swap(nums[best],nums[i]);
            i=best;
            l=i*2+1;
        }
    }
public:
    vector<int> sortArray(vector<int>& nums) {
        int n=nums.size();
        //自底向上原地建堆
        for(int i=n-1;i>=0;--i){
            sink(nums,i,n);
        }
        //将最大值搬到堆的(末位)右下角,并逐渐缩小堆的siz
        int siz=n;
        while(siz){
            swap(nums[0],nums[siz-1]);
            siz--;
            sink(nums,0,siz);
        }
        return nums;
    }
};

priority_queue

vector除外,体感上priority_queueunordered_map是我使用最多的容器

priority_queue就是用堆实现的,在cpp中默认是大根堆

实现O(logN)push(),pop()操作

O(1)top()查看堆顶

可以通过

priority_queue<int,vector<int>,greater<int>>pq;

获得小根堆

也可以这样实现自定义比较器

struct edge{
    int u;
    int v;
    int w;
};
auto cmp=[](auto a,auto b){return a.w < b.w;};

priority_queue<edge,vector<edge>,decltype(cmp)>pq(cmp);

习题

合并k个升序链表

leetcode 23

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *dumm=new ListNode(-1);
        ListNode *p=dumm;
        auto cmp=[](auto a,auto b){return a->val>b->val;};
        priority_queue<ListNode*,vector<ListNode*>,decltype(cmp)>pq(cmp);
        for(auto list:lists){
            if(list!=nullptr)
             pq.push(list);
        }
        while(!pq.empty()){
            auto cur=pq.top();
            pq.pop();
            p->next=cur;
            if(cur->next!=nullptr){
                pq.push(cur->next);
            }
            p=p->next;
        }
        return dumm->next;
    }
};

top K 问题

堆里最多只有k个元素,所以复杂度是O(N*logk)

优于直接排序O(N*logN),逊于快速选择算法O(N)

快速选择算法

leetcode 215

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int,vector<int>,greater<int>>pq;
        for(auto x:nums){
            pq.push(x);
            while(pq.size()>k)pq.pop();
        }
        return pq.top();
    }
};

leetcode 692

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        unordered_map<string,int>cnt;
        unordered_set<string>vis;
        for(auto x:words){
            cnt[x]++;
            vis.insert(x);
        }
        auto cmp=[&](auto a,auto b){
            if(cnt[a]!=cnt[b])return cnt[a]>cnt[b];
            return a<b;    
        };
        priority_queue<string,vector<string>,decltype(cmp)>pq(cmp);
        for(auto x:vis){
            pq.push(x);
            while(pq.size()>k)pq.pop();
        }
        vector<string>ans;
        while(pq.size()){
            ans.push_back(pq.top());
            pq.pop();
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};
posted @ 2026-01-19 21:12  射杀百头  阅读(0)  评论(0)    收藏  举报