簡體   English   中英

獲取與 std::set 中給定元素最近的元素

[英]Get the closest element to a given element in an std::set

我有一組(排序的) unsigned int 我需要找到最接近給定數字的元素。

我正在尋找使用標准庫的解決方案,我的第一個解決方案是使用二進制搜索,但 STL 的實現僅在元素存在時才返回。 這篇文章Find Closest Element in a Set很有幫助,我實現了一個基於std::lower_bound方法的解決方案,

(*假設集合有超過 2 個元素,則不進行空/邊界檢查):

#include <iostream>
#include<set>
#include<algorithm>
#include<cmath>

int main()
{
    std::set<unsigned int> mySet = {34, 256, 268, 500, 502, 444};
    unsigned int searchedElement = 260;
    unsigned int closestElement;

    auto lower_bound = mySet.lower_bound(searchedElement);
    if (lower_bound == mySet.end()){
        closestElement = *(--lower_bound);
    }

    std::set<unsigned int>::iterator prevElement = --lower_bound;
    bool isPrevClosest = std::abs(*prevElement - searchedElement) > std::abs(*lower_bound - searchedElement);
    closestElement = isPrevClosest ? *prevElement : *lower_bound;

    std::cout << closestElement << std::endl;

    return 0;
}

有沒有更簡單更標准的解決方案?

我認為沒有比使用.lower_bound更好的解決方案了。 您可以將算法包裝到 function 模板中:

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

此 function 正確處理所有極端情況(空集、一個元素、第一個元素、最后一個元素)。

例子:

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

請注意,您的代碼中的std::abs (幾乎)什么都不做:它的參數具有無符號類型並且始終為非負數。 但是我們知道std::set元素是有序的,因此我們知道*prev_it <= value <= *it ,並且不需要std::abs()

您可以使用 std::min_element(): 作為比較器,給它一個 lambda 返回絕對差異,例如

std::min_element(mySet.begin(), mySet.end(), [searchedElement](const unsigned int a, const unsigned int b) {
  return std::abs(searchedElement - a) < std::abs(searchedElement - b);
});

但是,我確實認為這將不再適用二進制搜索......

編輯:此外,如下面的評論中所述,當x < y時,無符號整數值的std::abs(x - y)可能會返回意外大的 integer 。

std::set容器適用於查找相鄰元素,即查找在給定元素之后或之前的元素。 考慮到您面臨的問題:

我正在尋找使用標准庫的解決方案,我的第一個解決方案是使用二進制搜索,但 STL 的實現僅在元素存在時才返回

仍然有一種方法可以在不改變邏輯的情況下遵循:如果集合中不存在您想要找到的最接近的元素,那么您只需將其插入集合中(它需要對數時間的大小放)。 接下來,您將找到與剛剛添加的元素最近的元素。 最后,完成后將其從集合中移除,以使集合保持與之前相同。

當然,如果元素已經在集合中,則無需向集合中插入或移除任何內容。 因此,您需要跟蹤是否添加了該元素。


下面的 function 是上面闡述的思想的一個例子:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM