数据结构之堆——学习笔记

news2024/10/3 8:22:15

1.堆的简介:

 

接下来看一下堆的建立;

 

接下来是如何在堆中插入数据以及删除数据:

 

 

 

大根堆的插入操作类似只是改变了一下大于和小于符号,同时插入操作的时间复杂度为O(logn)。

 

 

 

 来看几个问题:

答案当然是不可以:

 

这样的话就能根据原堆的末尾数字的大小来判断是应该尝试将它往上还是下进行移动。

 来看看STL里面的优先队列:

 

 

 值得注意的是 用优先队列是没有clear操作的。

接下来看几道例题:

1.堆排序:

 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
int n,heap[N],x,len=0;
inline void up(int x){
	while(x>1 && heap[x]<heap[x/2]){
	swap(heap[x],heap[x/2]);
	x/=2;
	}
}
inline void down(int k){
	while(k+k<=len){
		int j=k+k;
		if(j+1<=len && heap[j+1]<heap[j]) ++j;
		if(heap[j]>=heap[k]) break;
		swap(heap[k],heap[j]);
		k=j;
	}
} 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x;
		heap[++len]=x;
		up(len);
	}
	for(int i=1;i<=n;i++){
		cout<<heap[1]<<' ';
		swap(heap[1],heap[len]);
		--len;
		down(1);
	}
	return 0;
}

事实上用优先队列来做会非常的简单:

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int>>q;
const int N=1e5+100;
int n;
/*inline void up(int x){
	while(x>1 && heap[x]<=heap[x/2]){
		swap(heap[x],heap[x/2]);
		x/=2;
	}
}
inline void down(int x){
	while(x+x<=len){
		int y=x+x;
		if(y+1<=len && heap[y+1]<heap[y])  ++y;
		if(heap[y]>=heap[x]) break;
		swap(heap[y],heap[x]);
		x=y;
	}
}*/
int main(){
    cin>>n;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		q.push(x);
	}	
	for(int i=1;i<=n;i++){
		cout<<q.top()<<' ';
		q.pop();
	}
	return 0;
}

使用小根堆的话是需要背一下优先队列的写法,有一点长。

接下来看一下第二题:

合并数列:

 这道题目的数据范围如果给的很小的话其实可以直接考虑模拟做法,但是实际上这道题目并没有那么的简单,接下来看代码,我在上面给了注释。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;//这里的N要比序列的个数上限设置的更大
int n,len,m;//n个序列,len代表当前读入的堆的序列的个数,m是最后要输出的数字个数
struct xulie{
	int v,delta;//v代表某一序列当前的堆顶元素,delta代表对应序列的k值
}heap[N];
//对新读入的序列试着将他往上面排的函数
inline void up(int x){
	while(x>1 && heap[x].v<=heap[x/2].v){
		swap(heap[x],heap[x/2]);
		x/=2;
	}
}
//将堆首的元素试着往下排
inline void down(int x){
	while(x+x<=len){
		int y=x+x;
		if(y+1<=len && heap[y+1].v<heap[y].v)  ++y;
		if(heap[y].v>=heap[x].v) break;
		swap(heap[y],heap[x]);
		x=y;
	}
}
int main(){
   cin>>n;
   for(int i=1;i<=n;i++){
       int k,b;
       cin>>k>>b;
       heap[++len].v=b;
       heap[len].delta=k;
       up(len);
   }
   cin>>m;
   for(int i=1;i<=m;i++){
   	cout<<heap[1].v<<' ';
   	heap[1].v+=heap[1].delta;
   	//输出堆顶序列这时候的值之后,对该序列的v值加一个delta并将加了之后的堆顶序列试着往下排
   	down(1);
   }
   return 0;
}

这道题的做法还是很有意思的。

接下来看一下第三个题:

大富翁游戏:

 费了九牛二虎之力,可算是搞明白了。。。

代码如下:

