Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;基于链表的树结构。在列表之间复制节点_C++_Stl_Linked List - Fatal编程技术网

C++ C++;基于链表的树结构。在列表之间复制节点

C++ C++;基于链表的树结构。在列表之间复制节点,c++,stl,linked-list,C++,Stl,Linked List,编辑 分类:目的不是从原始列表中删除节点。但要创建与原始节点相同的节点(数据和子节点),并将其插入到新列表中。换句话说,“移动”并不意味着从原始文件中“移除” 恩迪特 要求: 列表中的每个节点都必须包含对其上一个同级节点的引用 列表中的每个节点必须包含对其下一个同级节点的引用 每个节点可以有一个子节点列表 每个子节点必须有对其父节点的引用 基本上我们有一个任意深度和长度的树结构。比如: -root(NULL) --Node1 ----ChildNode1 ------ChildOfChild

编辑

分类:目的不是从原始列表中删除节点。但要创建与原始节点相同的节点(数据和子节点),并将其插入到新列表中。换句话说,“移动”并不意味着从原始文件中“移除”

恩迪特

要求:

  • 列表中的每个节点都必须包含对其上一个同级节点的引用
  • 列表中的每个节点必须包含对其下一个同级节点的引用
  • 每个节点可以有一个子节点列表
  • 每个子节点必须有对其父节点的引用
基本上我们有一个任意深度和长度的树结构。比如:

-root(NULL)
--Node1
----ChildNode1
------ChildOfChild
--------AnotherChild
----ChildNode2
--Node2
----ChildNode1
------ChildOfChild
----ChildNode2
------ChildOfChild
--Node3
----ChildNode1
----ChildNode2
Node* myNode = ...
Node* someChildInListTwo = findPlaceToInsert(listTwo);
listTwo->insertAfter(myNode->clone(), someChildInListTwo);
给定任何单个节点,您需要能够遍历其兄弟节点。子节点,或从树向上到根节点

节点的最终外观如下所示:

class Node
{
  Node* previoius;
  Node* next;
  Node* child;
  Node* parent;
}
我有一个容器类来存储这些内容并提供STL迭代器。它执行典型的链表访问器。因此insertAfter看起来像:

void insertAfter(Node* after, Node* newNode)
{
  Node* next = after->next;
  after->next = newNode;
  newNode->previous = after;
  next->previous = newNode;
  newNode->next = next;
  newNode->parent = after->parent;
}
这就是设置,现在回答问题。如何将一个节点(及其子节点等)移动到另一个列表,而不使前一个列表悬空

例如,如果Node*myNode存在于ListOne中,我想将其附加到ListOne中

使用指针,listOne的列表中会留下一个洞,因为下一个和上一个指针都发生了更改。一种解决方案是通过附加节点的值进行传递。因此,我们的insertAfter方法将成为:

void insertAfter(Node*after,Node newNode)

这似乎是一个笨拙的语法。另一种选择是在内部进行复制,因此您可以:

void insertAfter(Node* after, const Node* newNode)
{
  Node *new_node = new Node(*newNode);
  Node* next = after->next;
  after->next = new_node;
  new_node->previous = after;
  next->previous = new_node;
  new_node->next = next;
  new_node->parent = after->parent;
}
最后,您可以创建一个moveNode方法来移动并防止原始插入或附加已分配同级和父级的节点

// default pointer value is 0 in constructor and a operator bool(..) 
// is defined for the Node

