【C++/STL深度剖析】priority_queue 最全解析(什么是priority_queue? priority_queue的常用接口有哪些?)

目录

一、前言

二、如何区分【优先级队列】与【队列】?

三、priority_queue的介绍 

四、priority_queue 的构造 

五、priority_queue 的常用接口 

💧push

💧pop 

💧size  

💧top  

💧empty   

💧swap    

六、priority_queue 的模拟实现 

🥝 堆的向上调整算法

🥝 堆的向下调整算法

🥝 priority_queue 的实现

七、priority_ququq 中的仿函数 

八、priority_queue 的常考面试 

九、总结 

十、共勉 


一、前言

        优先级队列 priority_queue容器适配器中的一种,常用来进行对数据进行优先级处理,比如优先级高的值在前面,这其实就是数据结构中的 堆,它俩本质上是一样东西,底层都是以数组存储的完全二叉树,不过优先级队列 priority_queue 中加入了 泛型编程 的思想,并且属于 STL 中的一部分。本就就来详细的讲解一下 priority_queue 是如何使用的!!

 二、如何区分【优先级队列】与【队列】?

首先要注意的就是别与 队列(queue)搞混了队列是一种先进先出(First in First out,FIFO)的数据类型。每次元素的入队都只能添加到队列尾部,出队时从队列头部开始出

优先级队列(priority_queue)其实,不满足先进先出的条件,更像是数据类型中的“堆”优先级队列每次出队的元素是队列中优先级最高的那个元素,而不是队首的元素。这个优先级可以通过元素的大小等进行定义。比如定义元素越大优先级越高,那么每次出队,都是将当前队列中最大的那个元素出队。

三、priority_queue的介绍 

        priority_queue是C++标准库中的一个容器适配器(container adapter),用于实现优先队列(priority queue)的数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级进行排序,每次取出的元素都是优先级最高的。它的底层实现通常使用堆(heap)数据结构。

  • 在C++中,priority_queue模板类定义在<queue>头文件中,可以通过指定元素类型和比较函数来创建不同类型的优先队列。比较函数用于确定元素的优先级,可以是函数指针、函数对象或Lambda表达式。
  •  priority_queue被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类。元素从特定容器的”尾部“弹出,其称为优先级队列的顶部

⭕需要注意的是,默认情况下,priority_queue使用std::less作为比较函数,即元素的优先级按照从大到小的顺序排列。如果需要按照从小到大的顺序排列,可以使用std::greater作为比较函数。 

四、priority_queue 的构造 

        优先级队列 默认使用 vector 作为底层存储数据的容器,在 vector 上又使用了 堆算法 将 vector 中的元素构造成堆的结构,因此 priority_queue 就是 ---- 堆,所以在需要用到 堆 的地方,都可以考虑使用 priority_queue

注意:默认情况下 priority_queue 是 大堆

 优先级队列的构造方式有两种:直接构造一个空对象 和 通过迭代器区间进行构造

 (1)直接构造一个空对象

#include <iostream>
#include <vector>
#include <queue>	//注意:优先级队列包含在 queue 的头文件中using namespace std;int main()
{priority_queue<int> pq;	//直接构造一个空对象,默认为大堆cout << typeid(pq).name() << endl;	//查看类型return 0;
}

