C++算法之更加复杂的数据结构

更加复杂的数据结构

1.并查集

并查集(union-find,or disjoint set)可以动态的连通两个点,并且可以非常快速的判断两个点是否连通。假设存在n个节点,我们将所有节点的父亲标为自己;每次要连接节点i和j时,我们可以将i的父亲标为j;每次要查询两个节点是否相连时,我们可以查找i和j的祖先是否最终为一个人。
在这里插入图片描述
题目一:Redundant Connection(Medium)
在无向图找出一条边,移除它之后该图能够成为一棵树(即无向无环图)。如果有多个解,返
回在原数组中位置最靠后的那条边。
在这里插入图片描述
因为需要判断是否两个节点被重复连通,可以使用并查集来解决此类问题。具体实现算法如下所示。

class UF
{
    vector<int>id;
public:
    UF(int n): id(n){
        id.begin(),id.end(),0);//iota函数可以把数组初始化为0到n-1
    }
    int find(int p)
    {
        while(p!-id[p])
        {
            p=id[p];
        }
        return p;
    }
    void connect(int p,int q)
    {
        id[find(p)]=find(q);
    }
    bool isConnected(int p,int q)
    {
        retrun find(p)==find(q);
    }
};
class Solution{
public:
    vector<int> findRedundantConnection(vector<vector<int>>&edges)
    {
        int n=dege.size();
        UF uf(n+1);
        for(auto e:edges)
        {
            int u=e[0],v=e[1];
            if(uf.isConnected(u,v))
            {
                return e;
            }
            uf.connect(u,v);
        }
        return vector<int>{-1,-1};
    }
};
                

为了加速查找,我们可以使用路径压缩和按秩合并来优化并查集,具体下发如下:

class UF
{
    vector<int>id,size;
public:
    UF(int n):id(n),size(n,1)
    {
        iota(id.begin(),id.end(),0);//iota函数可以把数组初始化为0到n-1
    }
    int find(int p)
    {
        while(p!=id[p])
        {
            id[p]=id[id[p]];//路径压缩,使得下次查找更快
            p=id[p];
        }
        return p;
    }
    void connect(int p,int q)
    {
        int i=find(p),j=find(q);
        if(i!=j)
        {
            //按秩合并,每次合并都把深度较小的集合合并在深度较大的集合下边
            if(size[i]<size[j])
            {
                id[i]=j;
                size[j]+=size[i];
            }
            else{
                id[j]=i;
                size[i]+=size[j];
            }
        }
    }
    bool isConnected(int p,int q)
    {
        return find(p)==find(q);
    }
};

2.复杂数据结构

这一类题通常采用 unordered_map 或 map 辅助记录,从而加速寻址;再配上 vector 或者 list进行数据储存,从而加速连续选址或删除值。

LRU Cache(Medium)
设计一个固定大小的,最少最近使用缓存 (least recently used cache, LRU)。每次将信息插入未满的缓存的时候,以及更新或查找一个缓存内存在的信息的时候,将该信息标为最近使用。在缓存满的情况下将一个新信息插入的时候,需要移除最旧的信息,插入新信息,并将该信息标为最近使用。
在这里插入图片描述
我们采用一个链表 list<pair<int, int>> 来储存信息的 key 和 value,链表的链接顺序即为最近使用的新旧顺序,最新的信息在链表头节点。同时我们需要一个嵌套着链表的迭代器的 unordered_map<int, list<pair<int, int>>::iterator> 进行快速搜索,存迭代器的原因是方便调用链表的splice 函数来直接更新查找成功(cash hit)时的信息,即把迭代器对应的节点移动为链表的头节点。

class LRUCache
{
    unordered_map<int,list<pair<int,int>>::iterator>>hash;
    list<pair<int,int>>cache;
    int size;
public:
    LRUCache(int capacity):size(capacity){}
    int get(int key)
    {
        auto it=hash.find(key);
        if(it==hash.end())
        {
            return -1;
        }
        cache.splice(cache.begin(),cache,it->second);
        return it->second-second;
    }
    void put(int key,int value)
    {
        auto it=hash.find(key);
        if(it!=hash.end())
        {
            it->second->second=value;
            return cache.splice(cache.begin(),cache,it->second);
        }
        cache.insert(cache.begin(),make_pair(key,value));
        hash[key]=cache.begin();
        if(cache.size()>size)
        {
            hash.erase(cache.back().first);
            cache.pop_back();
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月醉窗台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值