数组模拟小根堆(c++)(个人记录向)

本文介绍了小根堆的数据结构及其实现方法,并通过代码示例详细解释了如何进行元素的插入、查找最小值、删除最小值等操作,还特别讨论了如何处理特定题目要求的元素删除与修改。

小根堆就是我们数据结构里面常常提到的那个堆,一个堆是由一个完全二叉树所构成的。小根堆为什么叫小根堆呢,因为它的根节点永远是比其儿子节点小的。正是由于这个性质,所以我们能有 堆排序这种东西。

这里我们用一个数组来模拟我们的堆,如下图所示

 父节点的下标为n,其子节点分别为2n以及2n+1。堆的维护,需要两个操作一个是up一个是down,也就是调整数在堆中的位置,使其按照小根堆的父节点一定大于子节点的样子。

void up(int u)
{
    int t=u;
    if(u/2&&q[u/2]>q[u])
    {
        t=u/2;
        good_swap(u,t);//一个交换函数下面会讲到为什么不直接用swap
        up(t);//递归调用看看换完之后是否还能接着往上走
    }
    
}
void down(int x)
{
    int t =x;
    if(2*x<=cnt&&q[t]>q[2*x])t=2*x;
    if(2*x+1<=cnt&&q[t]>q[2*x+1])t=2*x+1;//cnt是堆里面数的个数,也就是其子节点必须得存在
    if(t!=x)
    {
        good_swap(t,x);
        down(t);
    }
}

首先是插入操作,用数组模拟堆我一般习惯从下标1开始存储,每次插入先把数插入到堆的最后,然后再给它up一遍即可,也就是q[++cnt]=x;up(cnt);

然后是输出最小值,也就是我们的堆顶,由于只是输出所以cout<<q[1]<<endl;

删除最小值就是,把堆里面存储在最后面的数,和堆顶的数互换,然后再down一下堆顶即可。

但是得记得要cnt--;不然的话就相当于没删除了,堆顶会在up的时候重新回到堆顶,因为后面插入的数不会覆盖掉最后原来的那个cnt而是被赋值给了cnt+1;

因为题目有个操作是要删除和修改第k个插入的数,那如果我们直接用swap函数,交换之后,去哪里找我们的第k个插入的数字呢。

这里 我们需要一组映射关系利用一个

#include<iostream>
#include<string.h>
using namespace std;

const int N=1e5+10;

int ph[N],hp[N],q[N],cnt,s;//q是堆,cnt是堆的长度,s是记录插入到了第几个数


void good_swap(int a,int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a],hp[b]);
    swap(q[a],q[b]);
}

void up(int u)
{
    int t=u;
    if(u/2&&q[u/2]>q[u])
    {
        t=u/2;
        good_swap(u,t);
        up(t);
    }
}
void down(int u)
{
    int t=u;
    if(u*2<=cnt&&q[u*2]<q[t])t=u*2;
    if(u*2+1<=cnt&&q[u*2+1]<q[t])t=u*2+1;
    if(t!=u)
    {
        good_swap(t,u);
        down(t);
    }
}

int main()
{
    int m;
    scanf("%d,",&m);
    while (m -- ){
        char op[5];
        int d,k;
        scanf("%s",op);
        if(!strcmp(op,"I"))
        {
            scanf("%d",&d);
            q[++cnt]=d;
            s++;
            ph[s]=cnt;
            hp[cnt]=s;
            up(cnt);
        }
        else if(!strcmp(op,"PM"))
        {
            printf("%d\n",q[1]);
        }
        else if(!strcmp(op,"DM"))
        {
            good_swap(1,cnt);
            cnt--;
            down(1);
        }
        else if(!strcmp(op,"D"))
        {
            scanf("%d",&k);
            int u=ph[k];
            good_swap(u,cnt);
            cnt--;
            up(u);
            down(u);
        }
        else{
            scanf("%d%d",&k,&d);
            int u=ph[k];
            q[u]=d;
            up(u);
            down(u);
        }
        
    }
    return 0;
}

