代码之家  ›  专栏  ›  技术社区  ›  Dave Van den Eynde

在向量中查找最近的点

  •  8
  • Dave Van den Eynde  · 技术社区  · 16 年前

    给定一个具有多个值的排序向量,如下例所示:

    std::vector<double> f;
    f.pushback(10);
    f.pushback(100);
    f.pushback(1000);
    f.pushback(10000);
    

    我正在寻找一种最优雅的方法来检索任何双D值,这两个值直接与之相邻。例如,给定值“45”,我希望返回“10”和“100”。

    我在看下界和上界,但他们不做我想做的。你能帮忙吗?

    编辑:我已经决定发表我自己的答案,因为它有点像是我在这篇文章中得到的所有有用答案的组合。我对那些我认为最有用的答案投了赞成票。

    谢谢大家,

    戴夫

    10 回复  |  直到 12 年前
        1
  •  8
  •   Harper Shelby damiankolasa    16 年前

    您可以在一个调用中同时获取两个值(如果它们存在),使用equal_range()。它返回一对std::迭代器,第一个是第一个位置,第二个是最后一个位置,您可以在其中插入传递的值,而不会违反顺序。为了严格满足您的条件,在验证迭代器不等于vector的begin()之后,您必须首先递减迭代器。

        2
  •  8
  •   Martin    16 年前

    你可以用STL的下界在几行代码中得到你想要的。下限使用引擎盖下的二进制搜索,因此运行时是O(log n)。

    double val = 45;
    double lower, upper;
    std::vector<double>::iterator it;
    it = lower_bound(f.begin(), f.end(), val);
    if (it == f.begin()) upper = *it; // no smaller value  than val in vector
    else if (it == f.end()) lower = *(it-1); // no bigger value than val in vector
    else {
        lower = *(it-1);
        upper = *it;
    }
    
        3
  •  6
  •   Wookai    16 年前

    您可以简单地使用 binary search ,将在o(log(n))中运行。

    这里有一个LUA代码段(我没有时间在C++中做它,抱歉)它做你想要的,除了限制条件(无论如何你没有定义):

    function search(value, list, first, last)
        if not first then first = 1; last = #list end
    
        if last - first < 2 then
            return list[first], list[last]
        end
    
        local median = math.ceil(first + (last - first)/2)
    
        if list[median] > value then
            return search(value, list, first, median)
        else
            return search(value, list, median, last)
        end
    end
    
    local list = {1,10,100,1000}
    
    print(search(arg[1] + 0, list))
    

    它从命令行中获取要搜索的值:

    $ lua search.lua 10 # didn't know what to do in this case
    10  100
    $ lua search.lua 101
    100 1000
    $ lua search.lua 99
    10  100
    
        4
  •  4
  •   Dave Van den Eynde    16 年前

    我将发表我自己的答案,投票给任何帮助我达成这个结论的人,因为这是我最终要用的,你们都帮助我得出这个结论。欢迎评论。

    std::pair<value_type, value_type> GetDivisions(const value_type& from) const
    {
        if (m_divisions.empty())
            throw 0; // Can't help you if we're empty.
    
        std::vector<value_type>::const_iterator it = 
            std::lower_bound(m_divisions.begin(), m_divisions.end(), from);
    
        if (it == m_divisions.end())
            return std::make_pair(m_divisions.back(), m_divisions.back());
        else if (it == m_divisions.begin())
            return std::make_pair(m_divisions.front(), m_divisions.front());
        else
            return std::make_pair(*(it - 1), *(it));
    }
    
        5
  •  1
  •   tunnuz    16 年前

    如果(在您的情况下)d小于第一个元素或大于最后一个元素呢?如何处理负值?顺便说一下:保证你的“d”在向量的第一个值和最后一个值之间,你可以这样做:

    // Your initializations
    std::vector<double>::const_iterator sit = f.begin();
    double upper, lower; 
    

    剩下的是:

    while ( *sit < d )         // if the element is still less than your d
        ++sit;                 // increase your iterator
    
    upper = *sit;              // here you get the upper value
    lower = *(--sit);          // and here your lower
    

    足够优雅?:

        6
  •  0
  •   Elie    16 年前

    你可以在向量中搜索你的值(如果它在向量中的话,它会告诉你值在哪里),然后返回该位置之前和之后的值。因此,搜索45将告诉您它应该在index=1,然后返回0和1(根据搜索的实现,您将得到较小值的索引或较大值的索引,但这很容易用几个边界条件检查)。这应该能够在o(log n)中运行,其中n是向量中的元素数。

        7
  •  0
  •   Edouard A.    16 年前

    我会写一些这样的东西,没有测试它是否编译,但是你会得到这样的想法:

    template <typename Iterator>
    std::pair<Iterator, Iterator> find_best_pair(Iterator first, Iterator last, const typename Iterator::value_type & val)
    {
        std::pair<Iterator, Iterator> result(last, last);
    
        typename Iterator::difference_type size = std::distance(first, last);
    
        if (size == 2)
        {
            // if the container is of size 2, the answer is the two elements 
            result.first = first;
            result.first = first;
            ++result.first;
        }
        else
        {
    
            // must be of at lease size 3
            if (size > 2)
            {
                Iterator second = first;
                ++second;
                Iterator prev_last = last;
                --prev_last;
                Iterator it(std::lower_bound(second, last, val));
    
                if (it != last)
                {
    
                    result.first = it;
                    result.second = it;
    
    
                    if (it != prev_last)
                    {
                        // if this is not the previous last
                        // then the answer is (it, it + 1)
                        ++result.second;
                    }
                    else
                    {
                        // if this the previous last
                        // then the answer is (it - 1, it)
                        --result.first;
                    }
    
                }
    
            }
    
        }
    
        return result;
    
    
    }
    
        8
  •  0
  •   Harper Shelby damiankolasa    16 年前

    我写下了这个小函数,它似乎更适合您想要的一般情况。我还没有完全测试过它,但是我写了一些测试代码(包括)。

    #include <algorithm>
    #include <iostream>
    #include <vector>
    
    template <class RandomAccessIt, class Container, class T>
    std::pair<RandomAccessIt, RandomAccessIt> bracket_range(RandomAccessIt begin, RandomAccessIt end, Container& c, T val)
    {
        typename Container::iterator first;
        typename Container::iterator second;
    
        first = std::find(begin, end, val);
        //Find the first value after this by iteration
        second = first;
        if (first == begin){ // Found the first element, so set this to end to indicate no lower values
        first = end;
        }
        else if (first != end && first != begin) --first; //Set this to the first value before the found one, if the value was found
        while (second != end && *second == val) ++second;
        return std::make_pair(first,second);
    }
    
    int main(int argc, _TCHAR* argv[])
    {
        std::vector<int> values;
        std::pair<std::vector<int>::iterator, std::vector<int>::iterator> vals;
    
        for (int i = 1; i < 9; ++i) values.push_back(i);
    
        for (int i = 0; i < 10; ++i){
            vals = bracket_range(values.begin(), values.end(),values, i);
            if (vals.first == values.end() && vals.second == values.end()){ // Not found at all
                std::cout << i << " is not in the container." << std::endl;
            }
            else if (vals.first == values.end()){ // No value lower
                std::cout << i << ": " << "None Lower," << *(vals.second) << std::endl;
            }
            else if (vals.second == values.end()) { // No value higher
                std::cout << i << ": " << *(vals.first) << ", None Higher" << std::endl;
            }
            else{
            std::cout << i << ": " << *(vals.first) << "," << *(vals.second) << std::endl;
            }
        }
        return 0;
    }
    
        9
  •  0
  •   Dave Van den Eynde    12 年前

    根据tunnuz发布的代码,在绑定检查方面有一些改进:

    template<typename T>
    void find_enclosing_values(const std::vector<T> &vec, const T &value, T &lower, T &upper, const T &invalid_value)
    {
        std::vector<T>::const_iterator it = vec.begin();
    
        while (it != vec.end() && *it < value)
            ++it;
    
        if(it != vec.end())
            upper = *it;
        else
            upper = invalid_value;
    
        if(it == vec.begin())
            lower = invalid_value;
        else
            lower = *(--it);
    
    }
    

    使用示例:

    std::vector<int> v;
    
    v.push_back(3);
    v.push_back(7);
    v.push_back(10);
    
    int lower, upper;
    
    find_enclosing_values(v, 4, lower, upper, -1);
    
    std::cout<<"lower "<<lower<<" upper "<<upper<<std::endl;
    
        10
  •  -1
  •   rmeador    16 年前

    如果您能够使用其他数据结构(而不是向量),我建议 B-tree . 如果您的数据是不变的,我相信您可以在恒定时间(最坏的是对数时间)中检索结果。