蓝桥杯备考:堆和priority queue(优先级队列)

news2025/1/19 20:22:30

堆的定义

heap堆是一种特殊的完全二叉树,对于树中的每个结点,如果该结点的权值大于等于孩子结点的权值,就称它为大根堆,小于等于就叫小根堆,如果是大根堆,每个子树也是符合大根堆的特征的,如果是小根堆,每课子树符合小根堆特征

堆的存储

顺序存储,按照层序遍历编号依次存放在数组里面,变量n负责标记元素个数,存储固然简单,但是我们的 题目一般不会那么好心直接给一个标准的堆,一般只是随便给我们一组数,这组数还原出来并不一定是堆结构,我们可以用数组存下来这组数,然后把数组调整称堆,也可以依次把这些数插入到堆里面

向上调整算法 

向上调整,当我们插入一个数的时候,我们需要进行向上调整

算法原理(大根堆,小根堆同理):如果插入的数权值小于等于父亲结点,那我们不变,如果插入的数是大于父亲结点的,我们就要让它和父亲结点换位置

不断重复这个操作,直到此数变成根结点或者权值小于等于父亲结点的时候为止

比如我们尝试调整一下这棵树

void up(int child)
{
    int parent = child / 2;
	while (child != 1 && heap[parent] < heap[child])
	{
		swap(heap[parent], heap[child]);
		child = parent;
		parent = child / 2;
	}


}

向下调整算法

当我们需要删除一个结点的时候,我们只能删除堆顶的元素,这时候,我们交换堆底和堆顶元素,然后对交换上来的堆顶进行向下调整算法,如果新的堆顶的两个孩子都小于等于它,那么就不用操作,因为我的其他子树都维持着大根堆的特征,如果堆顶元素的两个孩子的较大结点大于我们的堆顶元素,此时我们就要让他和最大的孩子交换,重复此操作,直到换到叶子结点或者该结点大于等于左右孩子为止

void down(int parent)
{
	int child = parent * 2;
	if (heap[child + 1] > heap[child]) child = child + 1;
	while (child <= n && heap[parent] < heap[child])
	{
		swap(heap[parent], heap[child]);
		parent = child;
		child = parent * 2;
	}

}

由于我们的向上调整算法和向下调整算法最坏的情况都是执行二叉树的深度次,又因为二叉树的深度是log(n+1) 所以我们的向上调整算法和向下调整算法就是O(log N)

堆数据结构的创建

创建

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int heap[N], n;

插入

插入的时候我们就放在n下标的下一个位置,然后把新的n下标传进去进行向上调整即可

代码;

void push(int x)
{
	heap[++n] = x;
	up(n);
}

删除

删除的时候我们要先交换堆顶和堆底元素,然后删除堆底元素n--,

然后对堆顶元素进行向下调整

void pop()
{
	swap(heap[n], heap[1]);
	n--;
	down(1);
}

查询堆顶元素

int top()
{
	return heap[1];
}

时间复杂度是O(1)

查询有效元素个数

int size()
{
	return n;
}

进行测试,把这个数组插入到堆里面,键堆,依次取出堆顶元素,就是个降序的过程

总代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int heap[N], n;

void up(int child)
{
    int parent = child / 2;
	while (child != 1 && heap[parent] < heap[child])
	{
		swap(heap[parent], heap[child]);
		child = parent;
		parent = child / 2;
	}


}
void down(int parent)
{
	int child = parent * 2;
	if (heap[child + 1] > heap[child]) child = child + 1;
	while (child <= n && heap[parent] < heap[child])
	{
		swap(heap[parent], heap[child]);
		parent = child;
		child = parent * 2;
	}

}
void push(int x)
{
	heap[++n] = x;
	up(n);
}
void pop()
{
	swap(heap[n], heap[1]);
	n--;
	down(1);
}

int top()
{
	return heap[1];
}
int size()
{
	return n;
}
int main()
{
	int a[10] = { 1, 41, 23, 10, 11, 2, -1, 99, 14, 0 };
	for (int i = 0; i < 10; i++)
	{
		push(a[i]);
	}
	while (size())
	{
		cout << top() << " ";
		pop();
	}



	return 0;
}

priority queue优先级队列的用法(初阶,进阶)

