相同向量的嵌套循环 - 擦除 - 删除成语

时间:2016-06-15 08:36:26

标签: c++ vector stl

我想迭代向量的所有元素,并为每个元素检查向量的所有其他元素的条件。

逻辑:

Precondition: q is not in vector

for every x,y in vector
    if d(x, y) <= d(x, q) && d(x, q) <= d(y, q) then
        eliminate x, y

方法:

for(vector<Point_d>::iterator it = candidates.begin(); it != candidates.end(); ++it){

    for (vector<Point_d>::iterator it2 = candidates.begin(); it2 != candidates.end(); ++it2) {

        if(dist.transformed_distance(*it, *it2) <=  dist.transformed_distance(*it, q)
                && dist.transformed_distance(*it, q) <= dist.transformed_distance(*it2, q)){

            /* Remove x,y without invalidate the iterators */
        }
    }

}

我知道如果删除循环内的元素,迭代器将失效。有没有办法用Erase-Remove Idiom做到这一点,还是有其他方法可以做到这一点?我已经搜索了很多,我找到了各种可以组合起来使其工作的部分,即通过从for循环中删除迭代器并使用erase和remove_if,但我无法弄明白,也因为我是c ++和STL的新手我想听听更好的方法。

修改

如果可能,我也不想为相同的元素d(x, x) <= d(x, q)做条件。

3 个答案:

答案 0 :(得分:2)

您描述的概念算法可以用两种不同的方式解释:

  1. 识别集合中满足某些条件 pred (x,y)的所有元素对{x,y}。确定所有这些对后,删除所有参与其中的元素。

  2. 您提供的确切命令式伪代码版本。一旦发现元素被清除,元素就会被移除。

  3. 2.与1.的差异是,当您在元素 y 配对时检测到它满足您的谓词时,删除元素 x 之后,就会消除该元素与另一个元素 z 形成令人满意的配对的可能性,并且可能会发现没有其他元素会与 z 形成如此令人满意的配对。

    第一种解释的解决方案如下:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    // Finds in the container 'c' pairs of elements {x, y} such that
    //           pred(x, y) == true
    // and removes any such elements.
    // The remaining elements may be reordered.
    // pred is assumed to be commutative, i.e.
    //        pred(x, y) == pred(y, x)
    template<class C, class Pred>
    void remove_element_pairs(C& c, Pred pred)
    {
        typedef typename C::iterator It;
        typedef typename C::value_type X;
        It e = c.end();
        for ( It it = c.begin(); it != e; )
        {
            const X& a = *it;
            const auto boundPred = [&pred, a](const X& x) -> bool { pred(a, x); };
            if ( c.end() == find_if(std::next(it), c.end(), boundPred) )
                ++it;
            else
                std::swap(*it, *--e);
        }
        c.erase(e, c.end());
    }
    
    int main()
    {
        std::vector<int> v{1, 2, 7, 0, 0, 4, 4, 5};
        remove_element_pairs(v, [](int a, int b) -> bool { return a + b == 8; });
        for(int x : v)
            std::cout << x << " ";
        return 0;
    }
    

答案 1 :(得分:2)

