哪种数据结构是在一个向量中搜索和计数对象对的最佳数据结构

Which is best data structure for searching and counting of pairs of objects in vector of vectors

本文关键字:数据结构 搜索 最佳 对象 一个 向量      更新时间:2023-10-16

我已经定义了class elementclass node

类元素

class element
{
int id;
std::vector<node> m_nodes;   // An element consist of 4 nodes.
public:
getnode(int)    // return n-th node;       
}

类节点

class node
{
int id;
// other members
}

class model由整个nodeelement对象组成。class element对象由四个node对象的向量组成。一对两个连续(相邻)的node对象称为面。

示例:
elem1:{1,2,3,4}
elem2:{3,5,6,4}
elem1和elem2是两个element对象,数组中的整数表示四个节点对象的id
1-2、2-3、3-4和4-1是elem1的面。3-5、5-6、6-4和4-3是elem2的面面3-4和面4-3是相同的,因此由两个元件共享

边界元素是由至少一个面组成的元素,其他元素不共享该面。在上述示例中,elem1和elem2都是边界元素。边界元素的矢量也在模型类中定义。

类模型

class model    
{    
std::vector<node> m_nodes;
std::vector<element>m_elements;
std::vector<element>m_boundary;
public:
void set_boundary_elements();
}

问题:如何初始化边界元素的向量
这是set_boundary_elements()函数的伪代码。

void model::set_boundary_elements()
{
std::vector <std::pair<std::set<int> , int >> faces;
std::set<int> s;
for(auto iter::m_elements)
{
//initialise  face.
for(int i=1; i<5; ++i)
{
if(i != 4)
{
s.insert(iter.getnode(i));
s.insert(iter.getnode(i+1));
}
else 
{
s.insert(iter.getnode(4));s.insert(iter.getnode(1));   
}
for(auto it: faces)
{
if(s== it.first)
(it.second)++; break;
}
faces.push_back(s,1);
}
//then push_back the elements which have nonshared faces, into m_boundary.
}
}             

我认为我的算法效率很低,因为每次添加人脸时,我都必须迭代所有人脸。stl/算法中有什么有用的方法可以有效地解决我的问题吗?

重新思考您的设计。目前,元素并没有真正共享节点。应该共享一个节点的两个元素,每个元素都存储一组不同的数据,而这些数据恰好具有相同的id。这意味着,如果某个节点发生了变化,系统可能会不一致。

这是我的建议(没有构造函数、getter、setter等,假设你可以很容易地填写):

class Model {
class Node;
class Element;
private:
vector<Node> nodes;
vector<Element> elements;
}
class Model::Element {
private:
Element(); //only to be created by Model
vector<unsigned int> incident_nodes;
}
class Model::Node {
private:
Node(); //only to be created by Model
vector<unsigned int> incident_elements;
}

请注意,Node和Element都存储偶发事件项,它们使用整数进行存储,这些整数在Model中的向量中引用其id。模型负责创建和修改节点和元素,这些方法将负责保持数据的一致性。Element或Node上的所有公共方法都是常量。

这为您留下了一个稳定的系统,它在两个方向上都有引用。如果你想知道元素是否在边界上,代码是

//returns all entries that are in both vectors
inline vector<unsigned int> intersection(const vector<unsigned int>& vector_a,
const vector<unsigned int>& vector_b);
typedef vector<unsigned int> Face; //defined in model, a pair of node ids
//number = 0..3, returns the corresponding face
Model::Face Model::get_face(const unsigned int element_id,
const unsigned int number);
vector<unsigned int> Model::incident_elements(const Face& face){
return intersection(nodes[face[0]].incident_elements,
nodes[face[1]].incident_elements);
}
bool Model::is_boundary(const unsigned int element_id){
//check if it has a face that is boundary
for (unsigned int i=0; i<4;i++){
Face face = get_face(element_id, i); 
if(incident_elements(face).size() == 1){
return true;
}
}
return false;
}

(所有引用的方法和函数都应该是不言自明的,Face可以转换为一个结构或类,可能使用方法Face::incident_elements{return intersection(…);},尤其是如果你想在脸上做更多的事情,但Face对象可能是临时的,因为它们可以很容易地提取)

这种方式允许清理操作,当然每个节点都需要存储偶发元素向量,这需要更多的内存。但我怀疑,如果没有这样的事情,你是否能够高效地工作,尤其是因为我认为你会想做更多这样的操作。

可以用静态大小的东西替换Node和Element中的向量,但我认为这没什么大不了的,尤其是因为它们只能在Model中访问。

该体系结构的缺点是,删除要么效率低下(更改所有id存储),要么在内存中留下漏洞(尽管如果存储了未使用的id列表,这也不算太糟)

正如在帖子的评论中所说,对静态大小的向量使用std::array,并在for循环中使用const引用,这将避免复制并有助于优化:

for (const auto &iter: m_elements)

for(auto &it: faces)

如果你有很多元素(>50),我认为你还应该将用于面的容器从std::vector更改为td::map,这样:

for(auto it: faces)
{
if(s== it.first)
(it.second)++; break;
}
faces.push_back(s,1);

将变为:

auto &it = faces.find(s);
if (it != faces.end())
it.second++;
else
faces.insert(std::make_pair(s, 1));