在我们的stl中,有一个现成的堆的数据结构,叫做优先级队列,有了它,我们就不用再自己手写一个堆的数据结构了

堆可以存储内置类型比如说int

堆也可以存储字符串类型

堆也可以存储自定义类型比如结构体

每一种存储方式都对应一种写法

我们先来介绍普通的存储内置类型的优先级队列

#include <iostream>
#include <queue>
using namespace std;

int a[10] = { 1, 41, 23, 10, 11, 2, -1, 99, 14, 0 };

int main()
{
	priority_queue <int> heap;//这种简单的写法默认就是大根堆
	for (int i = 0; i < 10; i++)
	{
		heap.push(a[i]);
	}
	while (heap.size())
	{
		cout << heap.top() << " ";
		heap.pop();
	}


	return 0;
}

首先是这种简单的写法,这种简单的写法默认就是大根堆

如果你想实现正经八本的一个堆的话,我们有一个模式

priority_queue<数据类型,数据实现方式,比较方式(判断大/小根堆)>

	priority_queue<int, vector<int>, less<int>> q1;//大根堆
	priority_queue<int, vector<int>, greater<int>> q2;//小根堆

这个需要我们特殊的记一下,大根堆用less(当第一个数小于第二个数是返回true,相当于我们sort里的比较函数一样),小根堆用greater

测试结果

测试代码

#include <iostream>
#include <queue>
using namespace std;

int a[10] = { 1, 41, 23, 10, 11, 2, -1, 99, 14, 0 };
void test()
{
	priority_queue<int, vector<int>, less<int>> q1;//大根堆
	priority_queue<int, vector<int>, greater<int>> q2;//小根堆
	for (int i = 0; i < 10; i++)
	{
		q1.push(a[i]);
		q2.push(a[i]);
	}
	while (q1.size())
	{
		cout << q1.top() << " ";
		q1.pop();
	}
	cout << endl;
	while (q2.size())
	{
		cout << q2.top() << " ";
		q2.pop();
	}
	cout << endl;
}
int main()
{
	priority_queue <int> heap;//这种简单的写法默认就是大根堆
	/*for (int i = 0; i < 10; i++)
	{
		heap.push(a[i]);
	}
	while (heap.size())
	{
		cout << heap.top() << " ";
		heap.pop();
	}*/
	test();

	return 0;
}

如果我们要存double和long long 只要把int改一下就行了

接下来我们来说一下结构体类型怎么建堆

如果想实现结构体类型的建堆,我们只需要在结构体内部重载小于号,如果返回b<x.b的话,就是大根堆,如果返回b>x,b的话就是小根堆

struct node {
	int x, y, z;
	bool operator < (const node& e)const 
	{
		return y < e.y;//大根堆
	}
};
void test2()
{
	priority_queue <node> heap5;
	for (int i = 1; i <= 5; i++)
	{
		heap5.push({ i + 5,i + 1,i + 2 });

	}
	while (heap5.size())
	{
		node t = heap5.top(); heap5.pop();
		cout << t.x << " " << t.y << " " << t.z << endl;
	}
}

可以看到这就是大根堆

把重载的返回值改成大于就变成小根堆了,我们就不再放代码了,只要改一个符号就行

堆和priority优先级队列的算法题

1.模板题

这是一道模板题,我们先用自己手写的堆实现一下

#include <iostream>
using namespace std;

const int N = 1e6+10;
int heap[N],n;
void up(int child)
{
	int parent = child/2;
	while(parent >=1 && heap[parent]>heap[child])
	{
		swap(heap[parent],heap[child]);
		child = parent;
		parent = child/2;
	}
}
void down(int parent)
{
	int child = parent * 2;
	while(child <= n)
	{
		if(child + 1 <= n && heap[child+1] < heap[child])
          ++child;
        if(heap[parent] < heap[child]) return;
        swap(heap[parent],heap[child]);
        parent = child;
        child = parent*2;
				
	}
}
void push(int x)
{
	heap[++n] = x;
	up(n);
}
void pop()
{
	swap(heap[1],heap[n]);n--;
	down(1);
}
int main()
{
	int T;cin >> T;
	while(T--)
	{
		int op;
		cin >> op;
		if(op == 1)
		{
			int t;
			cin >> t;
			push(t); 
		}
		else if(op == 2)
		{
			cout << heap[1] << endl;
		}
		else
		{
			pop();
		}
	}
	
	
	
	
	return 0;
}

