在std :: map中查找不存在的键

时间:2009-11-27 19:04:26

标签: c++ data-structures stl

有没有办法在地图中找到一个不存在的密钥?

我正在使用std::map<int,myclass>,我想自动生成新项目的密钥。可以从插图中以不同的顺序从地图中删除项目。

myclass项目可能或可能不相同,因此它们不能作为自己的密钥。

在程序运行期间,生成和删除的项目数量没有限制,因此我不能使用计数器作为密钥。

具有相同功能和性能的替代数据结构可以。

修改

我尝试为我的项目构建一个容器 - 这样我就可以根据他们的键删除/修改项目,我可以迭代这些项目。键值本身对我没有任何意义,但是,其他对象将存储这些键以供其内部使用。

我不能使用增量计数器的原因是,在程序的生命周期中它们可能超过2 ^ 32(或理论上为2 ^ 64)项,但是物理0理论上可能仍然存在项目已删除。

向std :: map询问最低值的未使用密钥会很好,所以我可以将它用于新项目,而不是使用向量或其他外部存储用于未使用的密钥。

10 个答案:

答案 0 :(得分:4)

我建议使用计数器和队列的组合。从地图中删除项目时,将其键添加到队列中。然后,队列会跟踪已从地图中删除的密钥,以便可以再次使用它们。要获取新密钥,首先要检查队列是否为空。如果不是,请关闭顶部索引并使用它,否则使用计数器获取下一个可用密钥。

答案 1 :(得分:3)

让我看看我是否理解。你想要做的是

寻找钥匙。 如果不存在,请插入元素。

可能会删除项目。

保持一个计数器(等待等待)和一个向量。向量将保留已删除项目的ID。 当您要插入新元素时,请在向量中查找键。如果vector不为空,请移除该键并使用它。如果它是空的,从柜台拿一个(计数器++)。 但是,如果您从地图中删除项目,则只会停留在计数器上。

替代: 如何使用元素的内存地址作为键?

答案 2 :(得分:2)

  

有没有找到不存在的方法   键入地图?

我不确定你的意思。你怎么能找到不存在的东西?你的意思是,有没有办法判断地图是否包含密钥?

如果这就是你的意思,你只需使用find函数,如果该键不存在,它将返回一个指向end()的迭代器。

if (my_map.find(555) == my_map.end()) { /* do something */ }

你继续说...

  

我正在使用std :: map,和   我想自动生成一个密钥   对于新项目。项目可能会被删除   来自地图的顺序不同   他们的插入。 myclass项目可能或可能不相同,因此它们不能作为自己的密钥。

我有点不清楚你要在这里完成什么。您的问题似乎是您希望在地图中存储myclass的实例,但由于您可能有myclass的重复值,因此需要某种方法来生成唯一键。为什么不使用std::multiset<myclass>而只是存储重复项,而不是这样做?当您查找myclass的特定值时,multiset将向所有具有该值的myclass实例返回一个迭代器。您只需要为myclass实现比较仿函数。

答案 3 :(得分:2)

我想说的是,对于一般情况,当key可以具有map允许的任何类型时,这是不可能的。甚至能够说出是否存在一些未使用的密钥需要一些关于类型的知识。

如果我们考虑使用int的情况,你可以存储未使用的密钥的std ::连续段的集合(因为这些段不重叠,可以使用自然排序 - 只需比较它们的起点)。当需要新密钥时,您将获取第一个段,切断第一个索引并将其余部分放在集合中(如果其余部分不为空)。当释放某个键时,您会发现集合中是否存在邻居段(由于设置性质,可能具有O(log n)复杂度)并在需要时执行合并,否则只需将[n,n]段放入集合中。

通过这种方式,您肯定会有相同的时间复杂度和内存消耗顺序,因为映射在请求历史记录中是独立的(因为段数不能超过map.size()+ 1)

类似的东西:

