【问题标题】:Traversal method for a m-ary Tree structure一种多叉树结构的遍历方法
【发布时间】:2019-10-31 10:55:39
【问题描述】:

我有一个 Node 类和一个 Tree 类(带有模板),我想为我的 Tree 类编写一个 遍历方法打印出我结构中的每个节点。但是我只得到了根的孩子,之后就没有任何打印了。


这是我的代码。它有点太长了,但是有问题的方法被命名为 traverse() 并且它被放置在 main 函数之前。

#include <iostream>
#include <cstddef>
#define N 5

template <typename V>
class Node {
private:
  V _data;
  unsigned short _size;
  Node<V>* _children;

  template <typename U>
  friend std::ostream& operator<< (std::ostream&, const Node<U>&);

public:
  Node();
  Node(V, unsigned short);
  Node(const Node&); // copy constructor
  Node& operator= (Node&); // assignement by copy constructor
  Node (Node&&); // transfer constructor
  Node& operator= (Node&&); // assignement by transfer constructor
  ~Node();
  V getData() const;
  unsigned short getSize() const;
  Node<V>* getChildren();
  void setChild(unsigned short, Node<V>);
};

template <typename V>
Node<V>::Node()
  : _data(0), _size(0), _children(nullptr) {}

template <typename V>
Node<V>::Node(V data, unsigned short size)
  : _data(data), _size(size), _children(new Node<V>[_size]) {
}

template <typename V>
Node<V>::Node (const Node& other)
  : _size(other._size), _data(other._data) {
  _children = new Node<V>[_size];
  for (unsigned short i = 0; i < _size; i++) 
    _children[i] = other._children[i];
}

template <typename V>
Node<V>& Node<V>::operator= (Node& n){
  if (&n != this) {
    delete[] _children;
    _data = n._data; _children = n._children; _size = n._size;
    n._data = 0; n._size = 0; n._children = nullptr;
  }
  return *this;
}

template <typename V>
Node<V>::Node (Node&& n){
  _data = n._data; _size = n._size; _children = n._children;
  n._data = 0; n._size = 0; n._children = nullptr;
}

template <typename V>
Node<V>& Node<V>::operator= (Node&& n){
  if (&n != this) {
    delete[] _children;
    _data = n._data; _children = n._children; _size = n._size;
    n._data = 0; n._size = 0; n._children = nullptr;
  }
  return *this; 
}

template <typename V>
Node<V>::~Node() { delete[] _children; }

template <typename V> // TO DO : move this to class scope ???
V Node<V>::getData() const {return _data;}

template <typename V>
unsigned short Node<V>::getSize() const {return _size;}

template <typename V>
Node<V>* Node<V>::getChildren() {return _children;}

template <typename V>
void Node<V>::setChild(unsigned short index, Node<V> childNode){
  this->_children[index] = childNode;
}

template <typename V>
std::ostream& operator<< (std::ostream& o, const Node<V>& node){
  if (node._data) o << node._data ;
  return o;
}

template <typename V>
class Tree {
private:
  Node<V>* _info;
public:
  Tree();
  Tree(Node<V>*);
  Tree(V, unsigned short);
  ~Tree() = default;
  Node<V>* info();
  void traverse();
  void traverse_process(Node<V>*, unsigned short);
};

template <typename V>
Tree<V>::Tree() 
  : _info(nullptr) {}

template <typename V>
Tree<V>::Tree(Node<V>* newNode)
  : _info(newNode) {}

template <typename V>
Tree<V>::Tree(V data, unsigned short size) {
  Node<V>* node = new Node<V>(data, size);
  _info = node;
}

template <typename V>
Node<V>* Tree<V>::info() { return _info;}

template <typename V>
void Tree<V>::traverse() {
  traverse_process(this->_info, _info->getSize());
}

template <typename V>
void Tree<V>::traverse_process(Node<V>* node, unsigned short size) {
  if (node){
    for (unsigned short i = 0; i < size; i++) {
      if (node->getChildren()[i].getData()){
        std::cout << node->getChildren()[i] << std::endl;
        traverse_process(node->getChildren()[i], size);
      }
    }
  }
}

int main(int argc, char const *argv[]) {
  Node<char> n1('A', N); // root 
  Node<char> n1_1('B',N); // child of n1
  Node<char> n1_2('C', N); // child of n1
  Node<char> n1_3('D', N); // child of n1
  Node<char> n1_4('E', N); // child of n1
  Node<char> n1_1_1('F', N); // child of n1_1
  Node<char> n1_1_2('G', N); // child of n1_1
  Node<char> n1_1_3('H', N); // child of n1_1
  Node<char> n1_2_1('I', N); // child of n1_2
  Node<char> n1_2_2('J', N); // child of n1_2
  Node<char> n1_1_1_1('K', N); // child of n1_1_1
  n1.setChild(0,n1_1);
  n1.setChild(1,n1_2);
  n1.setChild(2,n1_3);
  n1.setChild(4,n1_4);
  n1_1.setChild(1, n1_1_1);
  n1_1.setChild(2, n1_1_2);
  n1_1.setChild(3, n1_1_3);
  n1_2.setChild(2, n1_2_1);
  n1_2.setChild(4, n1_2_2);
  n1_1_1.setChild(0, n1_1_1_1);
  Tree<char> t(&n1);
  t.traverse();
  return 0;
}

提前感谢您的帮助

【问题讨论】:

  • 对您的错误消息的描述会有所帮助。我把整个东西都扔进了godbolt并且工作正常。我不得不将第一个参数更改为 traverse_process&amp;node-&gt;getChildren()[i] 尽管 godbolt.org/z/UhzIgr
  • @parktomatomi 我编辑了这个问题......没有任何错误。我得到了根的孩子,之后即使我用根的每个孩子调用 traverse_process ,我也没有得到任何孩子的打印。输出是B C D E,它只是根的孩子,而不是所有的孩子

标签: c++ class templates tree traversal


【解决方案1】:

您的节点有两个副本:

  • 您在此处将它们分配到堆栈中:Node&lt;char&gt; n1('A', N);
  • 他们在这里造自己的孩子:_children = new Node&lt;V&gt;[_size];

当你调用 setChild() 时,它会这样做:

  • this-&gt;_children[index] = childNode

复制整个节点从堆栈上的一个节点到节点拥有的节点

因此,当您将孙子添加到堆栈中时,traverse_process 不会看到任何内容,因为它使用了不同的副本。这就是为什么第一级遍历有效,但更深的级别无效。

要修复它,您需要为 Node 拥有的子节点添加访问器,然后调用 setChild there。或者,您需要更改 _children 的工作方式,以便从堆栈分配的节点中为其提供指针。

【讨论】:

  • 感谢您的回答。我现在明白了这个问题,但我无法解决它。我不明白你最后一段是什么意思。您能否详细说明或给出解决方案的提示。
  • 您需要确保您设置的节点的所有者是其父节点。使用您现在拥有的 API,您可以这样做:auto&amp; n1_1 = n1.getChildren()[0] = Node&lt;char&gt;('B',N); 它会起作用,因为 n1_1 成为对 n1 拥有的节点的引用,然后您为其分配一个临时节点,它应该调用移动赋值运算符。但是,这也有点乏味。您可以尝试调整您的 API 以使其更易于使用。
  • 非常感谢您的回答
猜你喜欢
  • 1970-01-01
  • 2021-01-12
  • 2016-01-09
  • 2020-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
相关资源
最近更新 更多