的堆中,另外还需一个hp[i]=k;表示第i下标存的是第k个数,这俩数组是相互映射的关系

所以交换函数的话ph,hp以及q都要交换

void good_swap(int x,int y)
{
    swap(ph[hp[x]],ph[hp[y]]);
    swap(hp[x],hp[y]);
    swap(q[x],q[y]);
}

接下来就是全部的代码

### 使用C++数组实现单链表 在某些情况下,为了简化内存管理或者出于特定应用需求,可以选择使用数组模拟单链表的行为。这种方式下,数组中的每一个元素代表一个节点,并且每个节点不仅存储数据还保存指向下一个节点的位置索引。 #### 节点表示法 对于基于数组的单链表来说,可以通过定义结构体`NodeInfo`来描述节点的信息: ```cpp struct NodeInfo { int data; // 存储的数据项 size_t nextIndex;// 下一节点所在数组位置的索引,-1表示无后续结点即为尾部 }; ``` 这里采用了一个整数变量`data`用于存放实际数据,而另一个成员`nextIndex`则用来指示下一个节点位于数组中的哪个位置,如果该值等于-1,则意味着当前节点之后没有其他节点[^1]。 #### 初始化与分配空间 创建固定大小的最大容量N的数组作为底层存储介质,在初始化阶段设定好头指针head以及记录有效长度length两个辅助属性: ```cpp const static size_t MAX_SIZE = 100; size_t head = -1, length = 0; std::array<NodeInfo, MAX_SIZE> listArray{}; ``` 此段代码声明了一个名为`listArray`的标准库模板类`std::array`,其内部包含着最多可达MAX_SIZE数量级的对象实例化自之前提到过的`NodeInfo`结构体类型[^2]。 #### 插入新元素 当需要往列表里添加新的条目时,应该考虑两种情况——头部插入或是常规位置处追加。下面展示的是后者对应的函数逻辑: ```cpp bool insertAt(size_t index, const T& value){ if(length >= MAX_SIZE || index > length) return false; for(auto pos=length;pos>index;--pos){ listArray[pos]=listArray[pos-1]; if(pos!=length)//更新除最后一个外所有受影响单元格内的链接关系 listArray[pos].nextIndex=pos+1; } listArray[index]={value,(int)index+1}; ++length; if(index==0 && head==-1){head=0;} //首次插入设置头指针 return true; } ``` 上述过程会遍历从现有末端到指定目标位之间的区间范围,依次挪动各实体并调整它们之间相互关联性的表达方式直到腾出所需空隙为止[^3]。 #### 删除已有项目 移除某个具体位置上的对象同样涉及到前后衔接关系的变化处理: ```cpp void removeAt(size_t index){ if(index>=length||length<=0)return ; --length; for(;index<length;++index){ listArray[index]=listArray[index+1]; if(listArray[index].nextIndex!=-1)--listArray[index].nextIndex; } if(head==(int)index&&length>0)head=index%length; } ``` 这段程序负责将被删除点右侧剩余部分向前迁移覆盖掉原址内容的同时修正因变动引起的相关联接路径改变问题。 #### 完整示例代码 综合以上各个功能模块可得如下完整的演示版本: ```cpp #include<iostream> using namespace std; template<typename T> class ArrayBasedLinkedList{ private: struct NodeInfo { T data; ssize_t nextIndex=-1; }; public: const static size_t CAPACITY = 100; ArrayBasedLinkedList():_len(0), _head(-1){} bool empty()const{return !_len;} void clear(){_len=_head=0;} protected: array<NodeInfo,CAPACITY>_nodes; size_t _len,_head; public://接口方法... }; //...省略其余操作的具体实现... int main(){ ArrayBasedLinkedList<int> my_list; // 测试用例... return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值