在std :: map中查找最近或准确的键

时间:2015-02-09 07:43:25

标签: c++

我需要创建一个查找表,它将长度链接到一个时间间隔(两者都是数据类型为double)。键在插入时线性增加,因此它已经被排序(也许unordered_map会更好?)。

我正在寻找的方法是找到一个最符合当前长度的键来获取时间值,或者更好地找到围绕长度的两个键(给定键位于它们之间)所以我可以找到两个时间值之间的插值。

我还需要尽可能最佳的性能,因为它会实时调用。

编辑:我宁愿以下是对下面第一个答案的评论,但格式很难阅读。

我尝试执行以下操作,但它似乎返回相同的迭代器(5.6):

std::map<double, double> map;
map.insert(std::pair<double, double>(0.123, 0.1));
map.insert(std::pair<double, double>(2.5, 0.4));
map.insert(std::pair<double, double>(5.6, 0.8));

std::map<double, double>::iterator low, high;
double pos = 3.0;
low = map.lower_bound(pos);
high = map.upper_bound(pos);

我如何得到“低”&#39;指向最后一个元素&lt;比用于搜索的密钥?

编辑2: 傻我,低......如果它不是第一个元素,它会做到这一点。

到达那里:)

7 个答案:

答案 0 :(得分:17)

为此,您可以使用std::map::lower_bound

  

返回指向第一个不小于key的元素的迭代器。

std::map::equal_range

  

返回一个范围,其中包含容器中具有给定键的所有元素。


在您的情况下,如果您想要最近的条目,则需要检查返回的条目和之前的条目并比较差异。像这样的东西可能会起作用

std::map<double, double>::iterator low, prev;
double pos = 3.0;
low = map.lower_bound(pos);
if (low == map.end()) {
    // nothing found, maybe use rbegin()
} else if (low == map.begin()) {
    std::cout << "low=" << low->first << '\n';
} else {
    prev = std::prev(low);
    if ((pos - prev->first) < (low->first - pos))
        std::cout << "prev=" << prev->first << '\n';
    else
        std::cout << "low=" << low->first << '\n';
}

答案 1 :(得分:5)

&#34;可能获得最佳效果&#34; - 如果您按递增顺序插入元素,则可以push_back / emplace_back将它们转换为std::vector然后使用std::lower_bound - 您将获得更好的缓存利用率,因为数据将被打包到连续的地址空间中。

答案 2 :(得分:3)

您当然可以使用lower_bound和upper_bound,它们在运行时是对数的。他们应该做你想做的事。

std::map<double,double>::iterator close_low;
//... your_map ...
close_low=your_map.lower_bound (current_length);

这应该给你一个迭代器,它的第一个map元素的键是&lt;当前长度。同样使用upper_bound并且你有时间被包围。

答案 3 :(得分:2)

这里的函数std::lower_bound()std::upper_bound()非常有用。

lower_bound()为您正在寻找的值提供>=的第一个元素; upper_bound()提供的第一个元素是>而不是值。

例如,在以下列表中搜索值5{1,3,5,5,6} 1 使用lower_bound()返回第三个元素,而upper_bound()会返回第五个元素。 如果这两个函数返回相同的内容x,那么您要查找的值不会出现在列表中。 它之前的值为x-1,紧随其后的值为x

1 正如Tony D在评论中指出的那样,问题是地图,通常不包含重复的元素。 我保留这个例子来说明这两个函数。

答案 4 :(得分:0)

完整的通用解决方案(来自Olaf Dietscheanswer的原始思想):

#include <map>
#include <iostream>
#include <cstdint>

template <typename T1, typename T2>
T1 findClosestKey(const std::map<T1, T2> & data, T1 key)
{
    if (data.size() == 0) {
        throw std::out_of_range("Received empty map.");
    }

    auto lower = data.lower_bound(key);

    if (lower == data.end()) // If none found, return the last one.
        return std::prev(lower)->first;

    if (lower == data.begin())
        return lower->first;

    // Check which one is closest.
    auto previous = std::prev(lower);
    if ((key - previous->first) < (lower->first - key))
        return previous->first;

    return lower->first;
}

int main () {
double key = 3.3;

std::map<double, int> data = {{-10, 1000}, {0, 2000}, {10, 3000}};

std::cout << "Provided key: " << key << ", closest key: " << findClosestKey(data, key) << std::endl;
return 0;
}

答案 5 :(得分:0)

#include <map>

template <typename T1, typename T2>
std::map<T1, T2>::iterator nearest_key(const std::map<T1, T2>& map, T1 key) {
    auto lower_bound = map.lower_bound(key);
    auto upper_bound = lower_bound; upper_bound++;
    if (lower_bound == map.end()) return upper_bound;
    if (upper_bound == map.end()) return lower_bound;
    unsigned int dist_to_lower = std::abs((int)lower_bound->first - (int)key);
    unsigned int dist_to_upper = std::abs((int)upper_bound->first - (int)key);
    return (dist_to_upper < dist_to_lower) ? upper_bound : lower_bound;
}

答案 6 :(得分:0)

以上是错误的。应该是这样的 模板

typename std::map<T1, T2>::const_iterator nearest_key(const std::map<T1, T2>& map, T1 key)
{
    auto lower_bound = map.lower_bound(key);
    if (lower_bound == map.end()) return --lower_bound;
    auto upper_bound = lower_bound; upper_bound++;
    if (upper_bound == map.end()) return lower_bound;
    auto dist_to_lower = lower_bound->first - key;
    auto dist_to_upper = upper_bound->first - key;
    return (dist_to_upper < dist_to_lower) ? upper_bound : lower_bound;
}