C ++标准库方法,用于删除列表中满足条件的一对项目之一

时间:2011-11-10 18:40:39

标签: c++ algorithm stl iterator std

想象一下,您的std::list中包含一组值。为了示范,我们会说它只是std::list<int>,但就我而言,它们实际上是2D点。无论如何,我想删除满足某种距离标准的一对 int s(或点)中的一个。我的问题是如何将其作为迭代来处理,而不是O(N ^ 2)次操作。

示例

来源是包含以下内容的int列表:

{ 16, 2, 5, 10, 15, 1, 20 }

如果我给这个1的距离标准(即列表中的项目不应该在任何其他项目的1之内),我想产生以下输出:

{ 16, 2, 5, 10, 20 }如果我向前迭代

如果我向后迭代,

{ 20, 1, 15, 10, 5 }

我觉得必须有一些很棒的方法来做到这一点,但是我坚持使用这个迭代器的双循环并尝试在迭代列表时擦除项目。

6 个答案:

答案 0 :(得分:2)

制作“地区”地图,基本上是std::map<coordinates/len, std::vector<point>>。 将每个点添加到它的区域,以及8个相邻区域O(N*logN)中的每一个。在每个较小的列表上运行“nieve”算法(技术上O(N^2),除非是最大密度,然后它变为O(N*density))。最后:在您的原始列表中,遍历每个point,如果它已从其中放入的8个迷你列表的任何中删除,请将其从列表中删除。 O(n)

对密度没有限制,这是O(N^2),而且速度很慢。但这越快越快点越多。如果点在某个已知的边界中稍微均匀分布,则可以切换到二维数组,使显着更快,如果密度有一个恒定的限制,那么技术上使其成为O(N)算法。

这就是你按顺序对两个变量列表进行排序的方法。 grid / map / 2dvector的东西。

[编辑]你提到你也遇到了“nieve”方法的问题,所以这就是:

template<class iterator, class criterion>
iterator RemoveCriterion(iterator begin, iterator end, criterion criter) {
    iterator actend = end;
    for(iterator L=begin; L != actend; ++L) {
        iterator R(L);
        for(++R; R != actend;) {
            if (criter(*L, *R) {
                iterator N(R); 
                std::rotate(R, ++N, actend);
                --actend;
            } else
                ++R;
        }
    }
    return actend;
}

这应该适用于链表,矢量和类似容器,并且反向运行。不幸的是,由于没有考虑链表的属性,它有点慢。可以使很多更快的版本只能在特定方向的链表上工作。请注意,返回值很重要,就像其他变异算法一样。它只能改变容器的内容,而不能改变容器本身,所以你必须在返回值完成后擦除所有元素。

答案 1 :(得分:1)

Cubbi有最好的答案,尽管他出于某种原因将其删除了:

  

听起来它是一个排序列表,在这种情况下,std :: unique将完成删除每对中第二个元素的工作:

#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>
int main()
{
    std::list<int> data = {1,2,5,10,15,16,20};
    std::unique_copy(data.begin(), data.end(),
                    std::ostream_iterator<int>(std::cout, " "),
                    [](int n, int m){return abs(n-m)<=1;});
    std::cout << '\n';
}
  

演示:https://ideone.com/OnGxk

通过将int更改为其他类型或通过定义模板,可以简单地扩展到其他类型:

template<typename T> void remove_close(std::list<T> &data, int distance)
{
    std::unique_copy(data.begin(), data.end(),
                    std::ostream_iterator<int>(std::cout, " "),
                    [distance](T n, T m){return abs(n-m)<=distance;});
    return data;
}

哪种类型适用于定义operator -abs的任何类型,以便在两个对象之间找到距离。

答案 2 :(得分:1)

作为一名数学家,我非常确定没有“令人敬畏”的方法可以解决这个未排序列表的问题。在我看来,为了确定插入是否可行,检查任何一个元素对所有先前元素的标准是合乎逻辑的必要性。根据列表的大小和标准,可能有多种方法可以对其进行优化。

也许您可以根据标准维护一个bitset。例如。假设abs(n-m)<1)是标准。假设第一个元素的大小为5.这将被转移到新列表中。所以将bitset [5]翻转为1.然后,当你遇到一个大小为6的元素时,你只需要测试

!( bitset[5] | bitset[6] | bitset[7])

这将确保没有元素在结果列表的第1级内。然而,对于更复杂(非离散)的标准,这个想法可能难以扩展。

答案 3 :(得分:0)

怎么样:

struct IsNeighbour : public std::binary_function<int,int,bool>
{
    IsNeighbour(int dist)
        : distance(dist) {}
    bool operator()(int a, int b) const
        { return abs(a-b) <= distance; }
    int distance;
};

std::list<int>::iterator iter = lst.begin();
while(iter != lst.end())
{
    iter = std::adjacent_find(iter, lst.end(), IsNeighbour(some_distance)));
    if(iter != lst.end())
        iter = lst.erase(iter);
}

这应该有O(n)。它搜索第一对邻居(彼此相距最大some_distance)并删除该对中的第一对。这是重复的(从找到的项目开始,当然不是从开头开始)直到找不到对。

编辑:哦对不起,你说任何其他而不只是它的下一个元素。在这种情况下,上述算法仅适用于排序列表。所以如果需要的话,你应该先对它进行排序。

您也可以使用std::unique代替上面的自定义循环:

lst.erase(std::unique(lst.begin(), lst.end(), IsNeighbour(some_distance), lst.end());

但这会删除每个相等对的第二项,而不是第一项,因此如果这很重要,您可能必须反转迭代方向。

对于2D点而不是整数(1D点),它并不那么容易,因为你不能只按它们的欧氏距离对它们进行排序。因此,如果您真正的问题是在2D点上进行,那么您可以重新解释问题以更清楚地指出并删除过度简化的int示例。

答案 4 :(得分:0)

我认为只要您不介意制作数据副本,但是如果它只是一对整数/浮点数,那么这将是有效的,这应该是相当低的成本。你正在进行n ^ 2比较,但你正在使用std :: algorithm并且可以声明输入向量const。

//calculates the distance between two points and returns true if said distance is 
//under its threshold
bool isTooClose(const Point& lhs, const Point& rhs, int threshold = 1);     
vector<Point>& vec; //the original vector, passed in
vector<Point>& out; //the output vector, returned however you like

for(b = vec.begin(), e = vec.end(); b != e; b++) {
    Point& candidate = *b;

    if(find_if(out.begin(),
               out.end(),
               bind1st(isTooClose, candidate)) == out.end())
    {//we didn't find anyone too close to us in the output vector. Let's add!
        out.push_back(candidate);
    }
}

答案 5 :(得分:-3)

使用仿函数

std :: list&lt;&gt; .erase(remove_if(...))

http://en.wikipedia.org/wiki/Erase-remove_idiom

更新(添加代码):

struct IsNeighbour : public std::unary_function<int,bool>
{
  IsNeighbour(int dist)
    : m_distance(dist), m_old_value(0){}
  bool operator()(int a)
    { 
      bool result = abs(a-m_old_value) <= m_distance; 
      m_old_value = a; 
      return result;
    }
  int m_distance;
  int m_old_value;
};

main function...
std::list<int> data = {1,2,5,10,15,16,20};
data.erase(std::remove_if(data.begin(), data.end(),  IsNeighbour(1)), data.end());