当前位置: 首页 > news >正文

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

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

  • 堆Heap
  • 二叉堆Binary Heap
  • 二叉堆
  • 二叉堆的实现
  • 插入(insert)
  • 取出堆顶(extract / delete max)
  • 优先队列(Priority Queue)
  • 实战

堆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等技术内容,立即学习

相关文章:

  • 因果系列文章(3)——有向无环图
  • 02 数据库语言SQL
  • [简化开发] mybatis plus自动填充 INSERT 与 INSERT_UPDATE 坑(记录)
  • 如何构建一款自定义的开源微服务架构?
  • SNMP工具
  • Python学习:函数中定义参数的四种方式
  • 4个非常实用的Java项目,快用起来
  • 基于Sentry打造前端性能监控平台
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • 强化学习(ICML2022)
  • CS5181E 单节锂电池充电管理IC特点及应用
  • 计算机毕业论文基于springboot的社区物业服务管理项目源码
  • Hbase大批量数据迁移之BulkLoad
  • java计算机毕业设计外贸服装订单管理系统源码+系统+数据库+lw文档+mybatis+运行部署
  • C#基于asp.net的社区团购网站
  • JS 中的深拷贝与浅拷贝
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • android 一些 utils
  • CODING 缺陷管理功能正式开始公测
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • socket.io+express实现聊天室的思考(三)
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 不上全站https的网站你们就等着被恶心死吧
  • 从tcpdump抓包看TCP/IP协议
  • 从零开始在ubuntu上搭建node开发环境
  • 对象引论
  • 复杂数据处理
  • 聚类分析——Kmeans
  • 理解在java “”i=i++;”所发生的事情
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 前端相关框架总和
  • 如何学习JavaEE,项目又该如何做?
  • 实战|智能家居行业移动应用性能分析
  • 试着探索高并发下的系统架构面貌
  • 突破自己的技术思维
  • 微信公众号开发小记——5.python微信红包
  • 一天一个设计模式之JS实现——适配器模式
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (0)Nginx 功能特性
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (SpringBoot)第七章:SpringBoot日志文件
  • (vue)页面文件上传获取:action地址
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (四)汇编语言——简单程序
  • (五)关系数据库标准语言SQL
  • (学习日记)2024.01.19
  • (转)ObjectiveC 深浅拷贝学习
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net core Swagger 过滤部分Api