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

【C++】 priority_queue 容器模拟实现解析

一、仿函数(Functor):比较策略的封装

仿函数是重载了()运算符的类 / 结构体,可像函数一样被调用。在 priority_queue 中,仿函数用于定义元素的比较规则,实现大顶堆 / 小顶堆的灵活切换。

// 仿函数:小于比较(用于大顶堆,默认)
template<class T>
struct less
{bool operator()(const T& left, const T& right) const{return left < right;  // 左 < 右时返回true}
};// 仿函数:大于比较(用于小顶堆)
template<class T>
struct greater
{bool operator()(const T& left, const T& right) const{return left > right;  // 左 > 右时返回true}
};

解析:

  • 仿函数的核心是operator(),它定义了两个元素的比较逻辑。

  • less<T>:当left < right时返回true,配合堆调整逻辑可实现 “大顶堆”(父节点 > 子节点)。

  • greater<T>:当left > right时返回true,配合堆调整逻辑可实现 “小顶堆”(父节点 < 子节点)。

  • 这种设计采用 “策略模式”,让 priority_queue 无需修改核心代码,仅通过更换仿函数即可切换堆类型。

二、priority_queue 类的核心实现

priority_queue(优先队列)是一种 “适配器容器”,它基于底层容器(默认 vector)实现,内部维护一个 “堆” 结构(完全二叉树),确保每次可高效获取优先级最高的元素(堆顶)。

2.1 模板参数与成员变量
template<class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue
{
public:// 成员函数声明(见后续)
private:Container _con;  // 底层容器,用于存储堆的元素
};

解析:

  • 模板参数:

    • T:存储的元素类型。

    • Container:底层容器类型(默认 vector),需支持随机访问(如[]操作)和尾部插入 / 删除(push_back/pop_back),因此通常选 vector 或 deque(list 不适合,因不支持随机访问)。

    • Compare:比较仿函数(默认less<T>),决定堆的类型(大顶堆 / 小顶堆)。

  • 成员变量_con:底层容器实际存储元素,堆的结构通过该容器的元素顺序体现(完全二叉树的层次遍历顺序)。

2.2 堆的核心调整算法

堆的核心是 “向上调整” 和 “向下调整”,用于在插入 / 删除元素后维持堆的性质(父节点优先级高于 / 低于子节点)。

2.2.1 向上调整(AdjustUp)

插入元素后,需将新元素从尾部上移到合适位置,确保堆性质。

void AdjustUp(int child)
{Compare com;  // 实例化仿函数,获取比较规则int parent = (child - 1) / 2;  // 计算父节点索引(完全二叉树性质)while (child > 0)  // 子节点索引>0,说明未到根节点{// 若父节点不符合堆规则(需与子节点交换)// 大顶堆:父 < 子 → 交换;小顶堆:父 > 子 → 交换(由com决定)if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);  // 交换父子节点child = parent;  // 子节点上移到父节点位置parent = (child - 1) / 2;  // 重新计算新的父节点}else{break;  // 已满足堆性质,退出循环}}
}

解析:

  • 适用场景:元素插入(push)后调用,新元素初始在容器尾部(child = 最后一个索引)。

  • 核心逻辑:通过(child - 1) / 2计算父节点索引,反复比较父子节点,若父节点不符合规则则交换,直到根节点或满足堆性质。

  • 仿函数作用:com(parent, child)的返回值决定是否需要交换。例如,大顶堆中comless,当parent < child时返回true,触发交换。

2.2.2 向下调整(AdjustDown)

删除堆顶元素后,需将新的根节点下移到合适位置,维持堆性质。

void AdjustDown(int parent)
{Compare com;  // 实例化仿函数int child = parent * 2 + 1;  // 计算左子节点索引(完全二叉树性质)while (child < _con.size())  // 子节点索引有效(未超出容器范围){// 找“更需要交换”的子节点(左/右子节点中优先级更高的)// 大顶堆:找更大的子节点;小顶堆:找更小的子节点(由com决定)if (child + 1 < _con.size() && com(_con[child], _con[child + 1])){child++;  // 右子节点更符合交换条件,切换到右子}// 若父节点不符合堆规则(需与子节点交换)if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);  // 交换父子节点parent = child;  // 父节点下移到子节点位置child = parent * 2 + 1;  // 重新计算新的左子节点}else{break;  // 已满足堆性质,退出循环}}
}

解析:

