突破编程_C++_面试(STL 编程 priority_queue)

1 请解释 priority_queue 在 STL 中的作用,并说明它与队列(queue)的主要区别是什么?

priority_queue 在 STL 中的作用

priority_queue 是 STL(Standard Template Library)中的一个容器适配器,它提供了一种可以存储元素并根据它们的优先级进行排序的队列。它基于堆数据结构实现,允许用户为队列中的元素设置优先级,放置元素时并不是直接放到队尾,而是根据元素的优先级将其放置到合适的位置。这使得 priority_queue 在执行弹出操作时,能够按照优先级顺序返回元素,通常是返回并删除优先级最高(或最低,取决于比较函数)的元素。

默认情况下,priority_queue 使用 vector 作为其底层存储数据的容器,并在 vector 上使用了堆算法将元素构造成堆的结构。这种实现方式使得 priority_queue 在插入和删除元素时,能够保持堆的性质,从而确保弹出操作的时间复杂度为 O(log n)。

priority_queue 与队列(queue)的主要区别

(1)元素排序与访问方式:

  • queue(队列)是一种 FIFO(First-In-First-Out,先进先出)的数据结构。在队列中,元素按照它们进入队列的顺序被存储和访问。push 操作在队尾添加元素,而pop操作则从队头移除元素。
  • priority_queue 则不同,它根据元素的优先级进行排序。优先级最高的元素总是位于队列的顶部(或根据比较函数定义的位置)。因此,当执行 pop 操作时,会删除并返回优先级最高的元素,而不是最早进入队列的元素。

(2)应用场景:

  • queue 适用于需要按照元素进入的顺序进行处理的场景,如任务队列、打印队列等。
  • priority_queue 则适用于需要基于元素的优先级进行处理的场景,例如任务调度、路径搜索中的优先级队列(如 Dijkstra 算法)等。在这些场景中,优先级高的任务或路径会被优先处理。

(3)性能特点:

  • queue 的插入和删除操作在尾部进行,通常具有常数时间复杂度。
  • priority_queue 的插入操作通常具有对数时间复杂度,因为需要在保持堆性质的前提下插入元素。删除操作(即 pop)也具有对数时间复杂度,因为需要调整堆结构以维持其性质。

综上所述,priority_queue 与 queue 的主要区别在于元素的排序方式、访问方式以及它们的应用场景和性能特点。根据具体需求选择合适的数据结构是编程中的重要一环。

2 priority_queue 默认的底层实现是什么?为什么选择这种实现?

priority_queue 在 C++ STL中的默认底层实现是基于堆(heap)的数据结构,具体来说,是一个最大堆。堆是一种特殊的完全二叉树,它满足堆属性:父节点的值总是大于或等于(在最大堆中)或小于或等于(在最小堆中)其子节点的值。

选择堆作为 priority_queue 的底层实现有以下几个原因:

  • 时间复杂度优势:堆的插入和删除操作的时间复杂度都是 O(log n),其中 n 是堆中元素的数量。这种对数级别的性能对于优先队列来说是非常合适的,因为它允许在保持有序性的同时,高效地插入和删除元素。
  • 空间复杂度优势:堆通常使用数组来实现,因此其空间复杂度是 O(n),其中n是堆中元素的数量。这种连续的内存布局使得访问和操作堆元素非常高效。
  • 自然支持优先级排序:堆的特性使得它非常适合实现优先级队列。在最大堆中,堆顶元素总是最大的,这正好符合优先级队列中按优先级顺序处理元素的需求。通过调整堆的结构,可以轻松地插入新元素或删除最高优先级的元素。
  • 灵活性:虽然 priority_queue 默认使用最大堆实现,但用户也可以通过提供自定义的比较函数来定义不同的优先级排序方式。这种灵活性使得 priority_queue 能够适应各种应用场景。

综上所述,基于堆的实现方式在时间复杂度、空间复杂度以及灵活性等方面都具有优势,因此被选择作为 priority_queue 的默认底层实现。

3 假设有一个自定义的类,并且需要用这个类作为 priority_queue 的元素,并希望根据类的某个成员变量来排序,应该如何实现?

假设有一个自定义的类 Person,它有两个成员变量:name 和 age。现在想要创建一个 priority_queue,其中的 Person 对象根据 age 成员变量来排序,使得年龄最大的 Person 对象总是在队列的顶部。为了实现这一点,需要为 priority_queue 提供一个自定义的比较函数。

以下是如何实现这一点的示例代码:

