二叉堆与自定义优先队列实现删除任意元素

简介: 二叉堆与自定义优先队列实现删除任意元素


堆Heap

堆(Heap)是一种高效维护集合中最大或最小元素的数据结构。

大根堆:根节点最大的堆,用于维护和查询max。

小根堆:根节点最小的堆,用于维护和查询min。

堆是一棵树,并且满足堆性质(Heap property)

  • 大根堆任意结点的关键码>=它所有子结点的关键码(父>=子)
  • 小根堆任意结点的关键码<=它所有子结点的关键码(父<=子)

二叉堆Binary Heap

二叉堆是堆的一种简易实现

  • ●本质上是一棵满足堆性质的完全二叉树

常见操作

  • ●建堆(build) : 0(N)
  • ●查询最值(get max/min) : 0(1)
  • ●插入(insert) : 0(log N)
  • ●取出最值(delete max/min) : 0(log N)

斐波那契堆、配对堆等可以做到插入0(1),左偏树、斜堆等可以支持合并

这些高级数据结构就不再讲解了

二叉堆

二叉堆的实现

二叉堆一般使用一个一维数组来存储,利用完全二叉树的结点编号特性

假设第一个元素存储在索引(下标)为1的位置的话

  • 索引为p的结点的左孩子的索引为p * 2
  • 索引为p的结点的右孩子的索引为p*2+ 1
  • 索引为p的结点的父亲的索引为p/ 2 (下取整)

假设第一个元素存储在索引(下标)为0的位置的话

  • 索引为p的结点的左孩子的索引为p*2+ 1
  • 索引为p的结点的右孩子的索引为p *2+ 2
  • 索引为p的结点的父亲的索引为(p-1)/ 2 (下取整)

插入(insert)

新元素一律插入到数组heap的尾部

  • 设插入到了索引p的位置

然后向上进行一次调整 (HeapifyUp)

  • ●若已到达根,停止
  • ●若满足堆性质(heap[p] <= heap[p/ 2]) ,停止
  • ●否则交换heap[p]和heap[p/ 2], 令p=p/ 2,继续调整

时间复杂度: 0(log N)

取出堆顶(extract / delete max)

把堆顶(heap[1]) 与堆尾(heap[n])交换, 删除堆尾( 数组最后一个元素)

然后从根向下进行一次调整(Heapify Down)

  • ●每次与左、 右子结点中较大的一个比较,检查堆性质,不满足则交换
  • ●注意判断子结点是否存在

时间复杂度: 0(log N)

优先队列(Priority Queue)

二叉堆是优先队列(Priority Queue) 一种简单、常见的实现,但不是最优实现

理论上二叉堆可以支持0(log N)删除任意元素,只需要

  • ●定位该元素在堆中的结点p (可以通过在数值与索引之间建立映射得到)
  • ●与堆尾交换,删除堆尾
  • ●从p向上、向下各进行一次调整

不过优先队列并没有提供这个方法

在各语言内置的库中,需要支持删除任意元素时,一般使用有序集合等基于平衡二叉搜索树的实现

template<typename T>
class custom_priority_queue : public std::priority_queue<T, std::vector<T>>
{
  public:
      bool remove(const T& value) {
        auto it = std::find(this->c.begin(), this->c.end(), value);
        if (it != this->c.end()) {
            this->c.erase(it);
            std::make_heap(this->c.begin(), this->c.end(), this->comp);
            return true;
        }
        else {
           return false;
        } 
     }
};

实战

23.合并K个升序链表

https://leetcode.cn/problems/merge-k-sorted-lists/

