我遇到的问题是确定两个向量是否包含两个相同的元素。元素可以在向量中的任何位置,但它们必须相邻。
已编辑更多示例
例如,比较时,以下两个向量将返回false。
向量1 = [0,1,2,3,4,6]
向量2 = [1,4,2,0,5,3]
但以下两个将返回true:
向量1 = [0,1,2,3,4,5]
向量2 = [4,2,1,5,0,3]
因为第一个向量中的1,2对应于第二个向量中的2,1。
真:
向量1 = [0,1,2,3,4,5]
向量2 = [1,4,2,0,5,3]
{5,0}是一对,尽管在矢量周围循环(我最初说这是假的,感谢你发现'来自莫斯科的'弗拉德')。
真:
向量1 = [0,1,2,3,4,5]
向量2 = [4,8,6,2,1,5,0,3]
{2,1}仍然是一对,即使他们不在同一个位置
实际应用是我有一个多边形(面),其中N个点存储在矢量中。为了确定一组多边形是否完全包围3D体积,我测试每个面以确保每个边由另一个面共享(边缘由两个相邻点定义)。
因此,Face包含指向Points的指针向量...
std::vector<Point*> points_;
并检查Face是否被包围,Face包含成员函数...
bool isSurrounded(std::vector<Face*> * neighbours)
{
int count = 0;
for(auto&& i : *neighbours) // for each potential face
if (i != this) // that is not this face
for (int j = 0; j < nPoints(); j++) // and for each point in this face
for (int k = 0; k < i->nPoints(); k++ ) // check if the neighbour has a shared point, and that the next point (backwards or forwards) is also shared
if ( ( this->at(j) == i->at(k) ) // Points are the same, check the next and previous point too to make a pair
&& ( ( this->at((j+1)%nPoints()) == i->at((k+1)%(i->nPoints())) )
|| ( this->at((j+1)%nPoints()) == i->at((k+i->nPoints()-1)%(i->nPoints())) )))
{ count++; }
if (count > nPoints() - 1) // number of egdes = nPoints -1
return true;
else
return false;
}
现在,显然这段代码太可怕了。如果我在两周内回到这里,我可能会理解它。所以面对原来的问题,你怎么能整齐地检查这两个向量?
请注意,如果您尝试破译提供的代码。 at(int)
会返回面部中的点,nPoints()
会返回面部中的点数。
非常感谢。
答案 0 :(得分:1)
效率不高但可能会有以下情况。
bool comparePair ( pair<int,int> p1, pair<int,int> p2 ) {
return ( p1.first == p2.first && p1.second == p2.second )
|| ( p1.second == p2.first && p1.first == p2.second );
}
//....
vector< pair<int,int> > s1;
vector< pair<int,int> > s1;
vector< pair<int,int> > intersect( vec1.size() + vec2.size() );
for ( int i = 0; i < vec1.size()-1; i++ ) {
pair<int, int> newPair;
newPair.first = vec1[i];
newPair.first = vec1[i+1];
s1.push_back( newPair );
}
for ( int i = 0; i < vec2.size()-1; i++ ) {
pair<int, int> newPair;
newPair.first = vec2[i];
newPair.first = vec2[i+1];
s2.push_back( newPair );
}
auto it = std::set_intersection ( s1.begin(), s1.end(), s2.begin(), s2.end(),
intersect.begin(), comparePair );
return ( it != intersect.begin() ); // not sure about this.
答案 1 :(得分:1)
如果我理解你的问题:
std::vector<int> a, b;
std::vector<int>::iterator itB = b.begin();
std::vector<int>::iterator itA;
std::vector<std::vector<int>::iterator> nears;
std::vector<int>::iterator near;
for(;itB!=b.end() ; ++itB) {
itA = std::find(a.begin(), a.end(), *itB);
if(nears.empty()) {
nears.push_back(itA);
} else {
/* there's already one it, check the second */
if(*(++nears[0])==*itA && itA != a.end() {
nears.push_back(itA);
} else {
nears.clear();
itB--;
}
}
if(nears.size() == 2) {
return true;
}
}
return false;
答案 2 :(得分:1)
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;
class AdjacentSort
{
public:
AdjacentSort(const vector<int>& ref);
~AdjacentSort();
bool operator()(int e1,int e2) const;
private:
const vector<int>& ref_;
};
AdjacentSort::AdjacentSort(const vector<int>& ref):
ref_(ref)
{
}
bool AdjacentSort::operator()(int e1, int e2) const
{
auto it1 = find(ref_.begin(),ref_.end(),e1);
auto it2 = find(ref_.begin(),ref_.end(),e2);
return distance(it1,it2) == 1;
}
AdjacentSort::~AdjacentSort()
{
}
int main()
{
vector<int> vec {1,2,3,4,5};
vector<int> vec2 {1,3,5,4,2};
AdjacentSort func(vec);
auto it = adjacent_find(vec2.begin(),vec2.end(),func);
cout << *it << endl;
return 0;
}
它返回找到两个相邻数字的第一个元素,否则返回结束迭代器。
答案 3 :(得分:1)
如果您的元素是相同的元素集,则为每个元素分配索引。 (没有提到伪案中的角落案例): -
for(int i=0;i<vect1.size;i++) {
adj[vect1[i]][0] = vect1[i-1];
adj[vect2[i]][1] = vect2[i+1];
}
for(int j=0;j<vect2.size();j++) {
if(arr[vect2[i]][0]==(vect2[j-1] or vect[j+1]))
return true
if(arr[vect2[i]][1]==(vect2[j-1] or vect[j+1]))
return true
}
答案 4 :(得分:1)
如果我已正确理解这两个载体
std::vector<int> v1 = { 0, 1, 2, 3, 4, 5 };
std::vector<int> v2 = { 3, 5, 2, 1, 4, 0 };
包含相邻的相等元素。它们在第一个向量中是{1,2},在第二个向量中是{2,1},尽管这些对的位置在向量中是不同的。
事实上,您已经命名了可以在此任务中使用的相应标准算法。它是std :: adjacent_find。例如
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> v1 = { 0, 1, 2, 3, 4, 5 };
std::vector<int> v2 = { 3, 5, 2, 1, 4, 0 };
bool result =
std::adjacent_find( v1.begin(), v1.end(),
[&v2]( int x1, int y1 )
{
return std::adjacent_find( v2.begin(), v2.end(),
[=]( int x2, int y2 )
{
return ( x1 == x2 && y1 == y2 || x1 == y2 && y1 == x2 );
} ) != v2.end();
} ) != v1.end();
std::cout << "result = " << std::boolalpha << result << std::endl;
return 0;
}
答案 5 :(得分:1)
这是我对这个问题的尝试。很简单,遍历a
,在b
中找到相同的元素,然后将a
中的下一个元素与我们在b
中的位置之前和之后的元素进行比较。
如果它比它需要的有点晦涩,那么可以用任何容器调用这个函数。唯一的要求是容器的迭代器必须是双向的。
#include <vector>
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
template <class Iter>
pair<Iter, Iter> get_neighbors(Iter begin, Iter current, Iter end)
{
auto p = make_pair(end, next(current));
if(current != begin)
p.first = prev(current);
return p;
}
template <class Iter1, class Iter2>
bool compare_if_valid(Iter1 p1, Iter1 end1, Iter2 p2)
{
return p1 != end1 && *p1 == *p2;
}
template <class C1, class C2>
auto neighbors_match(const C1 & a, const C2 & b) ->
decltype(make_pair(begin(a), begin(b)))
{
for(auto i = begin(a); i != end(a) && next(i) != end(a); ++i)
{
auto pos_in_b = find(begin(b), end(b), *i);
if(pos_in_b != end(b))
{
auto b_neighbors = get_neighbors(begin(b), pos_in_b, end(b));
if(compare_if_valid(b_neighbors.first, end(b), next(i)))
return {i, b_neighbors.first};
else if(compare_if_valid(b_neighbors.second, end(b), next(i)))
return {i, pos_in_b};
}
}
return {end(a), end(b)};
}
int main()
{
vector<int> a = {0, 1, 2, 3, 4, 5};
vector<int> b = {1, 4, 2, 0, 5, 3};
cout << boolalpha << (neighbors_match(a, b).first != a.end()) << endl;
vector<int> a2 = {0, 1, 2, 3, 4, 5};
list<int> b2 = {4, 2, 1, 5, 0, 3};
auto match = neighbors_match(a2, b2);
cout << boolalpha << distance(a2.cbegin(), match.first)
<< ' ' << distance(b2.cbegin(), match.second) << endl;
return 0;
}
答案 6 :(得分:1)
你基本上要求的是两个面的边集(我们称之为a
和b
)是否不相交。这可以分解为b
中的任何边是否在a
中的问题,这只是成员资格测试。那么问题是,向量在成员资格测试中不是很好。
我的解决方案是将其中一个向量转换为unordered_set< pair<int, int> >
。
unordered_set
只是一个哈希表,对表示边缘。
在表示边缘时,我已经去了一个规范化方案,其中顶点的索引按顺序递增(因此[2,1]
和[1,2]
都存储为[1,2]
在我的边缘组)。这使得相等测试更容易一些(因为它只是对的相等)
所以这是我的解决方案:
#include <iostream>
#include <utility>
#include <functional>
#include <vector>
#include <unordered_set>
using namespace std;
using uint = unsigned int;
using pii = pair<int,int>;
// Simple hashing for pairs of integers
struct pii_hash {
inline size_t
operator()(const pii & p) const
{
return p.first ^ p.second;
}
};
// Order pairs of integers so the smallest number is first
pii ord_pii(int x, int y) { return x < y ? pii(x, y) : pii(y, x); }
bool
shares_edge(vector<int> a, vector<int> b)
{
unordered_set<pii, pii_hash> edge_set {};
// Create unordered set of pairs (the Edge Set)
for(uint i = 0; i < a.size() - 1; ++i)
edge_set.emplace( ord_pii(a[i], a[i+1]) );
// Check if any edges in B are in the Edge Set of A
for(uint i = 0; i < b.size() - i; ++i)
{
pii edge( ord_pii(b[i], b[i+1]) );
if( edge_set.find(edge) != edge_set.end() )
return true;
}
return false;
}
int main() {
vector<int>
a {0, 1, 2, 3, 4, 5},
b {1, 4, 2, 0, 5, 3},
c {4, 2, 1, 0, 5, 3};
shares_edge(a, b); // false
shares_edge(a, c); // true
return 0;
}
在您的特定情况下,您可能希望shares_edge
成为Face
课程的成员函数。预先计算边集并将其存储为Face
的实例变量也可能是有益的,但这取决于边数据变化的频率与计算发生的频率。
编辑额外解决方案
编辑2 已修复问题更改:边缘设置现在包围点列表。
如果您将初始化时预先计算的边集添加到某种Face
类,则会出现这种情况。私有嵌套Edge
类可以被认为是装饰边缘的当前表示(即点列表中的两个相邻位置),具有实际类,因此集合之类的集合可以将索引处理为点列表作为一个实际的边缘:
#include <cassert>
#include <iostream>
#include <utility>
#include <functional>
#include <vector>
#include <unordered_set>
using uint = unsigned int;
class Face {
struct Edge {
int _index;
const std::vector<int> *_vertList;
Edge(int index, const std::vector<int> *vertList)
: _index {index}
, _vertList {vertList}
{};
bool
operator==(const Edge & other) const
{
return
( elem() == other.elem() && next() == other.next() ) ||
( elem() == other.next() && next() == other.elem() );
}
struct hash {
inline size_t
operator()(const Edge & e) const
{
return e.elem() ^ e.next();
}
};
private:
inline int elem() const { return _vertList->at(_index); }
inline int
next() const
{
return _vertList->at( (_index + 1) % _vertList->size() );
}
};
std::vector<int> _vertList;
std::unordered_set<Edge, Edge::hash> _edgeSet;
public:
Face(std::initializer_list<int> verts)
: _vertList {verts}
, _edgeSet {}
{
for(uint i = 0; i < _vertList.size(); ++i)
_edgeSet.emplace( Edge(i, &_vertList) );
}
bool
shares_edge(const Face & that) const
{
for(const Edge & e : that._edgeSet)
if( _edgeSet.find(e) != _edgeSet.end() )
return true;
return false;
}
};
int main() {
Face
a {0, 1, 2, 3, 4, 5},
b {1, 4, 2, 0, 5, 3},
c {4, 2, 1, 0, 5, 3},
d {0, 1, 2, 3, 4, 6},
e {4, 8, 6, 2, 1, 5, 0, 3};
assert( !d.shares_edge(b) );
assert( a.shares_edge(b) );
assert( a.shares_edge(c) );
assert( a.shares_edge(e) );
return 0;
}
正如您所看到的,这个添加的抽象使得shares_edge()
的实现非常令人满意,但这是因为真正的技巧在于Edge
类的定义(或者更具体的是e1 == e2 <=> Edge::hash(e1) == Edge::hash(e2)
)的关系。
答案 7 :(得分:1)
首先,编写一个make_paired_range_view
,它取一个范围并返回迭代器返回std::tie( *it, *std::next(it) )
的范围。 boost
可以在这里提供帮助,因为它们的迭代器编写代码使得它更不那么烦人了。
接下来,unordered_equal
需要两个pair
并比较它们忽略顺序(所以如果第一个相等且第二个都相等,或者如果第一个等于另一个,则相反,反之亦然)。
现在我们使用unordered_equal
在右侧查找每个左手边的对。
这样做的好处是可以节省0个额外内存,但缺点是O(n^2
)时间。
如果我们更关心时间而不是记忆,我们可以在{em>排序 pair
之后将unordered_set
上面的pair
推到unordered_set
规范秩序。然后我们通过第二个容器,测试每对(排序后)以查看它是否在O(n)
中。这需要O(n)
额外的内存,但会在int
时间内运行。它也可以在没有花哨的dancy矢量和范围写入的情况下完成。
如果元素比pseudo_pair
更昂贵,您可以编写一个包含指针的自定义{{1}},其哈希和相等性基于指针的内容。
答案 8 :(得分:1)
一个有趣的“你会怎么做......”问题...... :-)让我从表格上的编辑框和组合框中取出15分钟的休息时间并进行一些编程以进行更改。 ..大声笑
所以,这就是我认为我会这样做的方式......
首先,我将边缘的概念定义为一对值(一对整数 - 遵循原始示例)。我意识到你的例子只是一个简化而你实际上是使用你自己的类的向量(Point *而不是int?)但是模板化这个代码并使用你想要的任何类型应该是微不足道的......
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <set>
#include <vector>
using namespace std;
typedef pair<int, int> edge;
然后我将创建一个集合类,它将按照我们需要的方式保持其元素(边缘)的顺序(通过以顺序不敏感的方式比较边缘 - 即如果e1.first == e2.first和e1.second == e2.second然后边e1和e2是相同的,但如果e1.first == e2.second和e1.second == e2.first,它们也是相同的。为此,我们可以创建一个功能:
struct order_insensitive_pair_less
{
bool operator() (const edge& e1, const edge& e2) const
{
if(min(e1.first,e1.second)<min(e2.first,e2.second)) return true;
else if(min(e1.first,e1.second)>min(e2.first,e2.second)) return false;
else return(max(e1.first,e1.second)<max(e2.first,e2.second));
}
};
最后,我们的助手类(称之为edge_set)将是使用上述函数排序的集合的简单派生,并添加了几个便利方法 - 从向量(或实践中的Face类)填充集合的构造函数)和测试器函数(bool shares_edge(const vector&amp; v))告诉我们该集合是否与另一个集合共享边缘。所以:
struct edge_set : public set<edge, order_insensitive_pair_less>
{
edge_set(const vector<int>&v);
bool shares_edge(const vector<int>&v);
};
实施为:
edge_set::edge_set(const std::vector<int>&v) : set<edge, order_insensitive_pair_less>()
{
if(v.size()<2) return; // assume there must be at least 2 elements in the vector since it is supposed to be a list of edges...
for (std::vector<int>::const_iterator it = v.begin()+1; it != v.end(); it++)
insert(edge(*(it-1), *it));
}
bool edge_set::shares_edge(const std::vector<int>& v)
{
edge_set es(v);
for(iterator es_it = begin(); es_it != end(); es_it++)
if(es.count(*es_it))
return true;
return false;
}
然后使用变得微不足道(并且相当优雅)。假设你在变量v1和v2中的问题摘要中给出了两个向量作为例子来测试它们是否共享边缘,你只需要编写:
if(edge_set(v1).shares_edge(v2))
// Yup, they share an edge, do something about it...
else
// Nope, not these two... Do something different...
关于这种方法中元素数量的唯一假设是每个向量至少有2个(因为你不能有一个“边缘”,至少没有顶点)。然而,即使不是这种情况(其中一个向量是空的或只有一个元素) - 这将导致一个空的edge_set,所以你只会得到一个他们没有共享边的答案(因为其中一个集合)是空的)。没什么大不了的......在我看来,这样做肯定会通过“两周测试”,因为你会有一个专门的课程,你可以有几条评论线来说明它在做什么,实际的比较是相当的可读(edge_set(v1).shares_edge(v2))...
答案 9 :(得分:1)
我认为这是我能提出的最简洁。
bool check_for_pairs(std::vector<int> A, std::vector<int> B) {
auto lastA = A.back();
for (auto a : A) {
auto lastB = B.back();
for (auto b : B) {
if ((b == a && lastB == lastA) || (b == lastA && lastB == a)) return true;
lastB = b;
}
lastA = a;
}
return false;
}
更有时间效果的方法是使用集合
bool check_for_pairs2(std::vector<int> A, std::vector<int> B) {
using pair = std::pair<int,int>;
std::unordered_set< pair, boost::hash<pair> > lookup;
auto last = A.back();
for (auto a : A) {
lookup.insert(a < last ? std::make_pair(a,last) : std::make_pair(last,a));
last = a;
}
last = B.back();
for (auto b : B) {
if (lookup.count(b < last ? std::make_pair(b,last) : std::make_pair(last,b)))
return true;
last = b;
}
return false;
}
如果实现散列函数哈希(a,b)和(b,a)相同,则可以删除检查哪个值最小
答案 10 :(得分:0)
我知道我有点迟到了,但这是我的看法:
不在原地:
#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
template<typename Pair>
class pair_generator {
public:
explicit pair_generator(std::vector<Pair>& cont)
: cont_(cont)
{ }
template<typename T>
bool operator()(T l, T r) {
cont_.emplace_back(r, l);
return true;
}
private:
std::vector<Pair>& cont_;
};
template<typename Pair>
struct position_independant_compare {
explicit position_independant_compare(const Pair& pair)
: pair_(pair)
{ }
bool operator()(const Pair & p) const {
return (p.first == pair_.first && p.second == pair_.second) || (p.first == pair_.second && p.second == pair_.first);
}
private:
const Pair& pair_;
};
template<typename T>
using pair_of = std::pair<T, T>;
template<typename T>
std::ostream & operator <<(std::ostream & stream, const pair_of<T>& pair) {
return stream << '[' << pair.first << ", " << pair.second << ']';
}
int main() {
std::vector<int>
v1 {0 ,1, 2, 3, 4, 5},
v2 {4, 8, 6, 2, 1, 5, 0, 3};
std::vector<pair_of<int> >
p1 { },
p2 { };
// generate our pairs
std::sort(v1.begin(), v1.end(), pair_generator<pair_of<int>>{ p1 });
std::sort(v2.begin(), v2.end(), pair_generator<pair_of<int>>{ p2 });
// account for the fact that the first and last element are a pair too
p1.emplace_back(p1.front().first, p1.back().second);
p2.emplace_back(p2.front().first, p2.back().second);
std::cout << "pairs for vector 1" << std::endl;
for(const auto & p : p1) { std::cout << p << std::endl; }
std::cout << std::endl << "pairs for vector 2" << std::endl;
for(const auto & p : p2) { std::cout << p << std::endl; }
std::cout << std::endl << "pairs shared between vector 1 and vector 2" << std::endl;
for(const auto & p : p1) {
const auto pos = std::find_if(p2.begin(), p2.end(), position_independant_compare<pair_of<int>>{ p });
if(pos != p2.end()) {
std::cout << p << std::endl;
}
}
}
上的示例输出
在原位:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <tuple>
#include <vector>
template<typename T>
struct in_situ_pair
: std::iterator<std::forward_iterator_tag, T> {
using pair = std::pair<T, T>;
in_situ_pair(std::vector<T>& cont, std::size_t idx)
: cont_(cont), index_{ idx }
{ }
pair operator*() const {
return { cont_[index_], cont_[(index_ + 1) % cont_.size()] };
}
in_situ_pair& operator++() {
++index_;
return *this;
}
bool operator==(const pair& r) const {
const pair l = operator*();
return (l.first == r.first && l.second == r.second)
|| (l.first == r.second && l.second == r.first);
}
bool operator==(const in_situ_pair& o) const {
return (index_ == o.index_);
}
bool operator!=(const in_situ_pair& o) const {
return !(*this == o);
}
public:
friend bool operator==(const pair& l, const in_situ_pair& r) {
return (r == l);
}
private:
std::vector<T>& cont_;
std::size_t index_;
};
template<typename T>
using pair_of = std::pair<T, T>;
template<typename T>
std::ostream & operator <<(std::ostream & stream, const pair_of<T>& pair) {
return stream << '[' << pair.first << ", " << pair.second << ']';
}
namespace in_situ {
template<typename T>
in_situ_pair<T> begin(std::vector<T>& cont) { return { cont, 0 }; }
template<typename T>
in_situ_pair<T> end(std::vector<T>& cont) { return { cont, cont.size() }; }
template<typename T>
in_situ_pair<T> at(std::vector<T>& cont, std::size_t i) { return { cont, i }; }
}
int main() {
std::vector<int>
v1 {0 ,1, 2, 3, 4, 5},
v2 {4, 8, 6, 2, 1, 5, 0, 3};
for(std::size_t i = 0; i < v1.size(); ++i) {
auto pos = std::find(in_situ::begin(v2), in_situ::end(v2), in_situ::at(v1, i));
if(pos != in_situ::end(v2)) {
std::cout << "common: " << *pos << std::endl;
}
}
}
上的示例输出
答案 11 :(得分:0)
有很多很棒的答案,我相信人们在寻找两个向量中寻找相邻元素对的一般问题时会发现它们很有启发性。我已经决定回答我自己的问题,因为我觉得我原来尝试的最简单版本是对我来说最好的答案。
由于似乎没有std算法的组合使方法更简单,我相信循环和查询每个元素是最简洁和可理解的。
以下是一般情况的算法:
std::vector<int> vec1 = { 1, 2, 3, 4, 5, 6 };
std::vector<int> vec2 = { 3, 1, 4, 2, 6, 5 };
// Loop over the elements in the first vector, looking for an equal element in the 2nd vector
for(int i = 0; i < vec1.size(); i++) for(int j = 0; j < vec2.size(); j++)
if ( vec1[i] == vec2[j] &&
// ... Found equal elements, now check if the next element matches the next or previous element in the other vector
( vec1[(i+1) % vec1.size()] == vec2[(j+1) % vec2.size()]
||vec1[(i+1) % vec1.size()] == vec2[(j-1+vec2.size()) % vec2.size()] ) )
return true;
return false;
或者在我的特定情况下,我实际上在检查向量的向量,以及元素不再是int的位置,而是指向类的指针。
(Face类的operator []返回属于face的向量元素。
bool isSurrounded(std::vector<Face*> * neighbours)
{
// We can check if each edge aligns with an edge in a nearby face,
// ... if each edge aligns, then the face is surrounded
// ... an edge is defined by two adjacent points in the points_ vector
// ... so we check for two consecutive points to be equal...
int count = 0;
// for each potential face that is not this face
for(auto&& i : *neighbours) if (i != this)
// ... loop over both vectors looking for an equal point
for (int j = 0; j < nPoints(); j++) for (int k = 0; k < i->nPoints(); k++ )
if ( (*this)[j] == (*i)[k] &&
// ... equal points have been found, check if the next or previous points also match
( (*this)[(j+1) % nPoints()] == (*i)[(k+1) % i->nPoints()]
|| (*this)[(j+1) % nPoints()] == (*i)[(k-1+i->nPoints()) % i->nPoints()] ) )
// ... an edge is shared
{ count++; }
// number of egdes = nPoints -1
if (count > nPoints() - 1)
return true;
else
return false;
}