堆(堆排序 模拟堆)

news2024/11/27 2:38:56

目录

  • 一、堆的数据结构
  • 二、堆的操作方法
    • 往下调整的示意图
    • 往上调整的示意图
    • 相关功能的实现思路
      • 1.插入一个数
      • 2.求最小值
      • 3.删除最小值
      • 4.删除任意一个元素
      • 5.修改任意一个元素
  • 三、堆的实战运用
    • 堆排序
    • 模拟堆


一、堆的数据结构

堆是一个完全二叉树:除了最后一层结点以外,上面的每一层都是满的。最后一层的结点是从左到右排布的。
堆的数据结构

小根堆:每一个点都是小于左右子节点的,所以根节点就是树中最小值或者叫小顶堆。(递归定义)

存储方式:全新的存储方式,用一维数组来存。因为是完全二叉树,所有数据的下标是有规则可以找到的。

  • 父节点x/2
  • 节点x
  • 左子节点2x
  • 右子节点2x+1

注意下标从1开始的(符合节点之间的数学规律)


二、堆的操作方法

往下调整的示意图

down(x) 往下调整
往下调整

往上调整的示意图

up(x) 往上调整
往上调整

相关功能的实现思路

1.插入一个数

heap[++size] = x;
up(sz); // 往上调整

2.求最小值

heap[1];

3.删除最小值

heap[1] = heap[size--];
down(1);

4.删除任意一个元素

heap[k] = heap[sz--];
// 只执行其中之一 :大了向下走  小了向上走
down(k);
up(k);

5.修改任意一个元素

heap[k] = x;
down(k);
up(k);

三、堆的实战运用

堆排序

题目描述:
输入一个长度为 n n n 的整数数列,从小到大输出前 m m m 小的数。

输入格式:
第一行包含整数 n n n m m m。第二行包含 n n n 个整数,表示整数数列。

输出格式:
共一行,包含 m m m 个整数,表示整数数列中前 m m m 小的数。

数据范围:
1 ≤ m ≤ n ≤ 1 0 5 1≤m≤n≤10^5 1mn105

1 ≤ 1≤ 1 数列中元素 ≤ 1 0 9 ≤10^9 109

输入样例:

5 3
4 5 1 3 2

输出样例:

1 2 3

tip:
tip
当建树时应从 n / 2 n/2 n/2的位置开始向上进行down(x)操作,因为根节点无需往下调整,同时其时间复杂度为 O ( n ) O(n) O(n)

由于根节点为堆的最小值,所以最后询问操作时,打印一次堆最小值,同时删除。

除此之外使用 for (int i = 1; i <= n; i++) up(i);时间复杂度为 o ( n l o g n ) o(nlogn) o(nlogn)的这种方法也是可以的。

由于堆为完全二叉树,其高度为 l o g 2 n log_2n log2n层,递归版的down(x)的时间复杂度为 o ( l o g n ) o(logn) o(logn),所以没必要将down(x)改为循环。


代码实现:

const int N = 1e5 + 10;
int h[N], cnt;
void down(int x)
{
	int t = x;
	// 注意此处不能使用else if
	// 每次左右子节点要分别进行一次条件判定
	if (x * 2 <= cnt && h[x * 2] < h[t]) t = x * 2;
	if (x * 2 + 1 <= cnt && h[x * 2 + 1] < h[t]) t = x * 2 + 1;
	if (x != t)
	{
		swap(h[x], h[t]);
		down(t);
	}
}
int main()
{
	cin.tie(0);
	int n, m;
	cin >> n >> m;

	for (int i = 1; i <= n; ++i) cin >> h[i];
	cnt = n;
	for (int i = n / 2; i > 0; --i) down(i); //建树,从倒数第二层开始建,然后向上

	while (m--)
	{
		cout << h[1] << ' ';
		h[1] = h[cnt--];
		down(1);
	}
	return 0;
}

tip:
up(x)的基础操作:

①循环

void up(int x)
{
	while (x / 2 && h[x / 2] > h[x])
	{
		swap(h[x / 2], h[x]);
		x /= 2;
	}
}

②递归

void up(int x)
{
    if(x / 2 && h[x / 2] > h[u])
    {
        swap(h[x / 2], h[x]);
        up(x / 2);
    }
}

down(x)循环版本:

void down(int i) {
    while ((i << 1) <= sz) {
        int t = i << 1;
        if (t + 1 <= sz && h[t] > h[t + 1]) t ++;
        if (h[t] >= h[i]) break;
        swap(h[t], h[i]);
        i = t;
    }
}

模拟堆

题目描述:

维护一个集合,初始时集合为空,支持如下几种操作:

  1. I x,插入一个数 x x x
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 k k k 个插入的数;
  5. C k x,修改第 k k k 个插入的数,将其变为 x x x

现在要进行 n n n 次操作,对于所有第 2 2 2 个操作,输出当前集合的最小值。

输入格式:
第一行包含整数 n n n

接下来 n n n 行,每行包含一个操作指令,操作指令为 I xPMDMD kC k x 中的一种。

输出格式:
对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。

每个结果占一行。

数据范围:
1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105