接着用我们stl的优先级队列来实现一下

#include <iostream>
#include <queue>
using namespace std;
priority_queue<int,vector<int>,greater<int>> heap1;
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
		int op;cin >> op;
		if(op == 1)
		{
			int x;
			cin >> x;
			heap1.push(x);
		}
		else if(op == 2)
		cout << heap1.top() << endl;
		else
		heap1.pop();
	}
	
	return 0;
}

2.第k小(topk问题)

我们要维护这个优先级队列,让优先级队列里只有k个元素,然后堆顶就是我们的第k小,比如1,2,3 3就是第三小,如果要找第k小的话,实际上就是k个元素的时候的最大的值,也就是k个元素的时候的堆顶,比如1 2 3 3就是第k小,如果你插入4 5 6 1 2 3 4 5 6 最小的还是3,所以我们依次删除堆顶,留下 1 2 3 3 当堆顶的时候就是第三小  再比如 1 2  5  插入了3  那 我们删除最大的堆顶 留下 1 2 3 3也就是第三小了

#include <iostream>
#include <queue>
using namespace std;
priority_queue <int> heap1;
const int N = 2e5+10;
int a[N];

int main()
{
    int n,m,k;
    cin >> n >> m >> k;
    for(int i = 0;i<n;i++)
    {
        cin >> a[i];
        heap1.push(a[i]);
    }
    while(heap1.size() > k)
    {
        int t = heap1.top();heap1.pop();
        
    }
    while(m--)
    {
      int op;
      cin >> op;
      if(op == 1)
      {
          int x;cin >> x;
          heap1.push(x);
          while(heap1.size() > k)
          {
              heap1.pop();
          }
      }
    if(op == 2)
    {
        if(heap1.size() < k)
            cout << -1 << endl;
        else
            cout << heap1.top() << endl;
    }
      
    }
    
    
    
}

3.除2!

如果遇到求和,我们一定要记得考虑用long long

#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
int n,k;
ll sum;
priority_queue <int> heap;
int main()
{
    cin >> n >> k;
    for(int i = 1;i<=n;i++)
    {
        int x;cin >> x;
        sum+=x;
        if(x%2 == 0)
        {
            heap.push(x);
        }
    }
    while(heap.size() && k--)
    {
        int t = heap.top()/2;
        heap.pop();
        sum-=t;
        if(t%2 == 0)
        {
            heap.push(t);
        }
    }
    cout << sum << endl;
    
    
    
    
    
    
    
    
    
    return 0;
}

4.最小函数值(结构体堆+单调性)

这道题我们要用构造一个结构体元素的堆,根据单调性,我们不需要一次性把所有的函数式前m个元素算出来,我们只需要先把代入值为1的值算出来,push成一个堆,然后把堆顶打印出来,把这个堆顶元素的代入值变为2再push进去,接下来再打印下一个堆顶即可

所以我们结构体要存下函数值(建堆的条件)还要存第几个函数表达式,并存下代入值x

#include <iostream>
#include <queue>
using namespace std;
const int N = 1e4+10;
int a[N],b[N],c[N];
int n,m;

struct node
{
	int f;
	int num;
	int x;
	bool operator <(const node& y) const
     {
     	return f > y.f;//前一个元素大于后一个元素 
	  }	
};
int calc(int i,int x)
{
	return a[i]*x*x + b[i]*x +c[i];
}
priority_queue <node> q;
int main()
{
	cin >> n >> m;
	for(int i = 1;i<=n;i++)
	{
		cin >> a[i] >> b[i] >> c[i];
	}
	//先把代入值为1的元素push进去,然后依次出堆顶
	//每次出堆顶都代入这个表达式代入的下一个值
	//这个循环一共循环m次 
	for(int i = 1;i<=n;i++)
	{
		q.push({calc(i,1),i,1});
	}
	while(q.size() && m--)
	{
		node t = q.top();
		q.pop();
		cout << t.f << " ";
		int x1 = t.x+1; int num1 = t.num;
		q.push({calc(num1,x1),num1,x1});
	}
	
	
	
	
	return 0;
}