  • 适用场景:堆顶元素删除(pop)后调用,新根节点初始为原最后一个元素(parent = 0)。

  • 核心逻辑:

    • 先通过parent * 2 + 1计算左子节点,比较左 / 右子节点(若右子存在),选择 “更需要交换” 的子节点(由仿函数决定)。

    • 比较父节点与选中的子节点,若不符合规则则交换,反复下移直到叶子节点或满足堆性质。

  • 为什么先比较子节点?确保父节点与 “最优子节点” 交换,一次调整即可维持堆性质。

2.3 优先队列的核心接口
2.3.1 插入元素(push)
void push(const T& x)
{_con.push_back(x);  // 先将元素插入到底层容器尾部AdjustUp(_con.size() - 1);  // 对新插入的元素(尾部)进行向上调整
}

解析:

  • 步骤:先在底层容器尾部添加元素(O (1) 时间,vector 的优势),再通过AdjustUp将其移动到合适位置(O (logn) 时间,n 为元素个数)。

  • 目的:确保插入后仍维持堆的性质。

2.3.2 删除堆顶元素(pop)
void pop()
{if (empty())  // 空队列直接返回return;swap(_con[_con.size() - 1], _con[0]);  // 交换堆顶与最后一个元素_con.pop_back();  // 删除最后一个元素(原堆顶)AdjustDown(0);  // 对新堆顶(原最后一个元素)进行向下调整
}

解析:

  • 步骤:

    • 交换堆顶(索引 0)与最后一个元素(O (1));

    • 删除尾部元素(原堆顶,O (1));

    • 对新堆顶(原尾部元素)进行向下调整(O (logn))。

  • 为什么不直接删除堆顶?直接删除会导致底层容器出现 “空洞”,破坏完全二叉树结构,而交换后删除尾部可高效维护结构。

2.3.3 其他基础接口
T& top() { return _con[0]; }  // 获取堆顶元素(优先级最高的元素)
size_t size() { return _con.size(); }  // 返回元素个数
bool empty() { return _con.empty(); }  // 判断队列是否为空

解析:

  • top():堆顶元素始终在索引 0(完全二叉树的根),直接返回即可(O (1))。

  • 其他接口直接复用底层容器的功能,简洁高效。

三、测试函数与验证
void priority_queue_test()
{// 1. 大顶堆(默认使用less仿函数)priority_queue<int> max_heap;max_heap.push(8);max_heap.push(18);max_heap.push(3);max_heap.push(6);max_heap.push(10);cout << "大顶堆弹出顺序:";while (!max_heap.empty()){cout << max_heap.top() << " ";  // 输出:18 10 8 6 3max_heap.pop();}cout << endl;// 2. 小顶堆(指定greater仿函数)priority_queue<int, vector<int>, greater<int>> min_heap;min_heap.push(8);min_heap.push(18);min_heap.push(3);min_heap.push(6);min_heap.push(10);cout << "小顶堆弹出顺序:";while (!min_heap.empty()){cout << min_heap.top() << " ";  // 输出:3 6 8 10 18min_heap.pop();}cout << endl;
}

解析:

  • 大顶堆测试:使用默认less仿函数,每次弹出最大元素(18 → 10 → 8 → 6 → 3),符合大顶堆性质。

  • 小顶堆测试:显式指定greater仿函数,每次弹出最小元素(3 → 6 → 8 → 10 → 18),符合小顶堆性质。

  • 测试结果验证了堆调整算法和仿函数的正确性:插入元素后堆结构正确,弹出顺序符合优先级规则。

总结

该实现完整模拟了 C++ 标准库中 priority_queue 的核心功能,其设计特点包括:

