C++ 元素如何在向量中找到自己的索引?
我试图使用C++ 元素如何在向量中找到自己的索引?,c++,c++11,stl,tree,C++,C++11,Stl,Tree,我试图使用std::unique\u ptr重新实现一个树数据结构,其思想是父节点将拥有其子节点,这些子节点存储在unique\u ptr的向量中 出于接口的原因,我需要一种方法,其中节点会破坏自身。在这种情况下,我认为节点需要从其父节点的子向量中删除自身 下面的实现“有效”(在c++11编译器中),但它非常丑陋,我确信它是处理这个问题的次优方法 #include <iostream> #include <memory> #include <vector> #
std::unique\u ptr
重新实现一个树数据结构,其思想是父节点将拥有其子节点,这些子节点存储在unique\u ptr
的向量中
出于接口的原因,我需要一种方法,其中节点会破坏自身。在这种情况下,我认为节点需要从其父节点的子向量中删除自身
下面的实现“有效”(在c++11编译器中),但它非常丑陋,我确信它是处理这个问题的次优方法
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
struct Node {
typedef std::vector<std::unique_ptr<Node>> vec_node_uptr;
unsigned id;
Node* parent;
vec_node_uptr child_nodes;
// ctor
Node(unsigned id): id(id){ parent = nullptr; }
void add_child(Node* new_child){
new_child -> parent = this;
child_nodes.push_back( std::unique_ptr<Node>(std::move(new_child) ) );
}
int where_am_i(){
int result_ = 0;
for(auto& i: this -> parent -> child_nodes) {
if (this == i.get()) {
return result_;
} else {
result_++;
}
}
}
void suicide(){
parent -> child_nodes.erase(parent -> child_nodes.begin()+ where_am_i());
}
};
int main()
{
std::unique_ptr<Node> root(new Node(0));
root -> add_child(new Node(1));
root -> add_child(new Node(2));
root -> child_nodes[0] -> add_child(new Node(3));
root -> child_nodes[0] -> add_child(new Node(4));
root -> child_nodes[1] -> add_child(new Node(5));
root -> child_nodes[1] -> add_child(new Node(6));
root -> child_nodes[1] -> suicide();
return 0;
}
#包括
#包括
#包括
#包括
结构节点{
typedef std::vec_node_uptr向量;
无符号id;
节点*父节点;
向量节点子节点;
//执行器
节点(无符号id):id(id){parent=nullptr;}
无效添加子节点(节点*新子节点){
新建子项->父项=此项;
子节点。向后推(std::unique_ptr(std::move(new_child));
}
int where_am_i(){
int结果=0;
用于(自动&i:此->父->子节点){
if(this==i.get()){
返回结果;
}否则{
结果++;
}
}
}
无效自杀(){
父节点->子节点。擦除(父节点->子节点。开始()+何处是i());
}
};
int main()
{
std::unique_ptr root(新节点(0));
根->添加子节点(新节点(1));
根->添加子节点(新节点(2));
根->子节点[0]->添加子节点(新节点(3));
根->子节点[0]->添加子节点(新节点(4));
根->子节点[1]->添加子节点(新节点(5));
根->子节点[1]->添加子节点(新节点(6));
根->子节点[1]->自杀();
返回0;
}
有什么建议吗?可能使用
std::find
您可以使用find_if和lambda更优雅地解决这个问题:
void suicide()
{
auto& parentsChildren = parent->child_nodes;
parentsChildren.erase(find_if(begin(parentsChildren), end(parentsChildren),
[&](const unique_ptr<Node>& node) { return node.get() == this; }));
}
void自杀()
{
auto&parentsChildren=parent->child\u节点;
parentsChildren.erase(查找if(开始)(parentsChildren),结束(parentsChildren),
[&](const unique_ptr&node){return node.get()==this;});
}
如果您希望在当前数据结构中有一个恒定的时间where_am_i()
,则需要在节点本身中存储索引或迭代器。这是(a)重复的,(b)将带来进一步的复杂性,因为无论何时删除不是其父节点最后一个子节点的节点,都需要更新所有后续子节点的索引/迭代器
再次强调,如果从向量中删除一个元素,除非您总是从末尾(或接近末尾)删除,否则使用恒定时间,这可能没有真正的好处,因为从向量中删除一个元素无论如何都是O(n)
但是,如果您通常从末尾删除,并且如果不需要将一组子节点的所有权从父节点转移出去,那么这里有一个替代的、更简单的设计,它可以避免在每个节点中存储索引或迭代器:
C++标准保证<>代码STD::vector s,像数组一样,将它们的内容在内存中连续地排列。因此,如果
子节点
向量实际上按值存储了它的元素,也就是说,如果它被声明为
typedef std::vector<Node> vec_node_uptr;
vec_node_uptr child_nodes;
我想这个问题更适合我,因为我会把责任推给父母。孩子们会调用parent->remove_child(this)@Enigma:OK,但找到孩子的基本问题保持不变。所有这些变化都是源代码的位置:-在这种情况下,父代不是其子代的唯一所有者,因为子代可以确定自己的生命周期何时结束。我怀疑这就是为什么您的解决方案看起来很笨拙的原因,因为unique\u ptr
是为指针对相关对象拥有唯一所有权的情况而设计的。实际上,您的设计使父级具有唯一的指向子级的“弱”指针,子级的生命周期是自我管理的。一种方法可能是实现unique\u-weak\u-ptr
和unique\u-strong\u-ptr
和self\u-ownership
CRTP类。。。即使这样,指向child的指针也会失效,但计数不会失效。@Yakk:是这样吗?我觉得父母仍然拥有自己的孩子们的记忆,也就是说,当父母被删除时,孩子们会被自动销毁“自杀”只是一个方便的函数,可以有效地调用父方法。如果我错了,请纠正我。从末尾删除是不相关的。如果树有父链接,则已覆盖到其他父节点的传输。(如果没有,那么已经编写了算法来避免这个问题。)没有理由以任何其他方式实现where\u am\u i
。@j\u random\u hacker:真是个有趣的主意。我希望使用do way在节点之间进行更多的所有权转移,而不是删除。@Potatoswatter:既然你不是OP,你说从末尾删除与什么权限无关?有些算法可以设计为只从末尾删除,而其他算法则不能,因此总体时间复杂度将取决于OP算法所属的类别。你知道OP打算使用哪些算法吗?@j_random_hacker,因为它与解决方案是否有效无关<代码>此-&parent->children[0]
是解决方案,句号。你说“如果……和……那么解决方案是减法”,但它们是错误的条件。@Potatoswatter:我的意思是,如果你总是从(接近)结尾处删除,我建议的解决方案只会给你带来一些好处。如果您不打算从(接近)结尾处删除,那么我的O(1)解决方案将没有实际用途,因为您将立即执行O(n)删除步骤。这真的很不清楚吗?这很合适。我还没看过lambda expr。对性能有什么评论吗?std::find_if将在parentsChildren上执行线性搜索,因此复杂性不像j_random_hacker的答案那样恒定(它是
size_t where_am_i() {
return this - &parent->child_nodes[0];
}