class TKeyManager
{
public:
    TKeyManager()
    {
        FreeKeys.insert(
          std::make_pair(
            std::numeric_limits<int>::min(),
            std::numeric_limits<int>::max());
    }
    int AlocateKey()
    {
        if(FreeKeys.empty())
            throw something bad;
        const std::pair<int,int> freeSegment=*FreeKeys.begin();
        if(freeSegment.second>freeSegment.first)
            FreeKeys.insert(std::make_pair(freeSegment.first+1,freeSegment.second));
        return freeSegment.first;
    }
    void ReleaseKey(int key)
    {
        std:set<std::pair<int,int>>::iterator position=FreeKeys.insert(std::make_pair(key,key)).first;
        if(position!=FreeKeys.begin())
        {//try to merge with left neighbour
            std::set<std::pair<int,int>>::iterator left=position;
            --left;
            if(left->second+1==key)
            {
                left->second=key;
                FreeKeys.erase(position);
                position=left;
            }
        }
        if(position!=--FreeKeys.end())
        {//try to merge with right neighbour
            std::set<std::pair<int,int>>::iterator right=position;
            ++right;
            if(right->first==key+1)
            {
                position->second=right->second;
                FreeKeys.erase(right);
            }
        }
    }
private:
    std::set<std::pair<int,int>> FreeKeys;
};

答案 4 :(得分:0)

请您说明为什么不能使用简单的增量计数器作为自动生成的密钥? (插入时增加)?这似乎没有问题。

答案 5 :(得分:0)

  • 考虑一下,您决定如何生成非基于计数器的密钥,并发现在批量生成它们比逐个生成它们要有效得多。
  • 将此生成器证明为“无限”和“有状态”(这是您的要求),您可以使用1000个未使用的密钥创建第二个固定大小的容器。
  • 使用此容器中的键为地图中的新条目供应,并返回键以进行回收。
  • 设置一些低“阈值”以对密钥容器达到低水平作出反应,并使用“无限”发生器批量重新填充密钥。

实际发布的问题仍然存在“如何基于非计数器制作高效的发电机”。您可能希望再次查看“无穷大”要求,并检查64位或128位计数器是否仍能在1000年的有限时间内满足您的算法。

答案 6 :(得分:0)

使用uint64_t作为关键类型的序列,或者即使您认为它还不够

struct sequence_key_t {
    uint64_t upper; 
    uint64_t lower; 
    operator++();
    bool operator<() 
};

像:

sequence_key_t global_counter;

std::map<sequence_key_t,myclass> my_map;

my_map.insert(std::make_pair(++global_counter, myclass()));

你不会有任何问题。

答案 7 :(得分:0)

和其他人一样,我很难搞清楚你想要什么。听起来你想要创建一个项目,如果找不到它。 sdt :: map :: operator [](const key_type&amp; x)将为您完成此任务。

std::map<int, myclass> Map;
myclass instance1, instance2;

Map[instance1] = 5;
Map[instance2] = 6;

这是你在想什么?

答案 8 :(得分:0)

与其他答案一起,我建议使用一个简单的计数器来生成id。如果您担心完全正确,可以使用计数器的任意精度整数,而不是内置类型。或类似以下内容,它将遍历所有可能的字符串。

void string_increment(std::string& counter)
{
    bool carry=true;
    for (size_t i=0;i<counter.size();++i)
    {
        unsigned char original=static_cast<unsigned char>(counter[i]);
        if (carry)
        {
            ++counter[i];
        }
        if (original>static_cast<unsigned char>(counter[i]))
        {
            carry=true;
        }
        else
        {
            carry=false;
        }
    }
    if (carry)
    {
        counter.push_back(0);
    }
}

e.g。所以你有:

std::string counter; // empty string
string_increment(counter); // now counter=="\x00"
string_increment(counter); // now counter=="\x01"
...
string_increment(counter); // now counter=="\xFF"
string_increment(counter); // now counter=="\x00\x00"
string_increment(counter); // now counter=="\x01\x00"
...
string_increment(counter); // now counter=="\xFF\x00"
string_increment(counter); // now counter=="\x00\x01"
string_increment(counter); // now counter=="\x01\x01"
...
string_increment(counter); // now counter=="\xFF\xFF"
string_increment(counter); // now counter=="\x00\x00\x00"
string_increment(counter); // now counter=="\x01\x00\x00"
// etc..

答案 9 :(得分:0)

另一种选择,如果地图中实际工作集足够小,则使用递增键,然后在计数器即将换行时重新生成键。该解决方案仅需要临时额外存储。哈希表的性能将保持不变,密钥生成只是一个if和一个增量。

当前工作集中的项目数确实可以确定这种方法是否可行。