C++ 获取std::集合中与给定元素最近的元素

C++ 获取std::集合中与给定元素最近的元素,c++,stl,set,C++,Stl,Set,我有一组(已排序的)无符号int。我需要找到最接近给定数字的元素 我正在寻找使用标准库的解决方案, 我的第一个解决方案是使用二进制搜索,但STL的实现仅在元素存在时返回。 这篇文章很有帮助,我实现了一个基于std::lower_bound方法的解决方案 (*假设集合有2个以上的元素,则不进行空/边界检查): #包括 #包括 #包括 #包括 int main() { std::set mySet={3425626850050244}; 无符号整数searchedElement=260; 无符号整数

我有一组(已排序的)无符号
int
。我需要找到最接近给定数字的元素

我正在寻找使用标准库的解决方案, 我的第一个解决方案是使用二进制搜索,但STL的实现仅在元素存在时返回。 这篇文章很有帮助,我实现了一个基于
std::lower_bound
方法的解决方案

(*假设集合有2个以上的元素,则不进行空/边界检查):

#包括
#包括
#包括
#包括
int main()
{
std::set mySet={3425626850050244};
无符号整数searchedElement=260;
无符号整数闭合符;
自动下限=mySet.下限(searchedElement);
if(下限==mySet.end()){
closestElement=*(-下限);
}
std::set::迭代器preveElement=--下限;
bool ispreveclosest=std::abs(*preveElement-searchedElement)>std::abs(*下限-searchedElement);
closestElement=isPrevClosest?*prevElement:*下限;

std::cout您可以使用std::min_element():作为一个comperator,给它一个lambda,返回absulute diff,例如

std::min_元素(mySet.begin(),mySet.end(),[searchedElement](常量无符号整数a,常量无符号整数b){
返回std::abs(searchedElement-a)
然而,我认为这将不再适用于二进制搜索


编辑:另外,正如下面的评论中所述,
std::abs(x-y)
对于无符号int值,当
x

时,可能会返回一个意外的大整数。我认为没有比使用
更好的解决方案了。下限
。您可以将算法包装到函数模板中:

template<typename Set>
auto closest_element(Set& set, const typename Set::value_type& value)
    -> decltype(set.begin())
{
    const auto it = set.lower_bound(value);
    if (it == set.begin())
        return it;

    const auto prev_it = std::prev(it);
    return (it == set.end() || value - *prev_it <= *it - value) ? prev_it : it;
}
模板
自动最近的_元素(Set&Set,const typename Set::value_type&value)
->decltype(set.begin())
{
const auto it=set.下限(值);
if(it==set.begin())
归还它;
const auto prev_it=std::prev(it);

return(it==set.end()| | value-*prev_itstd::set
容器适用于查找相邻元素,即查找在给定元素之后或之前的元素。考虑到您面临的问题:

我正在寻找一个使用标准库的解决方案,我的第一个解决方案是使用二进制搜索,但是STL的实现仅在元素存在时返回

在不改变逻辑的情况下,仍然有一种方法可以遵循:如果元素(您希望找到其最近的元素)在集合中不存在,那么您只需将其插入集合中(这需要集合大小的对数时间)。接下来,您将找到与刚刚添加的元素最近的元素。最后,在完成后将其从集合中删除,以便集合与以前保持相同

当然,如果元素已经在集合中,则不需要在集合中插入或删除任何内容。因此,您需要跟踪是否添加了该元素


以下函数是上述思想的一个示例:

#include <set>

unsigned int find_closest_element(std::set<unsigned int> s, unsigned int val) {
   bool remove_elem = false;
   auto it = s.find(val);

   // does val exist in the set?
   if (s.end() == it) {
      // element does not exist in the set, insert it
      s.insert(val); 
      it = s.find(val);
      remove_elem = true;
   }

   // find previous and next element
   auto prev_it = (it == s.begin()? s.end(): std::prev(it));
   auto next_it = std::next(it);

   // remove inserted element if applicable
   if (remove_elem)
      s.erase(it);

   unsigned int d1, d2;
   d1 = d2 = std::numeric_limits<unsigned int>::max();

   if (prev_it != s.end())
      d1 = val - *prev_it;

   if (next_it != s.end())
      d2 = *next_it - val;

   return d1 <= d2? *prev_it: *next_it;
}
#包括
无符号整数查找最近的元素(std::set s,无符号整数val){
bool remove_elem=false;
自动it=s.find(val);
//val是否存在于集合中?
如果(s.end()==它){
//元素在集合中不存在,请插入它
s、 插入(val);
它=s.find(val);
删除元素=真;
}
//查找上一个和下一个元素
auto prev_it=(it==s.begin()?s.end():std::prev(it));
自动下一步\u it=std::下一步(it);
//如果适用,拆下插入的元件
如果(删除元素)
s、 抹去(它);
无符号整数d1,d2;
d1=d2=std::数值限制::最大值();
if(prev_it!=s.end())
d1=val-*上一个;
if(next_it!=s.end())
d2=*下一个值;

返回d1
std::abs(x)
在这里没有意义:
x
具有无符号类型,并且始终为非负数。@Evg两个正数之间的差异不一定是负数postitive@formerlyknownas_463035818在C++中,两个无符号数之间的差异总是无符号的和非负的:<代码> StistaYaStRT(3U-5U> 0)
@Evg,但是删除对
std::abs的调用是错误的修复,因为
3u-5u>4u-3u
。它不是没有意义的,而是使用的wrongly@Evg不,它不是[正确的词],因为意图是明确的,如果它按预期工作,它会做正确的事情,只是它没有;)也许我不明白这个问题,但是
find
有什么错?它返回一个迭代器。然后你只需要移动一步。@ZDF,是的,成员
。find()
确实是对数的。但是它在这里怎么用呢?它查找特定的元素,与
std::find
相反,它不带谓词(这就是为什么我假设
std::find
)。我假设OP正在集合中查找元素。我的错。
std::set<unsigned int> my_set{34, 256, 268, 500, 502, 444};

std::cout << *closest_element(my_set, 26);   // Output: 34
std::cout << *closest_element(my_set, 260);  // Output: 256
std::cout << *closest_element(my_set, 620);  // Output: 502
#include <set>

unsigned int find_closest_element(std::set<unsigned int> s, unsigned int val) {
   bool remove_elem = false;
   auto it = s.find(val);

   // does val exist in the set?
   if (s.end() == it) {
      // element does not exist in the set, insert it
      s.insert(val); 
      it = s.find(val);
      remove_elem = true;
   }

   // find previous and next element
   auto prev_it = (it == s.begin()? s.end(): std::prev(it));
   auto next_it = std::next(it);

   // remove inserted element if applicable
   if (remove_elem)
      s.erase(it);

   unsigned int d1, d2;
   d1 = d2 = std::numeric_limits<unsigned int>::max();

   if (prev_it != s.end())
      d1 = val - *prev_it;

   if (next_it != s.end())
      d2 = *next_it - val;

   return d1 <= d2? *prev_it: *next_it;
}