acwing c++基础算法笔记 模拟堆 模拟散列表

堆 AcWing 839. 模拟堆

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

I x,插入一个数 x;

PM,输出当前集合中的最小值;

DM,删除当前集合中的最小值(数据保证此时的最小值唯一);

D k,删除第 k 个插入的数;

C k x,修改第 k 个插入的数,将其变为 x;

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

输入格式

第一行包含整数 N。

接下来 N 行,每行包含一个操作指令,操作指令为 I x,PM,DM,D k 或 C k x 中的一种。

输出格式

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

每个结果占一行。

#include<iostream>
#include<algorithm>
#include<string.h>

using namespace std;

const int N=100010;

int h[N],hp[N],ph[N],size;

void heap_swap (int a,int b)
{
        swap (ph[hp[a]],ph[hp[b]]);
        swap (hp[a],hp[b]);
        swap (h[a],h[b]);
}

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

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

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

一般哈希 AcWing 840. 模拟散列表

维护一个集合,支持如下几种操作:

I x,插入一个整数 x;

Q x,询问整数 x 是否在集合中出现过;

现在要进行 N 次操作,对于每个询问操作输出对应的结果。

输入格式

第一行包含整数 N ,表示操作数量。

接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。

输出格式

对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。

每个结果占一行。

// 模拟散队列  拉链法           哈希表 
#include<iostream>
#include<cstring>

using namespace std;

const int N=100003;

int h[N],e[N],ne[N],idx;

void insert (int x)
{
        int k=(x%N+N)%N;
        e[idx]=x;
        ne[idx]=h[k];
        h[k]=idx++;
}

bool find (int x)
{
        int k=(x%N+N)%N;
        for (int i=h[k];i!=-1;i=ne[i])
                if (e[i]==x)
                        return true;
        return false;
}
int main()
{
        int n;
        scanf ("%d",&n);
        memset(h,-1,sizeof h);
        while (n--)
        {
                char op[2];
                int x;
                scanf ("%s%d",op,&x);
                if (*op=='I') insert (x);
                else
                {
                        if (find (x)) puts ("Yes");
                        else puts ("No");
                }
        }
        return 0;
}
插入函数 insert

确定哈希桶位置:首先通过 int k=(x%N+N)%N; 这行代码计算要插入元素 x 在哈希表中的位置 k。这里先对 x 取余 N,然后再加上 N 后再次取余 N,是为了确保得到的索引值在 0N - 1 的范围内,处理可能出现的负数取余情况(在 C++ 中,负数取余的结果可能不符合预期,这样处理能保证得到正确的正整数索引)。

构建链表插入元素:接着 e[idx]=x; 将当前要插入的元素值 x 存储到 e 数组中以 idx 为索引的位置。ne[idx]=h[k]; 这行代码把当前位置 idx 的下一个节点指针设置为原来哈希桶 k 对应的链表头指针(也就是 h[k] 的值,如果原来该哈希桶没有元素,h[k]-1),相当于将新元素插入到链表头部。最后 h[k]=idx++; 把当前插入元素的索引 idx 赋值给 h[k],使得 h[k] 指向新插入元素所在的链表节点,然后将 idx 的值加 1,为下一次插入元素准备好新的索引。

查找函数 find

确定哈希桶位置:同样先通过 int k=(x%N+N)%N; 计算出元素 x 在哈希表中对应的哈希桶位置 k

遍历链表查找元素:然后通过一个 for 循环,从哈希桶 k 对应的链表头指针 h[k] 开始遍历链表(只要当前节点指针 i 不为 -1,就继续循环,通过 i=ne[i] 不断更新指针指向下一个节点),在遍历过程中,比较链表中每个节点存储的元素值(通过 e[i] 获取)与要查找的元素 x 是否相等,如果找到相等的元素,就返回 true,表示元素在哈希表中存在;如果遍历完整个链表都没有找到相等的元素,就返回 false,表示元素不在哈希表中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值