5.序列合并(结构体堆+单调性)

这道题和上一道题非常的类似

我们这么想,先让第一个序列里最小的值和第二个序列分别相加,push进入小根堆,删除堆顶再push进第一个序列第二个小的值和第二个序列那个值相加的值,直到打印完前n个值

#include <iostream>
#include <queue>
using namespace std;
const int N = 1e5+10;
int a[N],b[N];
typedef long long ll;
struct node{
	ll sum;
	int i;
	int j;
	bool operator <(const node& n) const
	{
		return sum > n.sum;
	}
	
};
priority_queue <node> heap;
int main()
{
	int n; cin >> n;
	for(int i = 1;i<=n;i++)	cin >> a[i];
	for(int i = 1;i<=n;i++)	cin >> b[i];
    for(int i = 1;i<=n;i++)
    {
    	heap.push({a[i]+b[1],i,1});
	}
    for(int i = 1;i<=n;i++)
    {
    	node t = heap.top(); heap.pop();
    	ll sum1 = t.sum,i1 = t.i,j1=t.j;
    	cout << sum1 << " ";
    	heap.push({a[i1]+b[j1+1],i1,j1+1});
	}
	
	
	
	return 0;
 } 

6.舞蹈课(双链表存数据,结构体元素组成的堆)

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

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

相关文章

力扣682

from typing import Listclass Solution:def calPoints(self, operations: List[str]) -> int:a [] # 用于存储有效得分的列表for op in operations:if op.isdigit() or (op[0] - and op[1:].isdigit()): # 如果是整数&#xff08;包括负数&#xff09;a.append(int(op)…

考研计算机组成原理——零基础学习的笔记

第一章 研究计算机硬件的学科。 1.计算机系统概述 计算机系统硬件软件&#xff08;系统软件&#xff1a;比如操作系统、数据库管理系统、标准程序库等&#xff0c;应用软件&#xff1a;QQ等&#xff09; 1.2计算机的层次结构 1.2.1计算机硬件的基本组成 冯诺伊曼计算机&a…

海康工业相机的应用部署不是简简单单!?

作者&#xff1a;SkyXZ CSDN&#xff1a;SkyXZ&#xff5e;-CSDN博客 博客园&#xff1a;SkyXZ - 博客园 笔者使用的设备及环境&#xff1a;WSL2-Ubuntu22.04MV-CS016-10UC 不会吧&#xff1f;不会吧&#xff1f;不会还有人拿到海康工业相机还是一脸懵叭&#xff1f;不会还有人…

计算机网络 (49)网络安全问题概述

前言 计算机网络安全问题是一个复杂且多维的领域&#xff0c;它涉及到网络系统的硬件、软件以及数据的安全保护&#xff0c;确保这些元素不因偶然的或恶意的原因而遭到破坏、更改或泄露。 一、计算机网络安全的定义 计算机网络安全是指利用网络管理控制和技术措施&#xff0c;保…

STM32 FreeRTOS中断管理

目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…

操作系统 期末重点复习

操作系统 期末重点复习 必会 课后题摘要 第二章&#xff1a; 在操作系统中为什么要引入进程概念&#xff1f;它会产生什么样的影响? 为了使程序在多道程序环境下能并发执行&#xff0c;并对并发执行的程序加以控制和描述&#xff0c;在操作系统中引入了进程概念。影响: 使程…

7.5.4 MVCC优化测试

作者&#xff1a; h5n1 原文来源&#xff1a; https://tidb.net/blog/4e02d900 1. 背景 由于MVCC 版本数量过多导致rocksdb扫描key数量过多影响SQL执行时间是tidb经常出现问的问题&#xff0c;tidb也一直在致力于优化该问题。 一些优化方式包括比&#xff1a; (1) 从传统…

2024年AI与大数据技术趋势洞察:跨领域创新与社会变革

目录 引言 技术洞察 1. 大模型技术的创新与开源推动 2. AI Agent 智能体平台技术 3. 多模态技术的兴起:跨领域应用的新风口 4. 强化学习与推荐系统:智能化决策的底层驱动 5. 开源工具与平台的快速发展:赋能技术创新 6. 技术安全与伦理:AI技术的双刃剑 7. 跨领域技…

