【问题标题】:what should be the structure of binary search tree node二叉搜索树节点的结构应该是什么
【发布时间】:2013-11-19 05:52:04
【问题描述】:

我正在尝试为二叉搜索树制作 c++ 程序,该程序将包含以下功能(实际上这是我大学作业的一部分):

A) 创建二叉搜索树。

B) 中序、前序、后序遍历。 (非递归)

C) 在树中搜索 Val。

D) 广度优先遍历。

E) 深度优先遍历

F) 计算叶节点、非叶节点。

G) 计数。级别

我的疑问是:-

1.通常一个树节点有如下结构:

class node{

 private:
   node *lChild;
   int info;
   node *rChild;
}

所以如果我想执行深度优先或广度优先遍历,我可以更改节点结构并添加一个指向父节点的指针,以便我可以轻松地在层次结构中向后移动

class node{

 private:
   node *parent //pointer to parent node
   node *lChild;
   int info;
   node *rChild;
}

这被认为是编程二叉树的正常做法还是不好的形式?如果它不被认为是编程树的好方法,还有其他方法还是我必须使用使用堆栈(用于深度优先)和队列(用于广度优先)的书中给出的方法来存储节点(访问过或相应地未访问)

2.这是我第一次学习数据结构,所以如果有人能用简单的语言解释二叉树递归和非递归遍历之间的区别,那将是一个很大的帮助考虑中

【问题讨论】:

  • 你知道递归函数吗? IE。调用自己的函数?
  • @Nick 是的,我知道递归的概念
  • 酷。所以你只需要从根开始递归遍历树。 IE。为每个孩子(然后每个孩子......每个孩子)调用您的函数。所需的遍历取决于您 a)处理每个节点和 b)处理每个孩子的顺序。

标签: c++ algorithm data-structures recursion binary-search-tree


【解决方案1】:

我建议您研究访问者模式 - 是因为它的风格,而不是专门针对它的结构(它非常复杂)。

本质上,它是一种设计模式,它以这样一种方式断开树的遍历,即您只有一组执行树遍历的代码,并且您使用该组代码在每个节点上执行各种功能。遍历代码一般不属于Node类。

具体来说,它将允许您不必多次编写遍历代码 - 例如,utnapistims 答案将迫使您为所需的每个功能编写遍历代码;该示例涵盖了打印 - 到 ouputXML() 将需要另一个遍历代码副本。最终,你的 Node 类变成了一只笨拙的庞然大物。

使用 Visitor,您将拥有 Tree 和 Node 类、一个单独的 Traversal 类以及许多功能类,例如 PrintNode、NodeToXML 和可能的 DeleteNode,以与 Traversal 类一起使用。

至于添加父指针,这仅在您打算在调用树之间停在给定节点上时才有用 - 即您打算从预先选择的任意节点开始进行相对搜索。这可能意味着您最好不要对所述树进行任何多线程工作。 Parent 指针也很难更新,因为红/黑树可以很容易地在当前节点和它的“父节点”之间插入一个新节点。

我建议使用 BinaryTree 类,该类具有实例化单个访问者类的方法,并且访问者类接受 Traversal 接口的实现,该接口可以是 Breadth、Width 或 Binary 之一。基本上,当Visitor准备移动到下一个节点时,它会调用Traversal接口实现来获取它(下一个节点)。

【讨论】:

    【解决方案2】:

    我更改了节点结构并添加了一个指向父级的指针 [...] 这被认为是正常的做法还是编程二叉树的不良形式?

    这不是一种正常的做法(但也不是很“糟糕的形式”)。每个节点都是数据和两个指针的集合。如果为每个节点添加第三个指针,则每个节点的开销将增加 50%(每个节点两个指针到三个指针),这对于大型二叉树来说会相当多。

    这是我第一次学习数据结构,如果有人能用简单的语言解释递归和非递归遍历之间的区别,那将是一个很大的帮助

    递归实现是一个仅适用于一个节点的函数,然后为后续节点调用自身。这利用应用程序调用堆栈来处理树的节点。

    非递归实现使用本地堆栈来推送未处理的节点;然后只要堆栈上有数据,它就会循环并处理每个条目。

    这是一个打印到控制台的示例,显示了递归和非递归之间的区别(示例不完整,因为这是家庭作业:]):

    void recursive_print(node* n) {
        std::cout << n->info << "\n";
        if(n->lChild)
            recursive_print(n->lChild); // recursive call
        // n->rChild is processed the same
    }
    void non_recursive_print(node* n) {
        std::stack<node*> stack;
        stack.push(n);
        while(!stack.empty()) { // behaves (more or less) the same as 
                                // the call-stack in the recursive call
            node* x = stack.top();
            stack.pop();
            std::cout << x->info << "\n";
            if(x->lChild)
                stack.push(x->lChild); // non-recursive: push to the stack
            // x->rChild is processed the same way
        }
    }
    // client code:
    node *root; // initialized elsewhere
    if(root) {
        recursive_print(root);
        non_recursive_print(root);
    }
    

    【讨论】:

      【解决方案3】:

      如果你需要支持迭代,通常你只需要一个父指针 想象一下你找到了一个叶子节点,然后想找到下一个节点(最小的key大于当前的key),例如:

      mytree::iterator it1=mytree_local.find(7);
      if (it1 != mytree_local.end())
      {
          mytree::iterator it2=it1.next();  // it1 is a leaf node and next() needs to go up
      }
      

      因为这里是从底部而不是顶部开始,所以你需要往上走

      但是你的分配只需要从根节点开始的操作,你不应该有一个向上的指针,遵循其他答案来避免需要向上的方法。

      【讨论】:

        【解决方案4】:

        如果您愿意,没有什么可以阻止您添加父指针。但是,它通常不是必需的,并且会稍微增加大小和复杂性。

        遍历树的常规方法是某种递归函数。您首先调用该函数并传入树的根节点。然后该函数调用自身,传递子指针(一次一个)。这会一直递归地沿着树向下发生,直到没有剩余的子节点。

        在递归调用返回后,该函数在其自己的节点上执行您想要的任何处理。这意味着您基本上是在每次调用时遍历树(使您的调用堆栈逐渐变深),然后在每个函数返回时在返回的路上进行处理。

        函数应该从不尝试以与下降相同的方式返回树(即传递父指针),否则您将得到无限递归。

        【讨论】:

          【解决方案5】:
          1. 您不需要指向父节点的指针。想想你会使用它的情况。访问节点的唯一方法是通过其父节点,因此您已经访问了父节点。

          2. 你知道递归是什么意思吗?

          【讨论】:

          • 是的,我知道递归的概念。并假设如果我正在执行后序遍历并访问左节点,那么现在从这里我必须移回父节点以转到右兄弟节点,因此我需要将父节点的地址存储在左子节点中并再次从右子节点到父母在这里我也需要父母在右孩子中的地址等等
          • 在这种情况下,有一个指向父级的指针会有所帮助。另一种解决方案是使用堆栈来维护从根到当前节点的路径。
          猜你喜欢
          • 1970-01-01
          • 2014-12-21
          • 2018-03-19
          • 1970-01-01
          • 1970-01-01
          • 2021-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多