C++ 查找未使用的最低数字

C++ 查找未使用的最低数字,c++,algorithm,stl,C++,Algorithm,Stl,我已经设置了一个std映射来映射一些数字,在这一点上,我知道我从一个映射到什么数字,例如: std::map<int, int> myMap; map[1] = 2; map[2] = 4; map[3] = 6; 有什么简单的方法吗 我考虑在将数字添加到地图时建立一个有序的数字列表,这样我就可以只查找1,而不查找它,使用它,添加它等等。我会使用一个双向地图类来解决这个问题。这样,您可以简单地检查值1是否存在等 编辑 使用bimap的好处是,已经有了可靠的bimap实现,即使搜索

我已经设置了一个std映射来映射一些数字,在这一点上,我知道我从一个映射到什么数字,例如:

std::map<int, int> myMap;

map[1] = 2;
map[2] = 4;
map[3] = 6;
有什么简单的方法吗


我考虑在将数字添加到地图时建立一个有序的数字列表,这样我就可以只查找1,而不查找它,使用它,添加它等等。

我会使用一个双向地图类来解决这个问题。这样,您可以简单地检查值1是否存在等

编辑

使用bimap的好处是,已经有了可靠的bimap实现,即使搜索下一个自由数为O(n),也只有当n较大时才有问题(或者,如果n适中,并且经常调用)。总体而言,这是一个简单的实现,不太可能容易出错且易于维护

如果n很大,或者此操作执行得非常频繁,那么投入精力实施更高级的解决方案是值得的。IMHO.

类似于

typedef std::set<int> SetType;
SetType used;           // The already used numbers
int freeCounter = 1;    // The first available free number

void AddToMap(int i)
{
    used.insert(i);
    // Actually add the value to map
}

void GetNewNumber()
{
    SetType::iterator iter = used.lower_bound(freeCounter);
    while (iter != used.end() && *iter == freeCounter)
    {
        ++iter;
        ++freeCounter;
    }
    return freeCounter++;
}
typedef std::set SetType;
使用的SetType;//已经使用的数字
int freeconter=1;//第一个可用的免费号码
无效添加映射(int i)
{
使用。插入(i);
//实际上,将值添加到映射中
}
void GetNewNumber()
{
SetType::迭代器iter=used.lower_-bound(freeCounter);
while(iter!=used.end()&&&*iter==freeCounter)
{
++iter;
++免费计数器;
}
返回freeCounter++;
}
如果您的映射非常大,但非常稀疏,这将像o(log(N))一样工作,其中N是映射中的项数-在大多数情况下,您不必迭代集合,也不需要执行几个步骤。
否则,如果映射中有很少的间隙,那么您最好在[1..maxValueInMap]范围内有一组空闲项。

在UNIX内核中查找最低的未使用数是一个非常常见的操作,因为每个
打开
/
套接字
/等等。系统调用应该绑定到最低的未使用FD数

在Linux上,中的算法是:

  • 跟踪
    下一个\u fd
    ,一个低水位线——它不一定100%准确
  • 每当释放FD时,
    next\u FD=min(FD,next\u FD)
  • 要分配FD,请从下一步开始搜索位图。FD是线性的,但仍然非常快,因为它一次每长跨步需要
  • 分配FD后,
    next\u FD=FD+1
FreeBSD遵循相同的思想:从
intfd_freefile;/*大约为下一个可用文件*/
,并向上搜索位图


然而,这些都是在假设大多数流程很少有FD打开的情况下运行的,很少有FD打开了数千个FD。如果数字会更高,有稀疏的洞,常见的解决方案(据我所见)是

#包括
#包括
#包括
使用名称空间std;
int高水位线=0;
向量未使用的_数=向量();
int get_new_number(){
如果(已使用\u number.empty())
返回高水位标记++;
pop_heap(未使用的_number.begin()、未使用的_number.end()、greater());
返回未使用的_编号。弹出_back();
}
无效循环编号(整数编号){
未使用的_编号。将_向后推(编号);
push_heap(未使用的_numbers.begin()、未使用的_numbers.end()、greater());
}
(未经测试的代码…想法是:保持一个高水位线;尝试从低于高水位线的
未使用的
处偷取,或者从高水位线处偷取;返回到
未使用的


如果你假设使用的数字是稀疏的,那么Dmitry的解决方案更有意义。

这隐藏了算法。。。哪一个是Q。哪个算法用于搜索该类上的键控项?检查值n是否存在以查找最低可用声音,就像一个O(n)练习。这是一个比乍一看更好的算法。请注意,尽管对GetNewNumber()的某些调用可能需要执行许多步骤,但freeCounter的总次数会增加(因此GetNewNumber()的总运行时间除以集合操作的对数因子)以映射中指定的项数为界(因为如果已指定N个数字,则freeCounter会增加)≤ N+1)。因此,如果您已分配到map[N],那么到目前为止对GetNewNumber()的所有调用将只花费O(N log N)时间:GetNewNumber是O(log N)摊销的。(换句话说,即使映射不是稀疏的,该算法也能很好地工作;它总是O(logN)摊销。)GetNewNumber()是基于索引值协调的常见缺失项搜索,但我仍然可以理解为什么你们说这是一个O(logN)函数。我希望最低值是INT_MIN和INT_MIN+1?如果它是一个映射,正如我在对另一个答案的评论中指出的那样,另一个解决方案实际上并不要求使用的数字是稀疏的(至少要达到对数因子)。这个答案很有趣:这个问题似乎只有插入,没有删除,并且可能分配了任意高的数字,但是你的是插入和删除,总的“范围”足够小,可以保留一个未使用的数字列表。我提到“稀疏”是因为如果你一次只有5个未使用的数字,O(5)很好而且很小,但是如果其中有一半未使用,O(logn)(相对而言)要大得多。
typedef std::set<int> SetType;
SetType used;           // The already used numbers
int freeCounter = 1;    // The first available free number

void AddToMap(int i)
{
    used.insert(i);
    // Actually add the value to map
}

void GetNewNumber()
{
    SetType::iterator iter = used.lower_bound(freeCounter);
    while (iter != used.end() && *iter == freeCounter)
    {
        ++iter;
        ++freeCounter;
    }
    return freeCounter++;
}
#include <algorithm>
#include <functional>
#include <vector>

using namespace std;

int high_water_mark = 0;
vector<int> unused_numbers = vector<int>();

int get_new_number() {
    if (used_numbers.empty())
        return high_water_mark++;
    pop_heap(unused_numbers.begin(), unused_numbers.end(), greater<int>());
    return unused_numbers.pop_back();
}

void recycle_number(int number) {
    unused_numbers.push_back(number);
    push_heap(unused_numbers.begin(), unused_numbers.end(), greater<int>());
}