vulnhub靶场【Lampiao靶机】,主要考察提权,脏牛提权

前言 靶机&#xff1a;lampiao&#xff0c;IP地址为192.168.10.11 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.2 都采用虚拟机&#xff0c;网卡为桥接模式 该靶机目前只剩下一个了&#xff0c;之前记得是有两台构成系列的。 文章中涉及的靶机&#xff0c;来源于v…

ASP .NET Core 学习(.NET9)配置接口访问路由

新创建的 ASP .NET Core Web API项目中Controller进行请求时&#xff0c;是在地址:端口/Controller名称进行访问的&#xff0c;这个时候Controller的默认路由配置如下 访问接口时&#xff0c;是通过请求方法&#xff08;GET、Post、Put、Delete&#xff09;进行接口区分的&…

构建core模块

文章目录 1.环境搭建1.sunrays-common下新建core模块2.引入依赖&#xff0c;并设置打包常规配置 2.测试使用1.启动&#xff01;1.创建模块2.引入依赖3.application.yml 配置MySQL和Minio4.创建启动类5.启动测试 2.common-web-starter1.目录2.WebController.java3.结果 3.common…

VRTK4 记录抓取错误

左手原本可以正常抓取&#xff0c;但是当右手拿起一个物体时&#xff0c;左手抓取右手的线性驱动器&#xff0c;只有部分区域可以抓取 原因是左手的判定物体的层级错误 应该在Collections下&#xff0c;之前错误的和Collections同一层级&#xff0c;导致抓取有时可以有时不可以…

游戏画质升级史的思考

画质代入感大众玩家对游戏的第一印象与评判标准 大众玩家还没到靠游戏性等内在因素来评判游戏的程度。 画面的重要性&#xff0c;任何时候都不能轻视。 行业就是靠摩尔定律来推动进步的。 NS2机能达到PS4到PS4PRO之间的水准&#xff0c;5050达到8G显存&#xff0c;都会引发连…

Windows11电脑总是一闪一闪的,黑一下亮一些怎么解决

Windows11电脑总是一闪一闪的&#xff0c;黑一下亮一些怎么解决 1. 打开设备管理器2. 点击显示适配器3. 更新下方两个选项的驱动3.1 更新驱动Inter(R) UHD Graphixs3.2 更新驱动NVIDIA GeForce RTX 4060 Laptop GPU 4. 其他文章快来试试吧&#x1f970; 1. 打开设备管理器 在电…

【RAG落地利器】向量数据库Qdrant使用教程

TrustRAG项目地址&#x1f31f;&#xff1a;https://github.com/gomate-community/TrustRAG 可配置的模块化RAG框架 环境依赖 本教程基于docker安装Qdrant数据库&#xff0c;在此之前请先安装docker. Docker - The easiest way to use Qdrant is to run a pre-built Docker i…

【逆境中绽放:万字回顾2024我在挑战中突破自我】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 一、引言二、个人成长与盘点情感与心理成长学习与技能提升其它荣誉 三、年度创作历程回顾创作内容概…

HTTP / 2

序言 在之前的文章中我们介绍过了 HTTP/1.1 协议&#xff0c;现在再来认识一下迭代版本 2。了解比起 1.1 版本&#xff0c;后面的版本改进在哪里&#xff0c;特点在哪里&#xff1f;话不多说&#xff0c;开始吧⭐️&#xff01; 一、 HTTP / 1.1 存在的问题 很多时候新的版本的…

于灵动的变量变幻间:函数与计算逻辑的浪漫交织(下)

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节我们主要来学习单个函数的声明与定义&#xff0c;static和extern… 这里写目录标题 一、单个函数…

pthread_create函数

函数原型 pthread_create 是 POSIX 线程&#xff08;pthread&#xff09;库中的一个函数&#xff0c;用于在程序中创建一个新线程。 #include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *a…

VSCode 的部署

一、VSCode部署 (1)、简介 vsCode 全称 Visual Studio Code&#xff0c;是微软出的一款轻量级代码编辑器&#xff0c;免费、开源而且功能强大。它支持几乎所有主流的程序语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比Diff、版本管理GIT等特性&…