#include<bits/stdc++.h>
using namespace std;
struct Info{
	int v,pos;
}heap1[100001],heap2[100001];
int n,m,len1,len2,s1[100001],s2[100001];
inline void up1(int k){
	while(k>1 && heap1[k].v<heap1[k/2].v){
		swap(heap1[k],heap1[k/2]);
		s1[heap1[k].pos]=k;
		s1[heap1[k/2].pos]=k/2;
		k/=2;
	}
}
inline void down1(int k){
	while(k+k<=len1){
		int j=k+k;
		if(j+1<=len1 && heap1[j+1].v<heap1[j].v)
		++j;
		if(heap1[k].v<=heap1[j].v) break;
		swap(heap1[k],heap1[j]);
		s1[heap1[k].pos]=k;
		s1[heap1[j].pos]=j;
		k=j;
	}
}
inline void up2(int k){
	while(k>1 && heap2[k].v>heap2[k/2].v){
		swap(heap2[k],heap2[k/2]);
		s2[heap2[k].pos]=k;
		s2[heap2[k/2].pos]=k/2;
		k/=2;
	}
}
inline void down2(int k){
	while(k+k<=len2){
		int j=k+k;
		if(j+1<=len2 && heap2[j+1].v>heap2[j].v)
		++j;
		if(heap2[k].v>=heap2[j].v) break;
		swap(heap2[k],heap2[j]);
		s2[heap2[k].pos]=k;
		s2[heap2[j].pos]=j;
		k=j;
	}
}
int main(){
	cin>>n;
	len1=len2=0;
	for(int i=1;i<=n;i++){
		heap1[++len1].v=100;
		heap1[len1].pos=i;
		s1[i]=len1;
		up1(len1);
		heap2[++len2].v=100;
		heap2[len2].pos=i;
		s2[i]=len2;
		up2(len2);
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		int x;
		cin>>x;
		if(x==1){
			int y,z;
			cin>>y>>z;
			heap1[s1[y]].v+=z;
			up1(s1[y]);
			down1(s1[y]);
			heap2[s2[y]].v+=z;
			up2(s2[y]);
			down2(s2[y]);
		}
		else{
			cout<<heap2[1].v<<' '<<heap1[1].v<<endl;
		}
	}
	return 0;
}

这里代码中使用了两个堆,heap1heap2,分别用来维护最小堆和最大堆。s1s2 数组用来记录每个人在堆中的位置。代码中的堆结构的使用主要是为了在O(log n)的时间复杂度内找到当前最高和最低资金,以提高效率。在每次资金变动后,通过调整堆,保证堆顶元素分别是当前最小和最大资金。

最后一个题:动态中位数

这里是代码:

#include<bits/stdc++.h>
using namespace std;
int n;
priority_queue<int>a,b;
int main(){
	cin>>n;
	int x;
	cin>>x;
	a.push(x);
	cout<<a.top()<<' ';
	for(int i=1;i<=n/2;i++){
		int x,y,z=a.top();
		cin>>x>>y;
		if(x<z)
		a.push(x);
		else 
		b.push(-x);
		if(y<z)
		a.push(y);
		else 
		b.push(-y);
		if(a.size()-b.size()==3){
			b.push(-a.top());
			a.pop();
		} else 
		if(b.size()-a.size()==1){
			a.push(-b.top());
			b.pop();
		}
		cout<<a.top()<<' ';
	}
	return 0;
}

 

  1. priority_queue 的使用:定义了两个优先队列(堆),ab。它们用于存储输入数字,其中 a 是最大堆,b 是最小堆。

  2. 第一个数字的处理:从输入中读取第一个数字 x,将其压入最大堆 a 中,然后输出当前中位数(即最大堆的堆顶元素)。

  3. 循环处理每个数字对(x,y):

    • 如果 x 小于当前中位数(即最大堆的堆顶元素 a.top()),则将 x 压入最大堆 a
    • 否则,将 -xx 的相反数)压入最小堆 b
    • 如果 y 小于当前中位数,则将 y 压入最大堆 a
    • 否则,将 -yy 的相反数)压入最小堆 b
  4. 调整堆大小:在每次插入后,检查两个堆的大小关系:

    • 如果最大堆 a 的大小比最小堆 b 大 3,将最大堆的堆顶元素弹出并压入最小堆 b
    • 如果最小堆 b 的大小比最大堆 a 大 1,将最小堆的堆顶元素弹出并取其相反数压入最大堆 a
  5. 输出中位数:每次插入后,输出当前中位数(即最大堆的堆顶元素 a.top())。

  6. 这段代码通过维护最大堆和最小堆,根据输入的数字实时调整堆,以便高效地计算中位数。在中位数的计算过程中,通过在两个堆之间调整元素,确保了两个堆的大小差距在一个合理的范围内。这样做的目的是为了避免在求中位数时需要对所有数据进行排序的开销,从而实现更加高效的中位数计算