注意: 默认比较方式为 less,最终为 优先级高的值排在上面(大堆


(2)通过迭代器区间构造对象 

#include <iostream>
#include <vector>
#include <queue>	//注意:优先级队列包含在 queue 的头文件中using namespace std;int main()
{vector<char> vc = { 'a','b','c','d','e' };priority_queue<char, deque<char>, greater<char>> pq(vc.begin(), vc.end());	//现在是小堆cout << typeid(pq).name() << endl;	//查看类型cout << "==========================" << endl;while (!pq.empty()){//将小堆中的堆顶元素,依次打印cout << pq.top() << " ";pq.pop();}return 0;
}

注意: 将比较方式改为 greater 后,生成的是 小堆,并且如果想修改比较方式的话,需要指明模板参数2 底层容器,因为比较方式位于模板参数3,不能跳跃缺省(遵循缺省参数规则) 


测试数据:【27,15,19,18,28,34,65,49,25,37】 分别生成 大堆 小堆 

大堆 :

vector<int> v = { 27,15,19,18,28,34,65,49,25,37 };
priority_queue<int, vector<int>, less<int>> pq(v.begin(), v.end());
//priority_queue<int> pq(v.begin(), v.end());	//两种写法结果是一样的,默认为大堆


小堆: 

vector<int> v = { 27,15,19,18,28,34,65,49,25,37 };
priority_queue<int, vector<int>, greater<int>> pq(v.begin(), v.end());	//生成小堆


五、priority_queue 的常用接口 

优先级队列的接口也很简单,唯一需要注意的是,每插入一个元素,都会进行排序,主要看你构建的是大堆还是小堆。

💧push

在优先级队列的尾部插入 一个 新的元素,每次插入前调用 堆排序算法,将其重新排序在堆中的位置 

void test_priority_queue()
{priority_queue<int, vector<int>, greater<int>> pq; // 构建小堆pq.push(2);pq.push(10);pq.push(7);pq.push(3);pq.push(5);cout << pq.top() << endl;pq.push(0);cout << pq.top() << endl;
}
  • 可以看到,我们构建的是小堆,排好序以后,队头(堆顶)的数据就是 2 ,重新插入一个 0 以后,会重新排序,此时队头(堆顶)的数据就是 0 


💧pop 

 删除位于优先级队列顶部的元素,有效的将其大小减少 1,其实就是删除队头元素

void test_priority_queue()
{priority_queue<int, vector<int>, greater<int>> pq; // 构建小堆pq.push(2);pq.push(10);pq.push(7);pq.push(3);pq.push(5);pq.push(1);cout << pq.top() << endl;pq.pop();cout << pq.top() << endl;
}


💧size  

返回优先级队列的元素数量 

void test_priority_queue()
{priority_queue<int, vector<int>, greater<int>> pq; // 构建小堆pq.push(2);pq.push(10);pq.push(7);pq.push(3);pq.push(5);pq.push(1);cout << pq.size() << endl;}


💧top  

返回 优先级队列中顶部元素的常量引用,顶部元素实在优先级队列中比较高的元素 

void test_priority_queue()
{priority_queue<int, vector<int>, greater<int>> pq; // 构建小堆pq.push(2);pq.push(10);pq.push(7);pq.push(3);pq.push(5);pq.push(1);cout << pq.top() << endl;
}


💧empty   

 测试容器是否为空

优先级队列同样也不支持迭代器的遍历,所以可以使用 empty 配合 toppop 来实现 

void test_priority_queue()
{priority_queue<int, vector<int>, greater<int>> pq; // 构建小堆pq.push(2);pq.push(10);pq.push(7);pq.push(3);pq.push(5);pq.push(1);while (!pq.empty()) {cout << pq.top() << " ";pq.pop();}
}


💧swap    

交换两个优先级队列的内容

void test_priority_queue()
{priority_queue<int> pq1; // 构建小堆pq1.push(2);pq1.push(1);pq1.push(7);pq1.push(3);priority_queue<int> pq2;pq2.push(5);pq2.push(1);pq2.push(2);pq1.swap(pq2);while (!pq1.empty()) {cout << pq1.top() << " ";pq1.pop();}cout << endl;while (!pq2.empty()) {cout << pq2.top() << " ";pq2.pop();}
}

 注意:交换的前提必须是 两个队列构建都是相同的类型的堆


六、priority_queue 的模拟实现 

我们知道「priority_ queue」 的底层就是,所以在模拟实现之前,要先实现堆的调整算法。 

🥝 堆的向上调整算法

假设我们现在已经有一个大堆, 我们需要在堆的末尾插入数据,然后对其进行调整,使其仍然保持大堆的结构。

堆的向上调整算法基本思想: (以建 大堆为例) 

  • 将要插入的数据与其父节点数据比较 
  • 若插入节点的数据大于父节点的数据,则交换位置,交换以后,插入的节点继续进行向上调整(此时该节点叔和上面的父节点比

来看一个动图 : 

  • (1) 首先我们在该大堆的末尾插入数据 60。 
  • (2) 我们先将 60 与其父结点 27 进行比较,发现 60 比其父结点大,则交换父子结点的数据,并继续进行向.上调整。
  • (3) 此时将 60 与其父结点 28 进行比较,发现 60 还是比父结点大,则继续交换父子结点的数据,并继续进行向上调整。
  • (4) 这时再将 60 与其父结点 65 进行比较,发现 60 比其父结点小,则停止向上调整,此时该树已经就是大堆了。

堆 的向上调整代码: 

void AdjustUp(vector<int>& v1, int child)
{int parent = ((child - 1) >> 1); // 通过child计算parent的下标while (child > 0) //调整到根结点的位置截止{if (v1[parent] < v1[parent]){// 父节点与子节点交换swap(v1[child], v1[parent]);// 继续向上调整child = parent;parent = ((child - 1) >> 1);}else {break;}}
}

🥝 堆的向下调整算法

以小堆为例,向下调整算法有一个前提,就是待向下调整的结点的左子树和右子树必须都为小堆 

 堆的向下调整算法基本思想: (以建小堆为例)

  • 从根节点开始,选出左右孩子节点中值较小的一个,让父亲与较小的孩子比较。 
  • 若父亲大于此孩子那么交换,交换以后,继续进行向下调整;若父亲小于此孩子,则不交换,停止向下调整,此时该树已经是小堆

如下图所示:将该二叉树从根结点开始进行向下调整。(此时根结点的左右子树已经是小堆)

将 27 与其较小的子结点 15 进行比较,发现 25 其较小的子结点大,则交换这两个结点的数据,并继续进行向下调整。 

此时再将27与其较小的子结点18进行比较,发现27其较大的子结点大,则再交换这两个结点的数据,并继续进行向下调整。 

此时再将27与其较小的子结点25进行比较,发现27其较小的子结点大,则再交换这两个结点的数据,并继续进行向下调整。

此时该树,就因该是小堆啦 

堆的向下调整代码: 

//堆的向下调整(小堆)
void AdjustDown(vector<int>& v1, int n, int parent)
{//child记录左右孩子中值较大的孩子的下标int child = 2 * parent + 1;//先默认其左孩子的值较小while (child < n){if (child + 1 < n && v1[child + 1] < v1[child])//右孩子存在并且右孩子比左孩子小{child++;//较小的孩子改为右孩子}if (v1[child] < v1[parent])//左右孩子中较小孩子的值比父结点还小{//将父结点与较小的子结点交换swap(v1[child], v1[parent]);//继续向下进行调整parent = child;child = 2 * parent + 1;}else{break;}}
}

🥝 priority_queue 的实现

通过对「priority_ _queue」 的了 解其底层结构就是堆,此处只需对堆的调整算法和常用接口进行通用的封装即可。 

namespace xas
{// 比较方式(使内部结构为大堆)template<class T>struct less{bool operator()(const T& x, const T& y) const{return x < y;}};// 比较方式(使内部结构为小堆)template<class T>struct greater{bool operator()(const T& x, const T& y) const{return x > y;}};// 优先级队列 --- 大堆 < --- 小堆 >template<class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{Compare _comFunc; // 比较方式public:// 创造空的优先级队列priority_queue(const Compare& comFunc = Compare()):_comFunc(comFunc){}// 以迭代器区间来建堆template <class InputIterator>priority_queue(InputIterator first, InputIterator last, const Compare& comFunc = Compare()): _comFunc(comFunc){while (first != last){_con.push_back(*first);++first;}// 建堆for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i){AdjustDown(i);}}// 堆的向上调整void AdjustUp(int child){int parent = (child - 1) / 2; // 通过child计算parent的下标while (child > 0){if (_comFunc(_con[parent], _con[child])) // 通过所给比较方式确定是否需要交换结点位置{swap(_con[parent], _con[child]); // 将父结点与孩子结点交换child = parent; //继续向上进行调整parent = (child - 1) / 2;}else{break;}}}// 插入元素到队尾(并排序)// 在容器尾部插入元素后进行一次向上调整算法void push(const T& x){_con.push_back(x);AdjustUp(_con.size() - 1);}// 堆的向下调整void AdjustDown(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _comFunc(_con[child], _con[child + 1])){++child;}if (_comFunc(_con[parent], _con[child])) //通过所给比较方式确定是否需要交换结点位置{swap(_con[parent], _con[child]); //将父结点与孩子结点交换parent = child; //继续向下进行调整child = parent * 2 + 1;}else{break;}}}// 删除队头元素(堆顶元素)// 将容器头部和尾部元素交换,再将尾部元素删除,最后从根结点开始进行一次向下调整算法void pop(){assert(!_con.empty());swap(_con[0], _con[_con.size() - 1]);_con.pop_back();AdjustDown(0);}// 访问队头元素(堆顶元素)const T& top(){return _con[0];}// 获取队列中有效元素个数size_t size(){return _con.size();}// 判断队列是否为空bool empty(){return _con.empty();}private:Container _con;};// 测试函数void test_priority_queue1(){priority_queue<int> pq1; // 构建大堆pq1.push(2);pq1.push(1);pq1.push(7);pq1.push(5);pq1.push(3);pq1.push(10);pq1.push(4);while (!pq1.empty()) {cout << pq1.top() << " ";pq1.pop();}}// 测试函数void test_priority_queue2(){priority_queue<int, vector<int>, greater<int>> pq2; // 构建小堆pq2.push(2);pq2.push(1);pq2.push(7);pq2.push(5);pq2.push(3);pq2.push(10);pq2.push(4);while (!pq2.empty()) {cout << pq2.top() << " ";pq2.pop();}}
}

七、priority_ququq 中的仿函数 

        在priority_queue中,仿函数用于比较元素的优先级,并根据其返回值确定它们在队列中的位置。默认情况下,priority_queue使用std::less作为仿函数,也就是将元素按照从大到小的顺序进行排序。 

你可以使用不同的仿函数来改变元素的排序方式。以下是一些常见的仿函数: 

  • std::less<T>对于基本数据类型和自定义类型,默认使用 < 运算符进行比较,按照从大到小的顺序排序。
  • std::greater<T>对于基本数据类型和自定义类型,默认使用 > 运算符进行比较,按照从小到大的顺序排序。

除了上述默认提供的仿函数外,你还可以自定义仿函数来实现自定义的元素比较规则。自定义仿函数需要满足严格弱排序(Strict Weak Ordering)的要求,即: 

  • 比较关系必须是可传递的(transitive):对于任意元素a、b和c,如果a与b比较相等,b与c比较相等,则a与c比较也相等。
  • 比较关系不能是部分顺序(partial order):对于任意元素a和b,它们不能同时大于、小于或等于彼此。
  • 比较关系必须是可比较的(comparable):比较关系的结果必须对所有元素定义明确的大小关系。

以下这段代码,演示了如何自定义一个仿函数来实现元素的自定义排序方式: 

// 创建一个 身份结构体
struct Person
{string name;int age;// 构造函数Person(const string& n, int a):name(n), age(a){}
};// 自定义仿函数
struct Compare
{// 函数重载()bool operator()(const Person& p1, const Person& p2)const{//按照年龄从下到大排序return p1.age > p2.age;}
};int main()
{priority_queue<Person, vector<Person>, Compare> pq;pq.push(Person("Alice", 25));pq.push(Person("Bob", 30));pq.push(Person("Charlie", 20));while (!pq.empty()) {Person p = pq.top();pq.pop();cout << p.name << " - " << p.age << endl;}return 0;
}

输出结果为: 

Charlie - 20
Alice - 25
Bob - 30

在上面的代码中,我们定义了一个名为Compare的结构体,重载了函数调用运算符operator()按照Person对象的age成员进行比较。然后,我们将Compare作为优先队列的仿函数类型,并插入3个Person对象到优先队列中。最后,我们按照自定义的排序方式依次取出元素并输出。 


八、priority_queue 的常考面试 

优先级队列(堆)可以用来进行排序和解决 Top-K 问题,比如 查找第 k 个最大的值 就比较适合使用优先级队列 

215. 数组中的第K个最大元素 - 力扣(LeetCode) 

思路:利用数组建立大堆,数组从大到小排序,删除前k-1个元素,选出队头即可

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {// 将数组中的元素 放入 优先级队列--堆priority_queue<int> p(nums.begin(),nums.end());// 将优先级队列 中的前K-1 个元素删除掉for(int i = 0;i< k-1;i++){p.pop();}return p.top();}
};

 九、总结 

优先队列是一种特殊的队列,其中存储的元素按照一定的优先级进行排列。在priority_queue中,优先级最高的元素能够快速被访问和删除。

  • 首先,我们介绍了priority_queue的概念和特点。它是基于堆(heap)这种数据结构实现的,通常使用最大堆来进行内部排序。最大堆保证了根节点的值最大,并且任意节点的值大于或等于其子节点的值。这种特性使得优先队列能够高效地访问和删除具有最高优先级的元素。
  • 接着,我们深入探讨了priority_queue的使用方法。基本操作包括插入元素、删除元素、访问元素和检查队列是否为空。
  • 底层结构是priority_queue的关键部分,它通常使用堆来实现。在堆中,通过使用数组的索引来表示节点之间的关系,能够快速定位和操作元素。
  • 最后,我们探讨了在priority_queue中使用的仿函数。仿函数用于确定元素之间的优先级,决定元素在队列中的位置。默认情况下,priority_queue使用std::less仿函数进行比较,对元素进行降序排列。你还可以选择其他仿函数或自定义仿函数来实现不同的排序方式。

十、共勉 

      以下就是我对 【priority_queue优先级队列】 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 C++STL 的理解,请持续关注我哦!!!   

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

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

相关文章

YOLOv5改进 | 损失函数 | EIoU、SIoU、WIoU、DIoU、FocuSIoU等多种损失函数

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a; 《YOLOv5入门 …

Nginx-rewrite模块详解

文章目录 前言一、ngx_http_rewrite_module模块二、指令详解1.break案例 2. if指令案例 3. return指令案例&#xff1a;return 的优先级 4. rewrite指令案例 5. set指令 总结 前言 nginx一些场景&#xff0c;我们需要使用rewrite模块。例如域名改了&#xff0c;为了利用网站se…

什么是YUV和IPB,PTS和DTS,视频编码解码过程

YUV 是一种在视频处理和压缩中常用的颜色空间。 它将图像的亮度 (Y) 与色度 (U 和 V) 成分分开。 这种分离对视频压缩和广播非常有益&#xff0c; 因为人眼对亮度变化比对颜色变化更敏感。 YUV 组件简介 Y (亮度)&#xff1a;表示图像的亮度或灰度信息。U (色度)&#xff1a;…

git - 变基、合并、重置后的回退操作

介绍 ORIG_HEAD 是 Git 中一个特殊的引用&#xff0c;用来指向某些操作&#xff08;如合并、变基、重置等&#xff09;前的状态。它可以用来快速恢复到这些操作之前的状态。下面是 ORIG_HEAD 的用法和一些常见的使用场景&#xff1a; 用法 1. 查看 ORIG_HEAD 使用以下命令查…

程序员需要具备的核心竞争力

随着IT人才的饱和&#xff0c;互联网就业形势越严峻。 作为一名工程师&#xff0c;需要具备哪些基本素养与能力&#xff0c;才能够应对这样的就业环境&#xff1f; 按照优先级排序如下&#xff1a; 1 业务理解、需求沟通能力 业务理解与需求沟通看似是技术经理、架构师需要…

四模卫星导航模块-高精度多模卫星定位技术

GPS02-UBX模块是思为无线基于u-blox最新款IC M10系列研发的一款全球卫星系统定位GPS/北斗模块。它可以支持BDS/GPS/GLONASS/Galileo四模定位(四选三&#xff0c;BDS和GLONASS不能同时使用)。GPS02-UBX模块能看到更多的卫星&#xff0c;有着更高的灵敏度&#xff0c;从而为用户获…

大模型日报 2024-07-04

大模型日报 2024-07-04 一、大模型资讯 大厂高管转战 AI 创业盘点&#xff1a;超 25 人&#xff0c;覆盖全产业链&#xff0c;AI 应用最热门 涉及多家互联网大厂高管加入生成式 AI 创业&#xff0c;涵盖多个领域及融资情况。 腾讯云发布自研大数据高性能计算引擎 Meson 软硬一体…

Linux脚本自动安装 docker

使用官方安装脚本自动安装 需使用 root 或sudu 权限账户安装 安装命令如下&#xff1a; curl -fsSL https://test.docker.com -o install-docker.shsudo sh install-docker.sh脚本中指令: –version 安装指定版本 Use the --version option to install a specific version, f…

浅谈chrome引擎

Chrome引擎主要包括其浏览器内核Blink、JavaScript引擎V8以及其渲染、网络、安全等子系统。下面我将对这些关键部分进行简要说明分析 1. Blink浏览器内核 Blink是Google开发的浏览器排版引擎&#xff0c;自Chrome 28版本起替代了Webkit作为Chrome的渲染引擎。Blink基于Webkit…

模余数最大公倍数

模余数最大公倍数 模余数题&#xff1a; 模余数 如果a%mb,则(am*k)%b。对m求模&#xff0c;余数为b的整数bm*k 题&#xff1a; 若干人&#xff0c;3001人为一排&#xff0c;余1人 4001人为一排&#xff0c;余2人 4999人为一排&#xff0c;余3人 求人数的最小值。 #include &l…

【Unity学习笔记】A*寻路算法

文章目录 图寻路算法BFS广度优先算法DFS深度优先贪心算法 引入权重Dijkstra算法 A*算法C#实现步骤 Unity中的A*算法A*优化建议 图 图的知识盘点 pathfinding 作为一名计算机专业的学生&#xff0c;对于图这种数据结构也是烂熟于心了。图是一种包含了多个结点的数据结构&…

案例分享:数据集市搭建方案中集成SQLFlow数据血缘分析工具

本文中描述的数据集市搭建方案是一家跨国公司在AWS平台上的具体实践案例。我公司参与其中的数据血缘部分的建设&#xff0c;SQLFlow数据血缘分析工具在该方案中帮助用户实现了数据血缘分析。 用户使用Redshift 数据库仓库进行数据集市开发。从各种数据源提取数据&#xff0c;并…

动态代理(通俗易懂)

程序为什么需要代理&#xff1f;代理长什么样&#xff1f; 例子 梳理 代理对象(接口)&#xff1a;要包含被代理的对象的方法 ---Star 被代理对象&#xff1a;要实现代理对象(接口) ---SuperStar 代理工具类&#xff1a;创建一个代理&#xff0c;返回值用代理对象&#xff0c…

罗克韦尔 AB 1756-OA16控制器 模块 处理器

罗克韦尔 AB 1756-OA16该模块是任何自动化系统的重要组成部分&#xff0c;提供对各种过程的精确控制。它被设计为易于安装和使用&#xff0c;具有用户友好的界面&#xff0c;允许简单的配置和监控。 罗克韦尔 AB 1756-OA16控制器是一款为工业应用而设计的先进控制系统。它具有…

React快速入门-跟着AI学习react

React的快速入门可以遵循以下步骤进行&#xff0c;我将结合参考文章中的相关信息&#xff0c;以分点表示和归纳的形式给出详细步骤&#xff1a; 一、React基础知识了解 React的概念和特点&#xff1a; React是一个专注于构建用户界面的JavaScript库&#xff0c;采用声明式设计…

python将多个文件夹里面的文件拷贝到一个文件夹中

网上可以搜到很多方式&#xff0c;有的好使&#xff0c;有的不好使&#xff0c;亲测如下脚本可用&#xff0c;并可达到我想要的效果&#xff0c;只将多个文件夹里的文件拷贝到一个文件夹中&#xff0c;不拷贝文件夹本身&#xff0c;如果需要文件夹也拷贝打开注释行即可 import…

初次使用GitHub教程入门

注册一个github账户 访问地址&#xff1a;https://github.com/&#xff0c;点击右上角sign up&#xff0c;录入以下信息&#xff0c;邮箱&#xff0c;密码&#xff0c;账号&#xff0c;会有邮箱验证&#xff0c;跟着步骤来就好了 配置 本机上设置你的github的邮箱和用户名 …

51-5 权限维持2 - 影子账号(隐藏用户)

权限维持技术 权限维持技术(Persistence,也称为权限持久化)是一种能够在系统重启、用户更改密码或其他可能导致访问中断的情况下保持对系统访问的技术。例如,它包括创建系统服务、利用计划任务、修改系统启动项或注册表、以及映像劫持等方法。 创建影子账户 影子账户是指隐…

【管理咨询宝藏139】某大型快消集团公司多渠道销售管理体系方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏139】某大型快消集团公司多渠道销售管理体系方案 【格式】PDF版本 【关键词】罗兰贝格、营销咨询、战略规划 【核心观点】 - 销售体系建设主要需…

谷粒商城学习-06-使用vagrant快速创建linux虚拟机

这一节的内容是在Windows上安装虚拟机。 为什么要按照虚拟机呢&#xff1f; 原因是很多软件只能在Linux下运行&#xff0c;有的虽然也可以在Windows上运行&#xff0c;但从安装到运行会遇到很多问题&#xff0c;为这些解决这些问题花时间对于大多数人特别是初学者是没有什么价…