平衡树中的节点数

时间:2015-04-06 00:11:59

标签: python c++ algorithm tree

所以我想出了一个有趣的问题,看看是否有一种有效的解决方法。所以基本上有一个平衡的二叉树,其中保存了id号(它不是bst所以没有正式的安排)。您有大量的查询来查找有多少节点。保证对于每个节点E,左子树将具有与该节点E上的右子树一样多或多一个节点。请求程序找出有多少节点的最佳方式是什么? 例如,给出这样的树:

          1
      4      2
  3

该程序将提供以下输出:

 Query: 1
 Response: 4 2
 Query: 4
 Response 3
 Query: 3
 Response: 0 0 
 Query: 2
 Response: 0 0
 Answer: 4

2 个答案:

答案 0 :(得分:1)

我终于把它弄糊涂了。

从条件

  

保证对于每个节点E,左子树将具有比该节点E处的右子树多或多一个节点。

紧随其后

  1. 可以从树的深度计算非叶节点的数量;它是2 depth - 1 。因此,有趣的事情是叶子节点。
  2. 鉴于平衡条件,总是只有一个地方可以插入新节点或删除现有节点。 (这意味着给定数量的叶节点意味着一个且仅有一个叶节点模式。)
  3. 如果我们知道节点左子树的叶子节点数,我们就知道右子树中叶子节点的数量(和节点数量)要么相同,要么小一个。
  4. 从2.和3开始,在右子树中只有一个叶节点槽,我们无法在不检查树是否填充的情况下知道它。找到它是这个算法的诀窍。
  5. 所以,利用3:假设我们有一个(子)树T.我们知道它左边子树中叶子节点的数量是n left 。因此,我们知道右子树中的叶节点数是n left 或n left - 1,特别是它最多n left

    我们进入正确的子树。知道了这个子树中叶子节点的最大数量,并且知道它们在两侧的子树中均匀分开,我们可以推断出两件事:

    • 如果此子树中的最大叶节点数是奇数,则可疑插槽位于左侧,因为右侧不能比左侧重。如果是偶数,则插槽位于右侧
    • 每个子子树中叶子节点的最大数量是子树中叶子节点的一半,左侧向上舍入,右侧向下舍入。

    这解决了事情的核心;剩下的就是简单的递归。在C ++中:

    #include <cstddef>
    
    // I'm using a simple node structure, you'd use query functions. The
    // algorithm is not meaningfully altered by this.
    struct node {
      node *left = nullptr, *right = nullptr;
    };
    
    struct node_counter {
      std::size_t leaf;      // number of leaf nodes,
      std::size_t trunk;     // number of trunk nodes,
      std::size_t depth;     // and depth of the inspected subtree.
    };
    
    // Interesting function #1: Given a right subtree and the leaf-count and
    // depth of its left sibling, find the node that might or might not be there
    node const *find_leaf(node const *branch, std::size_t leaf_count, std::size_t depth) {
      // We've gone down, found the slot. Return it.
      if(depth == 0) { return branch; }
    
      // The heart of the matter: Step into the subtree that contains the
      // questionable slot, with its maximum leaf node count and depth.
      return find_leaf(leaf_count % 2 ? branch->left : branch->right,
                       (leaf_count + 1) / 2, // int division
                       depth - 1);
    }
    
    // Recursive counter. This steps down on the left side, then infers the
    // number of leaf and trunk nodes on the right side for each level.
    node_counter count_nodes_aux(node const *root) {
      // leftmost leaf node is reached. Return info for it.
      if(!root->left) {
        return { 1, 0, 0 };
      }
    
      // We're in the middle of the tree. Get the counts for the left side,
      auto ctr_left   = count_nodes_aux(root->left);
    
      // then find the questionable slot on the right
      auto leaf_right = find_leaf(root->right, ctr_left.leaf, ctr_left.depth);
    
      return {
        // the number of leaf nodes in this tree is double that of the left
        // subtree if the node is there, one less otherwise.
        ctr_left.leaf * 2 - (leaf_right ? 0 : 1),
    
        // And this is just an easy way to keep count of the number of non-leaf
        // nodes and the depth of the inspected subtree.
        ctr_left.trunk * 2 + 1,
        ctr_left.depth + 1
      };
    }
    
    // Frontend function to make the whole thing easily usable.
    std::size_t count_nodes(node const *root) {
      auto ctr = count_nodes_aux(root);
      return ctr.leaf + ctr.trunk;
    }
    

    为了尝试这一点,我使用了以下非常难看的main函数,它只构建一个包含许多节点的树,在正确的位置插入新的并检查计数器是否以正确的方式移动。它不漂亮,它不遵循最佳实践,如果你在生产中编写这样的代码,你应该被解雇。这是它的方式,因为这个答案的要点是上面的算法,我没有看到任何使这个漂亮的感觉。

    void fill_node(node *n) {
      n->left  = new node;
      n->right = new node;
    }
    
    int main() {
      node *root = new node;
    
      fill_node(root);
    
      fill_node(root->left);
      fill_node(root->right);
    
      fill_node(root->left->left);
      fill_node(root->left->right);
      fill_node(root->right->left);
      fill_node(root->right->right);
    
      fill_node(root->left->left->left);
      fill_node(root->left->left->right);
      fill_node(root->left->right->left);
      fill_node(root->left->right->right);
      fill_node(root->right->left->left);
      fill_node(root->right->left->right);
      fill_node(root->right->right->left);
      fill_node(root->right->right->right);
    
      std::cout << count_nodes(root) << std::endl;
    
      root->left ->left ->left ->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->left ->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->left ->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->left ->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->right->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->right->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->right->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->right->left ->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->left ->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->left ->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->left ->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->left ->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->right->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->right->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->right->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->right->right->left  = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->left ->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->left ->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->left ->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->left ->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->right->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->right->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->right->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->right->left ->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->left ->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->left ->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->left ->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->left ->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->left ->right->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->left ->right->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->left ->right->right->right->right = new node;  std::cout << count_nodes(root) << std::endl;
      root->right->right->right->right->right = new node;  std::cout << count_nodes(root) << std::endl;
    }
    

答案 1 :(得分:-1)

int countnodes(ele,count)
{
 if(ele.right != null)
   {
      count += countnodes(ele.right,0);
   }
  if(ele.left != null)
  {
     count += countnodes(ele.left,0);
  }
  return count++; //got to count this node
}