希望这一篇学习笔记对读者有所帮助,让我们共同进步。

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

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

相关文章

关于曲率、曲率半径和曲率圆,看这几篇文章就够啦

关于曲率、曲率半径和曲率圆的内容&#xff0c;是考研数学数学一和数学二大纲中明确要求掌握的内容&#xff0c;但这部分内容在很多教材教辅以及练习题中较少涉及。在本文中&#xff0c;荒原之梦考研数学网就为大家整理了曲率、曲率半径和曲率圆方程相关的概念、基础知识以及练…

从千问Agent看AI Agent——我们很强,但还有很长的路要走

前言 最近双十一做活动买了台新电脑&#xff0c;显卡好起来了自然也开始大模型的学习工作了&#xff0c;这篇文章可能是该系列的第一弹&#xff0c;本地私有化部署千问agent&#xff0c;后面还会尝试一些其他的大模型结合本地知识库或者做行业垂直模型训练的&#xff0c;一步…

【编译原理】期末预习PPT后三章笔记+LL(1) II

继续预习O.o 从这一章开始看自己班发的 PPT 了 LL(1)的部分因为班里发了所以又看了一遍hhh感觉比之前那个清楚一点 目录 I. 自顶向下 一、概念&#xff08;看一眼&#xff09; 1、语法分析的两大类分析方法 2、算法基本思想 3、自顶向下介绍 1&#xff09;一般过程 2&a…

IPv6和IPv4在技术层面的区别

随着互联网的不断发展&#xff0c;IPv4地址资源已经逐渐枯竭&#xff0c;而IPv6地址的使用逐渐成为趋势。IPv6和IPv4作为互联网协议的两个版本&#xff0c;在技术层面存在许多区别。本文将从地址空间、地址表示方法、路由协议、安全性、移动性以及网络性能等方面对IPv6和IPv4进…

【计算机网络】TCP原理 | 可靠性机制分析(一)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】【Java系列】 本专栏旨在分享学习网络编程、计算机网络的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目…

探索生成式AI:自动化、问题解决与创新力

目录 自动化和效率&#xff1a;生成式AI的颠覆力量 解谜大师生成式AI&#xff1a;如何理解和解决问题 创新与创造力的启迪&#xff1a;生成式AI的无限潜能 自动化和效率&#xff1a;生成式AI的颠覆力量 1. 神奇的代码生成器&#xff1a;生成式AI可以帮助开发人员像魔术一样快…

Linux network — 网络层收发包流程及 Netfilter 框架浅析

Linux network — 网络层收发包流程及 Netfilter 框架浅析 1. 前言2. 基础网络知识2.1 网络分层模型2.2 数据包协议分层2.3 sk_buff 结构2.4 收发包整体框架 3. 网络层&#xff08;IPv4&#xff09;收发包流程4. Netfilter 框架4.1 IPv4 网络层的 Netfilter Hook 点4.2 iptable…

jquery图形验证码

效果展示 js图形随机验证码&#xff08;表单验证&#xff09; html代码片段 <form class"formwrap"><div class"item"><input type"text" id"code_input" value"" placeholder"请输入验证码"/>…

【KD】知识蒸馏(knowledge distillation)简单介绍

最近学到了知识蒸馏的相关知识&#xff0c;来简单总结一下૮꒰ ˶• ༝ •˶꒱ა。 知识蒸馏 知识蒸馏&#xff0c;是一种模型压缩的手段。通过训练学生模仿教师的行为&#xff0c;将嵌入在大的教师模型中的知识迁移到小的学生模型。 例如&#xff0c;TinyBERT(Jiao et al.,2…

第14课 利用openCV快速数豆豆