如果使用索引,您的工作可能会更容易,如下面的代码所示。我有一个稍微修改过的live demo,表明这有效。 (我将dist函数替换为只返回整数abs(x-y)的函数。

for(size_t i=0; i<candidates.size();){

    bool deleted = false;

    for (size_t j = 0; j < candidates.size();) {

        if(i==j) {
             ++j;
             continue;
        }

        if(dist.transformed_distance(candidates[i], candidates[j]) <=  dist.transformed_distance(candidates[i], q)
                && dist.transformed_distance(candidates[i], q) <= dist.transformed_distance(candidates[j], q)){

            bool dec_i = false;

            if(i < j) --j;
            else dec_i = true;

            candidates.erase(std::next(candidates.begin(), i));                
            candidates.erase(std::next(candidates.begin(), j));            

            if(dec_i) --i;
            deleted = true;
            break;
        }
        else 
            ++j;
    }
    if(!deleted)
        ++i;
}

请注意,您可以使用替代实现来标记要在第一次传递中删除的元素,然后删除它们。在这种情况下,行为是不同的:由于元素未被删除,它们仍被考虑用于以后的对匹配。因此,单个元素可以与多于一个的元素配对以进行删除,并且最终可能移除比上述元素更多的元素。这次的成本是O(n ^ 2)而不是O(n ^ 3)。现场演示here

std::vector<int> deleteIndices;
deleteIndices.reserve(candidates.size());

for(size_t i=0; i<candidates.size(); ++i){

    for (size_t j = 0; j < candidates.size(); ++j) {

        if(i==j) {
             continue;
        }

        if(dist.transformed_distance(candidates[i], candidates[j]) <=  dist.transformed_distance(candidates[i], q)
                && dist.transformed_distance(candidates[i], q) <= dist.transformed_distance(candidates[j], q)){
            deleteIndices.push_back(i);
            deleteIndices.push_back(j);    
        }
    }
}

std::sort(deleteIndices.begin(), deleteIndices.end());
auto unique_end = std::unique(deleteIndices.begin(), deleteIndices.end());

//I'm using this complicated thing as the template param just because I don't know what your element type is
std::vector<std::remove_reference_t<decltype(candidates[0])>> output;
output.reserve(candidates.size() - deleteIndices.size());

auto it = deleteIndices.begin();
auto i = 0;
std::copy_if(candidates.begin(), candidates.end(), std::back_inserter(output), [&it,&i,unique_end](int elem)
    { 
        if(it==unique_end) { 
            ++i; 
            return true; 
        } 
        if(i == *it) { 
            ++i; 
            ++it; 
            return false; 
        } 
        ++i; 
        return true; 
    });

答案 2 :(得分:1)

我真的不认为你能比O更好(&lt; = N ^ 2)

#include <vector>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <iterator>

struct point_d {
    double x, y;
};
std::ostream& operator<<(std::ostream& os, const point_d& p)
{
    return os << "(" << p.x << ", " << p.y << ")";
}

double distance(const point_d& l, const point_d& r)
{
    return std::sqrt(std::pow(r.x-l.x, 2) + std::pow(r.y-l.y,2));
}

using d_array = std::vector<point_d>;
//for every x,y in vector if d(x, y) <= d(x, q) && d(x, q) <= d(y, q) then eliminate x, y. q is not in vector.


d_array remove_q(const d_array& vec, point_d q)
{
    d_array result;
    std::vector<char> dropped(vec.size(), 0);    // note! avoid vector<bool>
    result.reserve(vec.size());

    auto is_dropped = [&](auto& p)
    {
        return dropped[std::distance(vec.data(), std::addressof(p))];
    };

    auto drop = [&](auto& p)
    {
        dropped[std::distance(vec.data(), std::addressof(p))] = 1;
    };

    auto should_drop = [&](auto& x) {
        for (auto& y : vec)
        {
            if (is_dropped(y))
                return true;

            if (std::addressof(x) != std::addressof(y))
            {
                if (distance(x, y) <= distance(x, q)
                    and distance(x, q) <= distance(y, q))
                {
                    return true;
                }
            }
        }
        return false;
    };


    for (auto& x : vec) {
        if (not should_drop(x))
            result.push_back(x);
        else
            drop(x);

    }

    return result;
}


int main()
{
    d_array v = {
        point_d{ 0, 0},
        point_d{ 1, 1},
        point_d{ 0.5, 0.5 },
        point_d{ 0.4, 0.4 },
        point_d{ 0.25, 0.25 }
    };

    auto v2 = remove_q(v, {0.45, 0.45});
    std::copy(v2.begin(), v2.end(), std::ostream_iterator<point_d>(std::cout, ", "));
    std::cout << std::endl;
}