想象一下,您的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 }
我觉得必须有一些很棒的方法来做到这一点,但是我坚持使用这个迭代器的双循环并尝试在迭代列表时擦除项目。
答案 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';
}
通过将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());