除了检测运动&#xff0c;openCV还能做许多有趣且实用的事情。其实openCV和FFmpeg一样都是宝藏开源项目&#xff0c;貌似简单的几行代码功能实现背后其实是复杂的算法在支撑。有志于深入学习的同学可以在入门后进一步研究算法的实现&#xff0c;一定会受益匪浅。 这节课&#…

sensor 点亮出图后,画面全黑是为什么?

同事在点一个思特威的 sensor sc035hgs&#xff0c;这个 sensor 主要负责数据采集&#xff0c;然后给到后面的 NN&#xff08;神经网络&#xff09;去做处理。 点亮出图后&#xff0c;画面很黑&#xff0c;如下图所示&#xff1a; 因为没拿到板子&#xff0c;只能盲猜&#xf…

案例097:基于微信小程序+PHP的家具购物商城系统

目录 前言 系统展示 管理员模块的实现 用户管理 家具分类管理 家具新品管理 家具广告管理 小程序用户模块的实现 首页 家具信息 我的 代码实现 登录功能实现代码 注册功能实现代码 密码重置功能实现代码 修改信息功能实现代码 删除信息功能实现代码 保存信息…

H266/VVC多样化视频编码工具概述

全景视频编码 全景视频&#xff1a; 具有360度全包围视角的球面视频。 全景视频编码&#xff1a; 包括H266在内的视频编码算法都是以平面视频为对象的&#xff0c;为了采用传统的视频编码编码算法&#xff0c;全景视频需要转换为平面视频&#xff0c;其中经纬图等角映射&#…

深度学习|4.1 深L层神经网络 4.2 深层网络的正向传播

4.1 深L层神经网络 对于某些问题来说&#xff0c;深层神经网络相对于浅层神经网络解决该问题的效果会较好。所以问题就变成了神经网络层数的设置。 其中 n [ i ] n^{[i]} n[i]表示第i层神经节点的个数&#xff0c; w [ l ] w^{[l]} w[l]代表计算第l层所采用的权重系数&#xff…

记一次实战云渗透总结

点击星标&#xff0c;即时接收最新推文 云渗透思路 所谓的云渗透通常指SaaS或PaaS渗透&#xff0c;即将服务器端的某些服务搭建在云服务器上&#xff0c;源代码的开发、升级、维护等工作都由提供方进行。从原理上看&#xff0c;云渗透思路与传统渗透思路相差无几。站点必须由底…

Python中的cls语法

在Python中&#xff0c;cls 是一个用于指代类本身的约定性名称&#xff0c;通常用作类方法&#xff08;class method&#xff09;中的第一个参数。cls 类似于 self&#xff0c;它是对类的引用&#xff0c;而不是对实例的引用。cls 通常在类方法中用于访问类级别的属性和方法。举…

Kafka消息阻塞:拯救面试的八大终极解决方案!

大家好&#xff0c;我是小米&#xff0c;一个对技术充满热情的90后程序员。最近在准备社招面试的过程中&#xff0c;遇到了一个超级有挑战性的问题&#xff1a;“Kafka消息阻塞怎么解决&#xff1f;”今天&#xff0c;我就来和大家一起深入剖析这个问题&#xff0c;分享我在解决…

1-02VS的安装与测试

一、概述 对于一名C语言程序员而言&#xff0c;进行C语言程序的开发一般需要一个文本编辑器加上一个编译器就足够了。但为了方便起见&#xff0c;我们选择使用集成开发环境——Visual Studio&#xff08;简称VS&#xff09;。安装Visual Studio 下面讲一下如何安装VS&#xff0…

找不到mfc110u.dll,是什么原因,五种找不到mfc110u.dll,的解决方法

在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“mfc110u.dll丢失”。那么&#xff0c;什么是mfc110u.dll文件&#xff1f;为什么会出现丢失的情况&#xff1f;本文将为您详细介绍mfc110u.dll文件的作用、丢失原因以及提供5种解决方法…

概率论基础知识补充

概率论基础知识 样本概率&#xff1a;P(x)表示样本x出现的概率&#xff0c;也就是在全体样本中出现的概率先验概率&#xff1a;对于多类问题&#xff0c;类别状态 ω i \omega_i ωi​出现的概率, P ( ω i ) P{\left(\omega_i\right)} P(ωi​)条件概率&#xff1a;在类别 ω…