struct Node {
    int key;
    ListNode* listNode;
    Node(int key,ListNode* listNode) : key(key),listNode(listNode){}
    // bool operator < (const Node &rhs) const {
    //         return key > rhs.key;
    // }
};
class BinaryHeap{
public:
    BinaryHeap(){
        heap.push_back(Node(0,nullptr));
    }
    bool empty(){
        return heap.size() == 1;
    }
    Node getMin(){
        return heap[1];
    }
    void deleteMin(){
        swap(heap[1],heap[heap.size()-1]);
        heap.pop_back();
        Down(1);   
    }
    void insert(Node node){
        heap.push_back(node);
        Up(heap.size()-1);
    }
private:
    void Up(int p){
        int parent;
        while(p>1){
            parent = p/2;
            if(heap[p].key<heap[parent].key){
                swap(heap[p],heap[parent]);
                 p/=2;
            }
            else break;
        }
    }
    void Down(int p){
        int child ;
        int other ;
        while(p*2<heap.size()){
            child = p*2;
            other = p*2+1;
            if( other<heap.size() && heap[child].key>heap[other].key){
                child=other;
            }
            if(child<heap.size() && heap[child].key<heap[p].key){
                swap(heap[child],heap[p]);
                p=child;
            }
            else break;
        }
        // int child = p*2;
        // while(child < heap.size()) {
        //     int other = p*2+1;
        //     if(other < heap.size() && heap[other].key < heap[child].key)
        //         child = other;
        //     if (heap[child].key < heap[p].key) {
        //         swap(heap[child],heap[p]);
        //         p = child;
        //         child = p*2;
        //     }
        //     else break;
        // }
    }
    vector<Node> heap;
};
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        for(ListNode* listNode : lists){
            if(listNode == nullptr) continue;
            q.insert(Node(listNode->val,listNode));
        }
        ListNode head;
        ListNode* tail = &head;
        while(!q.empty()){
            Node node = q.getMin();
            q.deleteMin();
            tail->next=node.listNode;
            tail = tail->next;
            ListNode* next = node.listNode->next;
            if(next != nullptr) {
                q.insert(Node(next->val,next));
            }
        }
        return head.next;
    }
private:
    //priority_queue<Node> q;
    BinaryHeap q;
};

239.滑动窗口最大值

https://leetcode.cn/problems/sliding-window-maximum/

对比单调队列和堆的解法

懒惰删除法

  • 指的是需要从堆中删除一个元素(不一定是最大值)时,不直接删除,而是打上删除标记 soft delete)
  • 等未来它作为最大值被取出时,再判断它是否已经被标记,若是则抛弃它,取下一个最大值
    “懒惰"的含义一只要 要删除的元素还不是最大值,在堆里多待一会儿也无妨,未来等它会影响
    最大值正确性的时候再说吧
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ans;
        for(int i=0;i<nums.size();i++){
            q.push({nums[i],i});
            if(i >= k-1){
                while(q.top().second <= i-k) q.pop();
                ans.push_back(q.top().first);
            }
        }
        return ans;
    }
private:
    priority_queue<pair<int,int>> q;
};

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

相关文章
数据结构学习记录——堆的删除(思路图解、代码实现、逐段解析)
数据结构学习记录——堆的删除(思路图解、代码实现、逐段解析)
407 1
|
消息中间件 存储 Kafka
Fluss: First Impression
Fluss: First Impression
302 0
|
机器学习/深度学习
《深度学习梯度消失问题:原因与解决之道》
梯度消失是深度学习训练中的常见问题,严重影响模型性能。其原因包括激活函数选择不当(如Sigmoid)、网络层次过深和权重初始化不合理。解决方法有:选择合适激活函数(如ReLU及其变种)、优化权重初始化(如Xavier、He初始化)、采用批量归一化、引入残差连接、使用LSTM等特殊结构、调整学习率及预训练加微调等策略。
1190 8
|
机器人 UED Python
基于Python+Flask实现一个简易网页验证码登录系统案例
基于Python+Flask实现一个简易网页验证码登录系统案例
545 0
基于Python+Flask实现一个简易网页验证码登录系统案例
|
存储 监控 自动驾驶
云端问道15期方案教学-基于数据生命周期管理的存储成本优化
基于数据生命周期管理的存储成本优化,由王太平分享。内容涵盖对象存储的产品技术背景、介绍、核心功能及商业应用情况。重点讲解了如何通过生命周期管理,根据数据热度和访问时间,将数据智能迁移至不同存储层级(标准、低频、归档、冷归档、深度冷归档),从而实现成本最优。同时,针对不同类型的数据访问需求,提供了具体的策略建议,帮助企业在不影响业务的前提下大幅降低存储成本。
404 0
蜂窝网络中的频分多址(FDMA)与码分多址(CDMA)详解
蜂窝网络中的频分多址(FDMA)与码分多址(CDMA)详解
2457 11
|
消息中间件 物联网 定位技术
MQTT常见问题之使用 MQTT实例会报异常如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
存储 算法 安全
堆 和 优先级队列(超详细讲解,就怕你学不会)
堆 和 优先级队列(超详细讲解,就怕你学不会)
|
算法 Java C++
非启发式算法——二分、三分搜索算法
非启发式算法——二分、三分搜索算法
549 0

热门文章

最新文章