首页 > 其他分享 >堆(优先队列)

堆(优先队列)

时间:2023-08-27 23:33:38浏览次数:49  
标签:优先 队列 hp down int swap ph size

又名优先队列

堆由完全二叉树构成,其每个节点都有一个键值,且每个节点的键值都大于等于/小于等于其父亲的键值

每个节点的键值都大于等于其父亲键值的堆叫做小根堆,否则叫做大根堆。STL 中的 priority_queue 其实就是一个大根堆

我们模拟的是小根堆,下标从1开始

1是根节点,令\(x\)的左儿子为\(2x\),\(x\)的右儿子为\(2x+1\)。则一个点u的父节点为n / 2

当修改或插入一个节点后,值可能不适合当前位置,则需要上移up(x)或下移down(x)

操作:

  1. 插入一个数(插入最后面)

heap[++size] = x; up(size)

  1. 求集合中的最小值

heap[1]

  1. 删除最小值

​ 用整个堆的最后的元素覆盖根元素,再down()

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

  1. 删除任意元素(STL无法直接实现)

heap[k] = heap[size]; size--; down(k); up(k);

  1. 修改任意元素(STL无法直接实现)

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

down()模板

void down(int u) { // 核心思就是判断u是否小于左右儿子
	int t = u;
	if (u * 2 <= sizen && h[u * 2] < h[t]) t = u * 2;
	if (u * 2 + 1 <= sizen && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (t != u) {
		swap(h[t], h[u]);
		down(t);
	}
}

up()模板

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

构造堆数组模板\(O(n)\)

for (int i = 1; i <= n; i++) cin >> h[i];
sizen = n;

for (int i = n / 2; i; i--) down(i);

修改或删除第k个插入的元素则需要创建两个数组记录元素映射关系

int hp[N]; //存放堆中点的插入次序
int ph[N]; //存放第k个插入点的下标
void heap_swap(int a, int b) {
	swap(ph[hp[a]], ph[hp[b]]);
	swap(hp[a], hp[b]);
	swap(h[a], h[b]);
}

例题:839. 模拟堆 - AcWing题库

#include<algorithm>
using namespace std;

const int N=1e5+10;
int h[N];   //堆
int ph[N];  //存放第k个插入点的下标
int hp[N];  //存放堆中点的插入次序
int cur_size;   //size 记录的是堆当前的数据多少

//这个交换过程其实有那么些绕 但关键是理解 如果hp[u]=k 则ph[k]=u 的映射关系
//之所以要进行这样的操作是因为 经过一系列操作 堆中的元素并不会保持原有的插入顺序
//从而我们需要对应到原先第K个堆中元素
//如果理解这个原理 那么就能明白其实三步交换的顺序是可以互换 
//h,hp,ph之间两两存在映射关系 所以交换顺序的不同对结果并不会产生影响
void heap_swap(int u,int v)
{   
    swap(h[u],h[v]); 
     swap(hp[u],hp[v]);     
     swap(ph[hp[u]],ph[hp[v]]);            

}

void down(int u)
{
    int t=u;
    if(u*2<=cur_size&&h[t]>h[u*2]) t=u*2;
    if(u*2+1<=cur_size&&h[t]>h[u*2+1])  t=u*2+1;
    if(u!=t)
    {
        heap_swap(u,t);
        down(t);
    }
}
void up(int u)
{
    if(u/2>0&&h[u]<h[u/2]) 
    {
        heap_swap(u,u/2);
        up(u>>1);
    }
}

int main()
{
    int n;
    cin>>n;
    int m=0;      //m用来记录插入的数的个数
                //注意m的意义与cur_size是不同的 cur_size是记录堆中当前数据的多少
                //对应上文 m即是hp中应该存的值
    while(n--)
    {
        string op;
        int k,x;
        cin>>op;
        if(op=="I")
        {
            cin>>x;
            m++;
            h[++cur_size]=x;
            ph[m]=cur_size;
            hp[cur_size]=m;
            //down(size);
            up(cur_size);
        }
        else if(op=="PM")    cout<<h[1]<<endl;
        else if(op=="DM")
        {
            heap_swap(1,cur_size);
            cur_size--;
            down(1);
        }
        else if(op=="D")
        {
            cin>>k;
            int u=ph[k];                //这里一定要用u=ph[k]保存第k个插入点的下标
            heap_swap(u,cur_size);          //因为在此处heap_swap操作后ph[k]的值已经发生 
            cur_size--;                    //如果在up,down操作中仍然使用ph[k]作为参数就会发生错误
            up(u);
           down(u);
        }
        else if(op=="C")
        {
            cin>>k>>x;
            h[ph[k]]=x;                 //此处由于未涉及heap_swap操作且下面的up、down操作只会发生一个所以
            down(ph[k]);                //所以可直接传入ph[k]作为参数
            up(ph[k]);
        }

    }
    return 0;
}

标签:优先,队列,hp,down,int,swap,ph,size
From: https://www.cnblogs.com/-37-/p/17661114.html

相关文章

  • 面对新领域,做真题应优先于学习书本知识,二者应同时进行
    面对全新的领域,做真题和概览考试用书同时进行或前者优于后者开始,一方面可以熟悉考点,通过错题了解知识点往往能留下更深刻的印象;另一方面,通过题目和答案提炼考点和知识关键字,熟悉考题呈现方式,对于看书抓住可能的出题点有一定帮助(这对于选择题型较为适用)。简单来讲,当你熟悉如何......
  • 二叉树层序遍历队列实现
    参考:二叉树的层序遍历(两种方法实现)_askunix_hjh的博客-CSDN博客题解|#求二叉树的层序遍历#_牛客博客(nowcoder.net)题解二:BFS(迭代)主要思路:广度优先8.27用到的思路是广度优先,循环,不是递归......
  • hdu:Rescue(bfs+优先队列)
    ProblemDescriptionAngelwascaughtbytheMOLIGPY!HewasputinprisonbyMoligpy.TheprisonisdescribedasaN*M(N,M<=200)matrix.ThereareWALLs,ROADs,andGUARDsintheprison.Angel’sfriendswanttosaveAngel.Theirtaskis:approach......
  • [算法学习笔记][刷题笔记] 单调队列优化 dp
    前置知识·单调队列单调队列顾名思义,一般用于解决滑动RMQ问题。它的原理非常简单。我们维护一个双端队列,这个双端队列只维护可能成为区间最值的元素。最基础的单调队列,例如滑动窗口。直接依据题意维护即可。这里提供单调队列模板(STLdeque版)单调队列模板(STLdeque版)......
  • 13、从0到1实现SECS协议之优先级队列(SECS-I)
    13、从0到1实现SECS协议之优先级队列(SECS-I)逻辑和HSMS协议中的优先级队列一样,只不过存储的数据变了而已。1、并发安全的优先级队列packagequeueimport( "secs-gem/common" "secs-gem/secs/packets" "secs-gem/secsgem" "container/heap" "context" "sync......
  • leetcode 题库994——bfs典型解法(队列+递归实现)
     classSolution:deforangesRotting(self,grid:list[list[int]])->int:m,n=len(grid),len(grid[0])queue,good=[],[]defbfs(queue,good,m,n,grid):times=0directions=[(-1,0),(1,0),(0,1),(0,-1)]......
  • 循环队列的定义、入队、出队等操作 C++代码实现
    #include<iostream>usingnamespacestd;/*循环队列的类型定义*/constintQueue_Size=100;typedefstructcirclQueue{char*elem;intrear;intfront;intqueueSize;}circlQueue;/*初始化*/voidinitQueue_C(circlQueue&......
  • 链队列的实现,C++代码实现
    /*链队列的实现*/#include<iostream>usingnamespacestd;/*链队列类型定义*/typedefstructQNode{structQNode*next;chardata;}QNode,*queuePtr;//结点的定义typedefstructlinkQueue{queuePtrrear;queuePtrfront;}linkQueue;//队列的定......
  • 无涯教程-进程 - 消息队列
    使用消息队列的通信可以通过以下方式进行:通过一个进程写入共享内存,并通过另一个进程从共享内存读取。我们知道,读取也可以通过多个进程完成。由具有不同数据包的一个进程写入共享内存,并由多个进程(即根据消息类型)从共享内存中读取。看完消息队列上的某些信息后,现在该检查支持消......
  • System.Messaging.MessageQueueException: 对消息队列系统的访问被拒绝
    无法启动服务。System.Messaging.MessageQueueException:对消息队列系统的访问被拒绝。使用Windows的消息队列时,窗体界面的应用可以对消息队列进行全部权限的操作,但是编写的Windows服务对消息队列进行操作时有可能会出现此错误提示,在这里提供一种解决方法:首先明确Windows服务程......