  1. 适配器模式:基于底层容器(如 vector)实现,复用容器的存储能力,专注于堆的逻辑维护。

  2. 策略模式:通过仿函数Compare灵活切换比较规则,轻松支持大顶堆 / 小顶堆。

  3. 高效操作:插入(push)和删除(pop)均为 O (logn) 时间复杂度,获取堆顶(top)为 O (1),适合需要频繁获取最值的场景(如任务调度、贪心算法等)。

理解 priority_queue 的关键在于掌握堆的调整算法(向上 / 向下调整),以及仿函数如何影响堆的性质。


文章转载自:

http://da8JaxKz.sftrt.cn
http://XTgOLjrj.sftrt.cn
http://ZOxkvb5q.sftrt.cn
http://FAM7z4N3.sftrt.cn
http://0xnvNEQ6.sftrt.cn
http://2av0YCqe.sftrt.cn
http://Wqaf9RaB.sftrt.cn
http://IqmSiHzy.sftrt.cn
http://3ue99u5k.sftrt.cn
http://qiOKwoAO.sftrt.cn
http://KkGz2OSY.sftrt.cn
http://PEnoo5LO.sftrt.cn
http://6P72lEvb.sftrt.cn
http://R8ldeJMP.sftrt.cn
http://9a2wtcyK.sftrt.cn
http://C9FXejYw.sftrt.cn
http://yuv5TaZz.sftrt.cn
http://9hkF0oLZ.sftrt.cn
http://zD9vo6Ol.sftrt.cn
http://7fubiPeJ.sftrt.cn
http://QAcsY8TC.sftrt.cn
http://FDEdUtmk.sftrt.cn
http://ZHwYnSR8.sftrt.cn
http://U6HoTXvg.sftrt.cn
http://ETKUVnTx.sftrt.cn
http://Hv8G3O2B.sftrt.cn
http://syeatYFr.sftrt.cn
http://BgaAB2Y0.sftrt.cn
http://2F3xhZ3t.sftrt.cn
http://VntlSMJ0.sftrt.cn
http://www.dtcms.com/a/369129.html

相关文章:

  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(二十六)windows平台运行时隐藏控制台
  • leetcode 6 Z字形变化
  • 《失落之魂》M站评分仅40?国产动作类游戏究竟何去何从?
  • Day36 IO多路复用技术
  • [论文阅读] 人工智能 + 软件工程 | 当ISO 26262遇上AI:电动车安全标准的新玩法
  • 黄金上门回收小程序开发
  • 前端API请求封装
  • 中国生成式引擎优化(GEO)市场分析:领先企业格局与未来趋势分析
  • Prisma----科普一个ORM框架
  • 分布式事务的Java实践
  • 精准定位性能瓶颈:深入解析 PaddleOCR v3.2 全新 Benchmark 功能
  • The Algorithmic Foundations of Differential Privacy - 3(2)
  • 亚马逊关键词选择:从人工试错到智能闭环的进化之路
  • WIN11控制面板中丢失BitLocker,找回WIN10控制面板中的BitLocker驱动器加密设置
  • TDengine 时间函数 TODAY() 用户手册
  • 架构性能优化三板斧:从10秒响应到毫秒级的演进之路
  • LeetCode_位运算
  • 每日一算:颜色分类
  • 使用自定义固定公网URL地址远程访问公司内网OA办公系统,本地无需公网IP和专线让外网访问
  • pthread_join函数
  • 视觉项目,怎么选主机
  • AI生成内容的版权问题解析与实操指南
  • Oracle软件在主机平台的应用(课程下载)
  • TVS防护静电二极管选型需要注意哪些参数?-ASIM阿赛姆
  • 数据传输优化-异步不阻塞处理增强首屏体验
  • 通信安全员【单选题】考试题库及答案
  • 【开题答辩全过程】以 基于springboot的职业学校教务管理系统设计与实现为例,包含答辩的问题和答案
  • ImmutableMap
  • Oracle 10g → Oracle 19c 升级后问题解决方案(Pro*C 项目)
  • 使用MS-SWIF框架对大模型进行SFT微调