在std::映射中查找最接近或精确的关键字

Finding the closest or exact key in a std::map

本文关键字:关键字 查找 std 映射 最接近      更新时间:2023-10-16

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

我正在寻找的是一种方法,可以找到一个与当前长度最匹配的关键点来获得时间值,或者更好地找到长度周围的两个关键点(给定的关键点在它们之间),这样我就可以找到两个时间值之间的插值。

我还需要尽可能好的表现,因为它将被实时调用。

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

我尝试了以下操作,但似乎返回了相同的迭代器(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);

我如何使"low"指向<比用来搜索的钥匙?

编辑2:愚蠢的我,只要它不是第一个元素,"low——"就会这么做。

到达:)

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

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

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';
}

"尽可能获得最佳性能"-如果按递增顺序插入元素,则可以将它们push_back/emplace_back放入std::vector中,然后使用std::lower_bound-因为数据将被打包到连续的地址空间中,所以缓存利用率会更高。

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

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

这应该为键为<当前长度。对upper_bound也这样做,你的时间就被包围了。

函数std::lower_bound()std::upper_bound()在这里会很有用。

lower_bound()将第一个元素>=赋予您要查找的值;CCD_ 11给出的第一个元素是CCD_。

例如,在以下列表中搜索值5:使用lower_bound()搜索{1,3,5,5,6}1将返回第三个元素,而upper_bound()将返回第五个元素。如果这两个函数返回相同的x,那么您要查找的值不在列表中。刚好在它之前的值是x-1,刚好在它之后的值是x

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

完整的通用解决方案(最初的想法取自Olaf Dietsche的答案):

#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;
}
#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;
}
上面的

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

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;
}

我不得不解决同样的问题,但提供的答案并不能给我正确的答案。如果有人想要,这里有一个完整的例子

            template <typename T>
            class Key
            {
                public:
                    T x;
                    T y;
                    explicit Key(T x_, T y_): x(x_), y(y_){}
                    bool operator<( const Key<T> right) const{
                        if((x == right.x) && (y == right.y)){
                            return false;
                        }
                        return true;
                    }
                    T operator-( const Key<T> right) const{
                        return std::sqrt(std::pow(x-right.x, 2) + std::pow(y-right.y, 2));
                    }
            };
           int main(int argc, char **argv)
              {
                std::map<Key<double>, double> pixel_mapper;
                Key<double> k1(400,5);
                Key<double> k2(4,5);
                Key<double> k3(4,5);
                Key<double> k4(4667,5);
                Key<double> k5(1000,5);
                pixel_mapper.insert(std::pair<Key<double>, double>(k2, 5));
                pixel_mapper.insert(std::pair<Key<double>, double>(k3, 5));
                pixel_mapper.insert(std::pair<Key<double>, double>(k4, 5));
                pixel_mapper.insert(std::pair<Key<double>, double>(k1, 5));
                auto it = std::min_element( pixel_mapper.begin(), pixel_mapper.end(),
                      [&](const auto &p1, const auto &p2)
                      {
                        return std::abs(p1.first - k5) < std::abs(p2.first - k5);
                      });
                std::cout<< it->first.x << "," << it->first.y << std::endl;
                return 0;
                }

在这里,我们可以使用std:min_element来获得最接近的值,以防不存在确切的密钥