bool isInList(const Node* node) const
{
  return (node->previous || node->next || node->parent);
}
// then in insertAfter and friends
if(isInList(newNode)
  // throw some error and bail

我想我应该把这个扔出去,看看大家有什么想法。

让我们调用要删除的节点
current
。只有
current
指向的节点,即
current->previous
current->next
current->child
,以及
current->parent
指向节点
current
。例如,如果当前节点的上一个节点为非空,则当前节点的上一个节点为下一个节点。因此,我们可以很容易地从列表中删除当前节点,如下所示:

垂直链接
节点保留其子节点,但需要将其从父节点中删除。这是这样做的:

Let parent = current->parent
if parent is non-null:
    if parent->child == current:
         parent->child = current->next
current->parent = null
水平链接
以下代码将在水平(下一个/上一个)方向上取消当前节点的链接:

Let prev = current->previous
Let next = current->next
if prev is non-null:
    prev->next = next
if next is non-null:
    next->previous = prev
current->previous = null
current->next = null

诚然,这仍然有点混乱,但是如果你将链表功能分解成小功能(看起来你已经在做了)并使用好的注释,那么我不认为这真的没有那么糟糕。

让我们调用将要删除的节点
current
。只有
current
指向的节点,即
current->previous
current->next
current->child
,以及
current->parent
指向节点
current
。例如,如果当前节点的上一个节点为非空,则当前节点的上一个节点为下一个节点。因此,我们可以很容易地从列表中删除当前节点,如下所示:

垂直链接
节点保留其子节点,但需要将其从父节点中删除。这是这样做的:

Let parent = current->parent
if parent is non-null:
    if parent->child == current:
         parent->child = current->next
current->parent = null
水平链接
以下代码将在水平(下一个/上一个)方向上取消当前节点的链接:

Let prev = current->previous
Let next = current->next
if prev is non-null:
    prev->next = next
if next is non-null:
    next->previous = prev
current->previous = null
current->next = null

诚然,这仍然有点混乱,但是如果你将链表功能分解成小功能(看起来你已经在做了)并使用好的注释,那么它真的没有那么糟糕,我不认为。

首先,我同意,如果你复制节点,而不是从原始树中删除它,该操作应称为复制,而不是移动

其次,我建议您将实际执行复制的操作与执行插入的操作分开。这将使它更加灵活,例如,如果您需要将来自其他源的节点插入到目标中,或者您希望复制节点以用于其他目的

第三,您没有指定节点是否是多态的。如果是,我将实现如下方法进行复制:

virtual Node* clone();
在这里,您需要做出几个设计决策:

  • 进行克隆时是否要保留下一个、上一个和父级?我认为把它们归零是有道理的
  • 在进行克隆时,是否也要对子对象进行深度复制?根据用例的不同,您可能希望执行其中一项或另一项操作,或者同时考虑这两项
第四,如果已经设置了parent/prev/next指针,则假定插入应该失败。您可以这样做,但您可能会受益于更强大的插入操作,在本例中,该操作只需从现有树中删除节点。任何一方的行为都将始终如一

第五,如果要修复const节点的指针,就不能传递该节点

现在让我们假设您决定将链表指针归零,但决定深度复制子指针。那么您的代码可能如下所示:

class Node {
public:
    Node() : prev(NULL), next(NULL), parent(NULL), child(NULL) { }
    virtual Node* clone() {
        Node* newN = new Node();
        newN->cloneChildren(*this);
        return newN;
    }
    Node* lastChild() const { /* left as exercise */ }
    void insert(Node* node_) { insertAfter(node_, lastChild()); }
    void insertAfter(Node* node_, Node* prevSibling_) {
        ASSERT(node_);
        if (! prevSibling_) { // assume we want to push to front of child list
           prevSibling_ = child; // will be NULL if we have no children
        }
        ASSERT(! prevSibling_ || prevSibling_->parent == this);
        if (node_->parent) {
            // assume you want to move the child in this case
            node_->parent->remove(node_);
        }
        node_->parent = this;
        node_->prev = prevSibling_;
        if (prevSibling_) {
            node_->next = prevSibling_->next;
            prevSibling_->next = node_;
        } else {
            /* the new child is the only child - left as exercise */
        }
    }
    void remove(Node* child_) { /* left as exercise */ }
protected:
    virtual void cloneChildren(const Node& rhs) { /* left as exercise */ }
};
从一棵树复制到另一棵树的代码看起来就像:

-root(NULL)
--Node1
----ChildNode1
------ChildOfChild
--------AnotherChild
----ChildNode2
--Node2
----ChildNode1
------ChildOfChild
----ChildNode2
------ChildOfChild
--Node3
----ChildNode1
----ChildNode2
Node* myNode = ...
Node* someChildInListTwo = findPlaceToInsert(listTwo);
listTwo->insertAfter(myNode->clone(), someChildInListTwo);
在本例中,我们已将一个有点复杂的操作转换为一组离散操作,您可以在多种情况下使用这些操作:

  • 一种
    clone()
    操作,允许您复制任意节点,无论它们是否已添加到树中
  • 一个
    insert()
    操作,如果您愿意,它将自动为您执行移动,并正确处理添加到子列表前面的操作
  • 一个
    remove()