− 1 0 9 ≤ x ≤ 1 0 9 −10^9≤x≤10^9 109x109

数据保证合法。

输入样例:

8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM

输出样例:

-10
6

难点:
要找到第 k k k 个插入的数,因为插入后会进行上下调整,要额外开辟数组,来储存第 k k k 个插入的数在堆中的位置。


代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

const int N = 1e5 + 10;

// h代表heap(堆),ph(point->heap)可以获得第几个插入的元素现在在堆的那个位置
// hp(heap->point)可以获得在堆的第n个元素存的是第几个插入的元素
int h[N], ph[N], hp[N], cnt;

void heap_swap(int a, int b)
{
	// 根据a和b的位置找到它们分别是第几个插入的元素,然后将其(在h数组中的)下标转换
	swap(ph[hp[a]], ph[hp[b]]);
	// 将两个位置存的是第几号元素转换
	swap(hp[a], hp[b]);
	// 最后再转换值(这三个语句位置可以换,但是从上到下逐渐变短的话比较美观)
	swap(h[a], h[b]);
}
void down(int x)
{
	int t = x;
	if (2 * x <= cnt && h[2 * x] < h[t]) t = 2 * x;
	if (2 * x + 1 <= cnt && h[2 * x + 1] < h[t]) t = 2 * x + 1;
	if (t != x)
	{
		heap_swap(x, t);
		down(t);
	}
}
void up(int x)
{
	while (x / 2 && h[x] < h[x / 2])
	{
		heap_swap(x, x / 2);
		x >>= 1;
	}
}

int main()
{
	cin.tie(0);
	int n, m = 0;
	cin >> n;
	while (n--)
	{
		string s;
		int k, x;
		cin >> s;
		if (s == "I")
		{
			cin >> x;
			cnt++;
			m++;
			ph[m] = cnt, hp[cnt] = m;
			h[cnt] = x;
			up(cnt);
		}
		else if (s == "PM") cout << h[1] << endl;
		else if (s == "DM")
		{
			heap_swap(1, cnt);
			cnt--;
			down(1);
		}
		else if (s == "D")
		{
			cin >> k;
			k = ph[k];
			heap_swap(k, cnt);
			cnt--;
			up(k);
			down(k);
		}
		else if (s == "C")
		{
			cin >> k >> x;
			k = ph[k];
			h[k] = x;
			up(k);
			down(k);
		}
		else cout << "error" << endl;
	}
	return 0;
}

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

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

相关文章

C语言三子棋,五子棋,n子棋的代码实现

C语言三子棋&#xff0c;五子棋&#xff0c;n子棋的代码实现 这里以五子棋为例&#xff0c;来说明开发过程开发思路菜单打印棋盘的打印棋子的打印电脑下棋&#xff08;随机数&#xff09;判断输赢代码整合注意事项 这里以五子棋为例&#xff0c;来说明开发过程 其中该项目包含…

《用户增长方法论》从产品、渠道、营销创意等多个维度,搭建了一套完整的用户增长方法体系

关于作者 黄永鹏&#xff0c;目前在阿里巴巴担任高级用户增长专家。黄永鹏是一个典型的 “ 斜杠青年 ” &#xff0c;十年前从广告咨询行业转战互联网&#xff0c;在 BAT 三家 公司都待过&#xff0c;负责过多款用户和日活过亿的产品&#xff0c;比如腾讯手机管家、百度地图…

chatgpt赋能python:Python练手:提高你的SEO技能

Python练手&#xff1a;提高你的SEO技能 在当今数字化时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;成为了网站和企业在线成功的关键。优化技巧既可以提高网站的排名&#xff0c;还可以增加网站的可见性&#xff0c;从而吸引更多的流量和潜在客户。Python是一个适…

网络通信协议-ARP协议

目录 一、ARP协议 二、ARP协议通信过程 应用情景一&#xff1a;同一广播域内通信 &#xff08;1&#xff09;第一步&#xff1a;ARP协议通信 1.交换机接受消息 2.电脑2接收到广播消息 3.电脑2回复 4.交换机转发回复给电脑1 5.电脑1记录 &#xff08;2&#xff09;第二…

Go快速上手之基础语法 | 青训营笔记

Go快速上手之基础语法 &#xff5c; 青训营笔记 文章目录 Go快速上手之基础语法 &#xff5c; 青训营笔记系列介绍本文摘要1. Go 介绍2. Go 的环境配置2.1 :sparkles: IDE2.2 Gitpod 和 Jetbrians Gateway 的使用 3. Go的基础语法3.1 Hello World3.2 变量与常量3.3 条件控制语句…

Linux(进程间通信)

目录&#xff1a; 1.进程间通信的介绍 2.管道通信 3.管道的原理 ------------------------------------------------------------------------------------------------------------------------------- 1.进程间通信的介绍 2.管道通信 当我们在创建子进程时&#xff0c;我们的…

chatgpt赋能python:Python生成pyc文件的介绍

Python生成pyc文件的介绍 Python是一种解释型语言&#xff0c;但是在执行某些操作时&#xff0c;它会生成缓存文件&#xff0c;以便提高执行效率。这些缓存文件以 .pyc 扩展名保存在同一目录中。 在本文中&#xff0c;我们将重点介绍Python生成pyc文件&#xff0c;并探讨它们…

