【问题标题】:Traversing a tree using linked list structure使用链表结构遍历树
【发布时间】:2015-09-21 09:40:49
【问题描述】:

我有一个结构节点,其中包含下一个和一个子节点。

struct Node{
Node *parent;
Node *next;
Node *child;
}

下图是树的结构。

我将如何编写一个遍历该树的函数?没有递归?

我脑子里有一个伪代码,但不确定它是否正确,因为每个节点都可以有一个子节点,我也需要使用那个子节点进行搜索

while ( root !=NULL)
{

root = root->next;
}

我想访问所有节点。

【问题讨论】:

  • 意味着您在没有递归的情况下提出问题,或者您不想使用递归?
  • 我不想使用递归
  • “遍历”是什么意思?沿着一条路径穿过树还是访问所有节点?
  • 你需要在struct中有两个指针才能左右遍历。
  • @andre 如果它有两个或更多的孩子怎么办?然后你需要回到父母身边,然后走另一条路。

标签: c algorithm


【解决方案1】:

我猜你的树看起来像这样:

-> grandma
    -> dad
        -> me
        -> sister
            -> niece
        -> brother
    -> uncle
        -> cousin

其中缩进表示祖先级别。递归函数很简单:

void traverse_rec(const Node *node)
{
    if (node) {
        process(node);
        traverse_rec(node->child);
        traverse_rec(node->next);
    }
}

这可以重写为循环。如果当前节点有一个孩子,那就是下一个要访问的节点。否则,您想访问它的兄弟姐妹。但是该节点实际上可能没有兄弟节点。因为您有一个到父节点的链接(并且根节点的父节点应该为 'NULL),所以您可以回溯树,直到找到要跟随的兄弟节点或直到该节点为 NULL

void traverse2(const Node *node)
{
    while (node) {
        puts(node->name);

        if (node->child) {
            node = node->child;
        } else {
            while (node && node->next == NULL) {
                node = node->parent;
            }

            if (node) node = node->next;
        }
    }
}

这比递归函数更复杂,但可以转化为类似迭代器的代码,在其中得到下一个节点:

const Node *next(const Node *node)
{
    if (node == NULL) return NULL;
    if (node->child) return node->child;

    while (node && node->next == NULL) {
        node = node->parent;
    }

    if (node) return node->next;
    return NULL;
}

使用这个“迭代器”,您可以编写如下代码:

for (const Node *p = root; p; p = next(p)) {
    puts(p->name);
}

【讨论】:

  • 这里有一些我没有得到的东西。当您返回父级时,您将如何访问由子指针指向的另一个子而不是同一个子?
  • 那个孩子将是node->child->next,它在返回并找到一个有弟弟妹妹要访问的节点后被访问。
  • 我明白了。但是使用堆栈(如我的回答)不需要有指向父级的指针,也不需要提前返回。我说的对吗?
  • 即使结构只有“向下”指针nextchild,您的解决方案仍然有效。您可以将递归解决方案转换为基于堆栈/队列的解决方案。实际上,递归将您在堆栈中的数据保留在程序的调用堆栈中。
  • 我包含递归解决方案只是为了说明;在我看来,它更干净。当我测试我的非递归代码时,我用它作为参考。
【解决方案2】:

树按队列转换为班轮列表。并处理班轮列表的每个节点。

伪代码(未测试):

enqueue(q, root);//queue *q;
while(NULL!=(current_node = dequeue(q))){
    //do stuff current_node

    if(has_child(current_node)){//unnecessary, include for-loop
        for(node = current_node->child; node != NULL; node = node->next){
            enqueue(q, node);
        }
    }
}

【讨论】:

  • 所以它是一个水平顺序遍历?
【解决方案3】:

深度优先遍历可以通过以下方式完成,不需要父指针:

  std::stack<Node*> Q; Q.push(root);
  while(!Q.empty())
  {  Node* node = Q.top(); Q.pop();
     // visit the node, do stuff
     if(node->next) Q.push(node->next);
     if(node->child) Q.push(node->child);
  }

注意它是深度优先的,因为子节点(如果有)将在同一级别的节点之前被访问,这些节点通过 next 指针链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    相关资源
    最近更新 更多