#include <iostream>  
#include <queue>  
#include <string>  
#include <vector>  // 自定义的Person类  
class Person {
public:std::string name;int age;Person(const std::string& name, int age) : name(name), age(age) {}// 为了方便输出,可以重载<<运算符  friend std::ostream& operator<<(std::ostream& os, const Person& p) {os << p.name << " (" << p.age << " years old)";return os;}
};// 自定义比较函数,用于priority_queue,根据Person的年龄降序排列  
struct ComparePersonByAge {bool operator()(const Person& lhs, const Person& rhs) const {// 注意这里返回的是lhs < rhs,因为我们想要最大堆  return lhs.age < rhs.age;}
};int main() 
{// 使用自定义的比较函数创建priority_queue  std::priority_queue<Person, std::vector<Person>, ComparePersonByAge> max_age_queue;// 向队列中添加Person对象  max_age_queue.push(Person("Alice", 25));max_age_queue.push(Person("Bob", 30));max_age_queue.push(Person("Charlie", 20));// 输出队列顶部的Person对象(即年龄最大的)  std::cout << "Person with the maximum age is: " << max_age_queue.top() << std::endl;// 弹出并删除队列顶部的Person对象  max_age_queue.pop();// 再次输出队列顶部的Person对象(即次大年龄的)  std::cout << "After popping, Person with the maximum age is: " << max_age_queue.top() << std::endl;return 0;
}

上面代码的输出为:

Person with the maximum age is: Bob (30 years old)
After popping, Person with the maximum age is: Alice (25 years old)

这个示例定义了一个 ComparePersonByAge 结构体,它重载了 operator() 以提供比较逻辑。然后将这个比较函数作为第三个模板参数传递给 priority_queue,这样 priority_queue 就知道如何根据Person对象的age成员变量来排序了。注意,在比较函数中,返回lhs.age < rhs.age,这是因为需要一个最大堆,所以年龄较小的对象应该被认为“小于”年龄较大的对象。

接下来,创建了一个 priority_queue 对象 max_age_queue,并使用自定义的比较函数。接着,向队列中添加了几个 Person 对象,并输出了年龄最大的对象。最后,弹出并删除了队列顶部的对象,并输出了新的年龄最大的对象。

4 如果需要频繁地从 priority_queue 中删除和插入元素,哪种类型的 priority_queue(基于 vector 或基于 deque)会更高效?为什么?

在 C++ STL 中,priority_queue 的默认底层容器是 vector,但也可以指定其他容器,如 deque,作为底层容器。当需要频繁地从 priority_queue 中删除和插入元素时,选择基于 deque 的 priority_queue 可能会更高效。以下是原因:

(1)插入效率: 对于 vector,在尾部插入元素是高效的,因为只需要在内存末尾分配新的空间。然而,在 vector 的头部或中间插入元素会涉及到元素的移动,因为需要为新元素腾出空间。相比之下,deque 在头部和尾部插入元素都是高效的,因为 deque 是由多个块组成的双向队列,可以在其两端快速分配和释放内存。因此,如果需要在 priority_queue 中频繁插入元素,尤其是在头部或中间位置,基于 deque 的实现可能会更有效率。

(1)删除效率: priority_queue 的删除操作通常指的是删除堆顶元素(即优先级最高的元素)。这个操作在基于 vector 或 deque 的 priority_queue 中都是 O(log n) 的复杂度,因为需要重新调整堆的结构以保持其性质。然而,如果指的是删除任意位置的元素,priority_queue 本身并不直接支持高效的 O(1) 复杂度删除操作。在这种情况下,无论是基于 vector 还是 deque 的 priority_queue,都需要通过额外的数据结构或算法来实现,这可能会增加实现的复杂性和开销。

(1)内存管理: vector 在内存中是连续存储的,因此可以高效地利用内存。然而,当 vector 需要扩容时,可能需要重新分配一块更大的内存并将现有元素复制到新位置,这可能会导致性能下降。相比之下,deque 的内存管理更加灵活,它由多个固定大小的块组成,可以在需要时分配或释放块,从而避免了大量数据的移动。

综上所述,如果需要在 priority_queue 中频繁地插入元素,尤其是在头部或中间位置,并且关心内存管理的灵活性,基于 deque 的 priority_queue 可能会是更好的选择。然而,如果主要是进行尾部插入和删除堆顶元素的操作,并且内存使用效率是一个重要考虑因素,那么基于 vector 的默认 priority_queue 可能仍然是一个合理的选择。在实际应用中,应根据具体的使用场景和需求来选择合适的底层容器。

5 描述一下 priority_queue 的 top()、push() 和 pop() 方法分别做什么,并提供使用示例。

(1)top() 方法

top() 方法用于获取堆顶元素的引用,但不删除它。堆顶元素是根据优先级(在最大堆中是最大的元素)确定的。

使用示例:

#include <iostream>  
#include <queue>  int main() 
{  std::priority_queue<int> pq;  // 插入元素  pq.push(3);  pq.push(1);  pq.push(4);  // 获取堆顶元素(最大值)  int topElement = pq.top();  std::cout << "Top element: " << topElement << std::endl;  // 输出: Top element: 4  return 0;  
}

上面代码的输出为:

Top element: 4

(2)push() 方法

push() 方法用于向 priority_queue 中插入一个元素。插入后,priority_queue 会根据元素的优先级重新排列,以保持堆的性质。

使用示例:

#include <iostream>  
#include <queue>  int main() 
{  std::priority_queue<int> pq;  // 插入元素  pq.push(3);  pq.push(1);  pq.push(4);  // 输出堆的内容(从大到小)  while (!pq.empty()) {  std::cout << pq.top() << " ";  pq.pop();  }  // 输出: 4 3 1  return 0;  
}

上面代码的输出为:

4 3 1

(3)pop() 方法

pop() 方法用于从 priority_queue 中删除堆顶元素。这通常与 top() 方法一起使用,首先使用 top() 获取堆顶元素的值,然后使用 pop() 删除它。

使用示例:

#include <iostream>  
#include <queue>  int main() 
{  std::priority_queue<int> pq;  // 插入元素  pq.push(3);  pq.push(1);  pq.push(4);  // 删除并输出堆顶元素(最大值)  int topElement = pq.top();  pq.pop();  std::cout << "Removed top element: " << topElement << std::endl;  // 输出: Removed top element: 4  // 输出剩余堆的内容(从大到小)  while (!pq.empty()) {  std::cout << pq.top() << " ";  pq.pop();  }  // 输出: 3 1  return 0;  
}

上面代码的输出为:

Removed top element: 4
3 1

这些示例展示了如何使用 priority_queue 的基本方法。开发者可以根据需要调整示例中的元素类型和操作。如果有一个自定义的类,并希望根据类的某个成员变量来排序,则可以通过提供一个自定义的比较函数来实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/769857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么情况下 C++ 需要垃圾处理机制?

C&#xff0c;作为一种以性能和灵活性著称的编程语言&#xff0c;历来以其严谨的手动内存管理而闻名。然而&#xff0c;尽管C提供了丰富的工具如RAII&#xff08;Resource Acquisition Is Initialization&#xff09;原则、智能指针等来协助开发者有效地管理内存&#xff0c;但…

学会在 C++ 中使用变量:从定义到实践

C 变量 变量是用于存储数据值的容器。 在 C 中&#xff0c;有不同类型的变量&#xff08;使用不同的关键字定义&#xff09;&#xff0c;例如&#xff1a; int - 存储整数&#xff08;没有小数点&#xff09;&#xff0c;例如 123 或 -123double - 存储浮点数&#xff0c;带…

Python 命名规则

变量名 使用小写字母和下划线组合&#xff0c;例如&#xff1a;my_variable。 变量名应具有描述性&#xff0c;以便易于理解变量的用途。 函数名 使用小写字母和下划线组合&#xff0c;例如&#xff1a;my_function。 函数名应具有描述性&#xff0c;以便易于理解函数的功…

Qt登录页面

#include "mywidget.h" #include "ui_mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget) {ui->setupUi(this);//接收动图QMovie *mv new QMovie(":/pictrue/luori.gif");ui->loglab->setMovie(…

2024蓝桥杯省赛保奖突击班-Day1-二分查找_笔记_练习题解

3月22日-课堂笔记 非降序序列二分查找等于 x x x 的数下标 int find(int x, int l, int r) {while(l < r) {int mid (l r) / 2;if(x < a[mid]) r mid;else l mid 1;}return l; }非降序可重序列下标最小 ≥ x \geq x ≥x 的元素 int find(int x, int l, int r) …

修复编译RK3568-buildroot时提示不能使用root权限编译的问题

问题 使用 rk3568 的 sdk 中的 buildroot 来编译根文件系统的时候&#xff0c;出现 "you should not run configure as root (set FORCE_UNSAFE_CONFIGURE1 in environment&#xff09;" 的错误。 解决方法 根据错误提示&#xff0c;我们将 set FORCE_UNSAFE_CON…

算法打卡day16

今日任务&#xff1a; 1&#xff09;513.找树左下角的值 2&#xff09;112.路径总和 3&#xff09;113.路径总和Ⅱ 4&#xff09;106.从中序与后序遍历序列构造二叉树 5&#xff09;105.从前序与中序遍历序列构造二叉 513.找树左下角的值 题目链接&#xff1a;513. 找树左下角…

如何在软件测试行业走的更远?

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 时间往前推10年&#xff0c;IT业如日中天。 其中测试更是一个极具包容性的行业。那些希望在技术…

【云开发笔记No.9】Kanban与敏捷开发

Kanban看板起源于丰田。 看板&#xff08;Kanban&#xff09;一词来自日文&#xff0c;本义是可视化卡片。如下图所示&#xff0c;看板工具的实质是&#xff1a;后道工序在需要时&#xff0c;通过看板向前道工序发出信号——请给我需要数量的输入&#xff0c;前道工序只有得到看…

Orangedx:引领新一轮 BTCFi 浪潮

“OrangeDx 作为新一轮 BTCFi 浪潮引领者被市场寄予厚望 &#xff0c;前不久在 FinceptorApp 的平台的公开销售 20 万美元的额度仅在几秒售罄&#xff0c;而其即将以 Startup 方式登陆 Gate 平台也同样备受市场期待。” 自 Ordinals 面向市场为比特币生态带来全新的资产发行方案…

洛谷刷题 | B3621 枚举元组

枚举元组 题目描述 n n n 元组是指由 n n n 个元素组成的序列。例如 ( 1 , 1 , 2 ) (1,1,2) (1,1,2) 是一个三元组、 ( 233 , 254 , 277 , 123 ) (233,254,277,123) (233,254,277,123) 是一个四元组。 给定 n n n 和 k k k&#xff0c;请按字典序输出全体 n n n 元组&am…

翻过DP这座大山

1.AcWing 跳台阶 第一种方法:暴力搜索DFS #include <iostream> using namespace std;int dfs(int n) {if(n 1) return 1;else if(n 2) return 2;else return dfs(n-1)dfs(n-2); }int main() {int x; cin>>x;cout<<dfs(x)<<endl;return 0; }显然如…

银河麒麟系统安装设备类型选择lvm简单模式之后,数据写入导致失败导致系统重启无法正常加载

银河麒麟系统安装设备类型选择lvm简单模式之后&#xff0c;数据写入导致失败导致系统重启无法正常加载 一 系统环境1.1 系统版本信息1.2 通过镜像安装的过程中选择设备类型选择的是lvm简单模式 二 问题描述三 问题修复过程3.1 挂载ISO镜像&#xff0c;引导到字符终端界面3.2 修…

茶饮品牌抖音账号规划流量运营策划方案

【干货资料持续更新&#xff0c;以防走丢】 茶饮品牌抖音账号规划流量运营策划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 抖音运营资料合集&#xff08;完整资料包含以下内容&#xff09; 目录 冷启动期 1. 直播前期准备 - 进行DOUA/B测试&#xff0…

jupyter操作LSTM模型,词向量模型理解

1.jupyter没有torch模块&#xff0c;参考下面链接的解决办法 【jupyter notebook安装配置教程&#xff0c;导入pytorch解决No module named torch-哔哩哔哩】 https://b23.tv/jYGvyVR 2.jupyter中没有某一模块怎么办&#xff0c;可以用pycharm打开一个项目&#xff0c;在该项…

人工智能三剑客NumPy、pandas、matplotlib和Jupyter四者之间的关系

NumPy 主要用途&#xff1a;NumPy&#xff08;Numerical Python的缩写&#xff09;主要用于处理大型多维数组和矩阵的科学计算。它提供了一个高性能的多维数组对象&#xff0c;以及用于数组操作的工具。与其他三者的联系&#xff1a;NumPy是pandas和matplotlib的基础库之一。许…

【C++】报错:multi-line comment

1、C/C中的注释 在C/C语言中&#xff0c;在对源文件做预处理的时候&#xff0c;有两条基本原则&#xff1a; 凡是以“//”开头的为单行注释凡是以“\”结尾的代表此行尚未结束 于是预处理器在处理的时候会先按第二条规则&#xff0c;看每行的末尾的那个字符是不是”\”,是的…

网络七层模型之应用层:理解网络通信的架构(七)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

蓝桥杯算法基础(29)字符串匹配(RabinKarp)(KMP)(前缀树,字典树,trie,后缀数组,高度数组)

RabinKarp RabinKarp S:ABABAB m个 P:ABB n个1.朴素算法&#xff0c;挨个匹配 2.哈希法 hash->滚动哈希 c0*31^2c1*31^1c2类似于进制的求法求hash值(c0*31c1)*31c2 hash(p)o(n) hash(s)o(m*n)private static void match(String p,String s){long hash_phash(p);int …

createDocumentFragment()用法总结

createDocumentFragment()用法总结 1.描述 DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。 因为文档片段存在于内存中,并不在DOM树中,…