平衡树中的节点数
Number of Nodes in a Balanced 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
我终于弄明白了。
从条件
保证对于每个节点E,左子树将具有与该节点E处的右子树一样多的节点或多出一个节点。
因此
- 非叶节点的数量可以根据树的深度来计算;它是2深度-1。因此,有趣的计数是叶节点
- 给定平衡条件,总是只有一个地方可以插入新节点或删除现有节点。(这意味着给定数量的叶节点意味着一个,而且只有一个叶节点模式。)
- 如果我们知道一个节点的左子树的叶节点的数量,我们就知道右子树中的叶节点数量(和节点数量)要么相同,要么少一个
- 它从2开始。和3。在右子树中只有一个叶节点槽,如果不检查树是否已填充,我们就无法知道它。找到它是这个算法的诀窍
因此,利用3:假设我们有一个(子)树T。我们知道它的左子树中的叶节点数是nleft。因此,我们知道它的右子树中的叶节点的数量是nleft或nleft-1,特别是它最多是nleft。
我们进入右边的子树。知道这个子树中叶节点的最大数量,并且知道它们在两侧的子树中均匀分布,我们可以推断出两件事:
- 如果这个子树中叶节点的最大数量是奇数,那么有问题的槽在左边,因为右边不能比左边重。如果是偶数,则插槽在右侧
- 每个子树中叶节点的最大数量是子树中叶节点数量的一半,在左侧向上取整,在右侧向下取整
这解决了问题的核心;剩下的就是简单的递归。在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;
}
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
}
相关文章:
- 计算每个节点的树高,帮助我解释这个代码解决方案
- 为什么我们要为avl树实现返回一个指向节点的指针,而不是void函数
- 二叉树结构平衡,使用递归时EXC_BAD_ACCESS
- 指向二叉树中新节点的指针
- 将树节点添加到向量向量中的 n 元树遍历的平均和最坏情况时间复杂度是多少?
- 在树结构.txt文件中搜索节点
- C++二叉搜索树模板从函数返回节点
- 没有从"树节点"到"树节点*"的可行转换
- 如何在C++中实现节点数任意的通用树数据结构?
- 用于基于节点的树结构的Python绑定
- 使用递归时不将节点写入树的函数
- 如何制作具有 n 个子节点的树
- 遍历仅引用每个节点的子节点的树
- 函数结束后儿童节点的树递归C 缺失值
- 二叉搜索 删除目标节点的树有两个子节点
- 平衡树中的节点数
- STL 映射容器在构造时是否优化(平衡树)
- 找到并返回比平衡树的q大的下一个最小值
- 如何使用QSortFilterProxyModel来过滤只显示子节点及其父节点的树模型
- 高度平衡树代码中的分段错误