使用Python绘制M2货币供应率曲线

M2广义货币供应量&#xff1a;流通于银行体系之外的现金加上企业存款、居民储蓄存款以及其他存款&#xff0c;它包括了一切可能成为现实购买力的货币形式&#xff0c;通常反映的是社会总需求变化和未来通胀的压力状态。近年来&#xff0c;很多国家都把M2作为货币供应量的调控目…

Fedora安装并配置开启SSH服务相关命令

Ubuntu参考我这篇&#xff1a;虚拟机里安装ubuntu-23.04-beta-desktop-amd64&#xff0c;开启SSH(换源、备份)&#xff0c;配置中文以及中文输入法等 一、过程 1、检测是否安装了openssh-server $ rpm -qa | grep openssh-serveropenssh-server-7.9p1-5.fc30.x86_642、如果上…

【web框架】——Django——如桃花来

目录索引 web框架介绍&#xff1a;常见软件的架构&#xff1a;*CS架构&#xff1a;**BS架构&#xff1a;* 网络通信&#xff1a;socket知识复习&#xff1a;*服务端代码逻辑&#xff1a;**客户端代码逻辑&#xff1a;* socket代码演示&#xff1a;*服务端代码演示&#xff1a;*…

chatgpt赋能python:Python生成:深入了解Python编程中的生成

Python 生成&#xff1a;深入了解Python编程中的生成 简介 Python是一门多用途编程语言&#xff0c;广泛应用于 Web 开发&#xff0c;数据分析&#xff0c;人工智能和科学计算等领域。在Python编程中&#xff0c;生成是一个强大而又常用的概念。本文将介绍Python编程中的生成…

如何在华为OD机试中获得满分?Java实现【输入n个整数,输出其中最小的k个】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

Java 的 String、StringBuffer 和 StringBuilder(一文讲透)

提到 String、StringBuffer 和 StringBuilder&#xff0c;就不得不谈及它们的历史&#xff0c;在了解它们的历史之后&#xff0c;我们对它们的理解将更上一级台阶&#xff01; 发展历史 String 与 StringBuffer 的出现 String 和 StringBuffer 在 Java1.0 中就已经有了&…

chatgpt赋能python:Python自动操作软件:提高工作效率和节省时间的利器

Python自动操作软件&#xff1a;提高工作效率和节省时间的利器 Python是一种高级编程语言&#xff0c;具有易读易用、快速开发、可移植性好、跨平台兼容等优点。它在自动化操作方面具有很大的优势&#xff0c;可以帮助用户实现各种自动化操作&#xff0c;从而为我们的工作提供…

有哪些信息安全/网络安全/渗透测试/众测/CTF/红蓝攻防/漏洞测试等前沿技术/研究/技巧获取渠道?

​前言 护网的定义是以国家组织组织事业单位、国企单位、名企单位等开展攻防两方的网络安全演习。进攻方一个月内采取不限方式对防守方展开进攻&#xff0c;不管任何手段只要攻破防守方的网络并且留下标记即成功&#xff0c;直接冲到防守方的办公大楼&#xff0c;然后物理攻破…

第2章 Class

Point结构体 //C语言写法 typedef struct point{float x;float y; }Point;Point a; a.x 1; a.y 2; //const表示p指向的对象里的值不能由p指针修改 void print(const Point* p){printf("%d %d\n", p -> x, p -> y); } print(&a);//想实现点的移动&#x…

深入解析OSI七层协议:实现网络通信的基石

目录 引言&#xff1a;详细介绍1. 物理层&#xff08;Physical Layer&#xff09;2. 数据链路层&#xff08;Data Link Layer&#xff09;3. 网络层&#xff08;Network Layer&#xff09;4. 传输层&#xff08;Transport Layer&#xff09;5. 会话层&#xff08;Session Layer…

【章节1】git commit规范 + husky + lint-staged实现commit的时候格式化代码

创建项目我们不多说&#xff0c;可以选择默认的&#xff0c;也可以用你们现有的项目。 前言&#xff1a; git commit 的时候总有人填写一堆花里胡哨乱写的内容&#xff0c;甚至看了commit 的描述都不知道他这次提交到底做了个啥&#xff0c;那我们有没有办法规范大家的commit提…

chatgpt赋能python:Python中的绝对值函数:abs()

Python中的绝对值函数&#xff1a;abs() 在Python中&#xff0c;绝对值函数可以用来计算一个数的绝对值。这个函数名为abs()&#xff0c;它的语法为&#xff1a; abs(x)其中x为需要计算绝对值的数字。 abs()的用法 abs()函数可以计算传入参数的绝对值&#xff0c;并返回一个…

JavaScript实现使用js外链的方式输出一个5行6列的长方形的代码

以下为实现使用js外链的方式输出一个5行6列的长方形的程序代码和运行截图 目录 前言 一、使用js外链的方式输出一个5行6列的长方形&#xff08;HTML部分&#xff09; 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 二、使用js外链的方式输出一个5行6列的长方形&…