【问题标题】:Binary Tree Node Fault二叉树节点故障
【发布时间】:2010-11-22 02:29:36
【问题描述】:

这是节点定义:

struct node{
    int data;
    stuct node * left;
    struct node * right;
};

我要做的是列出所有指向祖先节点的节点。在发布了错误的解决方案并从答案中获取建议后,我的新解决方案是:

递归遍历二叉树。将当前节点添加到节点数组中,然后检查当前节点的子节点是否指向任何先前的祖先节点。

默认情况是节点为 NULL。如果发生这种情况,函数将返回。

它应该如何工作:

将节点添加到数组中

检查左孩子是否为NULL。

如果不是,它将子节点与之前的每个节点进行比较。

如果发现故障,它会报告它。

如果不是,它以子作为参数调用函数。

重复直到完成。 (对于二叉树的 rhs 也一样)

问题:

  • 数组是最好存储的东西吗 节点?
  • 这行得通吗? for (i = 0; i
  • 因为函数是递归的, 数组和数组索引不能 在函数内部初始化 (或者他们可以吗?)他们应该是 全球?
  • 有两个阵列会更好吗? (一个用于 LHS,一个用于 右轴)

代码:

void findFault(node * root){
    if (root == NULL){
      return;
    }

    arrOfNodes[index++] == root; // array of nodes

    if (root->left != NULL){
      for (i = 0; i < sizeof(arrOfNodes) / sizeof(node); i++){
         if (ar[i] == root->left){
             printf("%d", root->left);
             return;
         }
       }
       findFault(root->left);
    } else return;

    if (root->right != NULL){
      for (i = 0; i < sizeof(ar) / sizeof(node); i++){
         if (ar[i] == root->right){
             printf("%d", root->right);
             return;
         }
      }
      findFault(root->right);
    } else return;
}

【问题讨论】:

  • 我已经更新了问题并且我的解决方案已经完全改变了。顺便尽量不使用c++标准库。

标签: c++ c binary-tree


【解决方案1】:

完成这种检查的另一种方法是对节点进行广度优先扫描,同时保留您已经访问过的节点向量(您可以保持按地址排序)。每次访问一个节点时,断言它不在向量中,然后将其添加到适当的位置以保持访问列表的排序。

这种检查的优点是它可以在不修改树或节点结构本身的情况下执行,尽管有一点性能损失。

注意事项:

  • 数组是存储节点的好方法。如果您要避免 STL(好奇:为什么?),那么您将不得不管理自己的内存。可行,但要重新发明它是一个脆弱的轮子。
  • 获取数组大小的 for 循环检查将不起作用;如果你使用 malloc/free 或 new/delete 那么你必须事先指定你想要的数组的大小;您应该使用该大小,而不是每次通过 for 循环都计算它。
  • 递归算法的典型模式是具有“外部”和“内部”函数。外层函数是由外部代码调用的,做初始设置等。内层函数只被外层函数调用,往往有更复杂的参数集(取外层函数设置的数据),调用自己来执行实际的递归。
  • 您将需要两个数组:一个用于您已访问的节点列表,另一个用于您尚未访问的节点列表。

【讨论】:

  • 接受这个作为正确答案。我将根据需要使用 malloc/free 分配内存。我不明白为什么我需要两个数组。
  • 您可以使用一个数组来跟踪您访问过的节点和尚未访问过的节点,并有一个指向每个集合中第一个节点的指针。 6 个,大部分是 6 个。
【解决方案2】:

我不知道递归,但是这个:

if (&root->left->left == &root){

在我可以描述的更多方面是错误的,但无论如何这里有三个问题:

  • 你为什么要取root的地址?
  • 为什么不测试第一个左指针是否为空?
  • 您可以简单地使用 std::map,但学习如何实现二叉树也是一个好主意。

【讨论】:

  • 关于您的第三个问题,他们正在使用的特定代码可能是遗留代码,或者可能只是用 C 编写的,因此他们无权访问 std::map。许多 C 问题被错误标记为 C++,因为 a) 一种普遍的混淆是,因为一个基于另一个,语言应该被认为是相似的,b) C++ 标签在这个网站上的流行度更高。
  • 提问者的错误标记真的不是我的问题——我不是通灵者。
  • 感谢您的回答。我没有使用 std::map 因为我想学习如何这样做。我将它标记为 c 和 c++,因为我认为如果不使用标准 c++ 库,解决方案可以以相同的方式完成。
  • @Bua 当然,学习如何自己做是一项有价值的练习。我会修改我的答案以反映这一点。
  • @Neil - 很公平。我想我不能要求你通灵,不管那有多酷。
【解决方案3】:

我不知道生成二叉树的算法是否能够传播除节点的左/右子节点以外的错误。

无论如何,这是您代码的更正版本:

void findFault(node * root){
    if (root == NULL){
      return;
    }

    if (root->left == root){
      printf("left: %d", root->data);
    } else findFault(root->left);

    if (root->right == root){
      printf("right: %d", root->data);
    } else findFault(root->right);
}

【讨论】:

    【解决方案4】:

    这是对问题的错误解决方案。 Neil Butterworth 已经在您的代码上注明,我会在算法上注明。

    您的算法只检查一个非常具体的情况 - 孙节点是否指向其祖父节点。您应该做的是在到达节点的过程中收集父节点,并查看节点的子节点不是其父节点之一。

    有很多方法可以做到这一点。一种是在您的节点结构中添加一个计数器并将所有节点的计数器设置为零,然后再开始遍历树。每当您到达一个节点时,请确保计数器为零,然后将其增加一。这意味着如果您看到一个计数器不为零的孩子,那么您已经访问过它,因此该树无效。

    【讨论】:

    • 这是个好主意,但我不想修改节点结构。
    • @Bua - 您可以为计数器使用外部数据结构,例如您可以使用 std::map ,其中地址是键,计数器是数据。
    猜你喜欢
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多