我需要为树中的每个节点添加一定量的字段,这样当给定两个节点x, y
时,我需要找出x
是否是{{1}的祖先} y
复杂性。
我的第一个想法是为每个节点添加一个“深度”字段。然后我可以直接消除查询O(1)
,但这显然不够......
答案 0 :(得分:0)
假设您构建了一棵树:
a
/ \
/ \
/ \
b c
/ \ / \
/ \ / \
d e f g
\ / \
h i j
/
k
每个节点都有一个附加字段uint32_t int lineage
。如果要使用数组实现树,则不需要此额外字段。对于所有意图和目的,假设我们使用节点。
你可以使用类似的想法:
left = 2 * parent + 1;
right = 2 * parent + 2;
但是,相反,在根节点处,让lineage等于1.对于所有后续节点,您正常插入,也传递沿袭。这将是一个整数,但您将对其进行逐位算术运算。如果你向左走,你只需向左移动(乘以2),如果你向右移动,向左移动+ 1。
/**
* Starts recursive insertion
*/
struct node* insert(struct node* node, int key) {
// Create root
if (node == NULL) {
// Root has value 1
node = newNode(key, 1);
// Start recursion to left
} else if (key < node->key) {
node->left = insert(node->left, key, node->lineage << 1);
// Start recursion to right
} else if (key > node->key) {
node->right = insert(node->right, key, (node->lineage << 1) + 1);
}
return node;
}
/**
* Recursive insert function
*/
struct node* insert(struct node* node, int key, uint32_t lineage) {
if (node == NULL) {
return newNode(key, lineage);
}
if (key < node->key) {
node->left = insert(node->left, key, 2 * node->lineage);
}
else if (key > node->key) {
node->right = insert(node->right, key, 2 * node->lineage + 1);
}
return node;
}
基本上,您可以创建二进制模式。如果你根据二元谱系来看你的树,那就是:
1
/ \
/ \
/ \
/ \
10 11
/ \ / \
/ \ / \
100 101 110 111
\ / \
1001 1110 1111
/
10010
或更简单:
1
/ \
/ \
/ \
/ \
1L 1R
/ \ / \
/ \ / \
1LL 1LR 1RL 1RR
\ / \
1LLR 1RRL 1RRR
/
1LLRL
无论你将它们称为Ls和Rs还是1和0,我们知道节点是祖先或节点,二进制沿袭(或LR模式)必须是子节点的子串,从左边开始在右边,条件是祖先的二进制字符串严格小于孩子的字符串。
但是,我们使用整数而不是字符串,以便我们可以确定它是否是恒定时间内的子字符串。
// Calculate if ancestor in O(1), no loops, no recursion
bool is_ancestor(struct node* parent, struct node* child) {
// Both pointers must be non-null
if (parent == NULL || child == NULL) {
return false;
}
// Get their lineages
uint32_t p_lin = parent->lineage;
uint32_t c_lin = child->lineage;
// Calculate the number of bits in
// binary lineage number
int p_bits = log2(p_lin);
int c_bits = log2(c_lin);
// Ancestors must
// have less bits than
// children. If this is false,
// than the parent pointer
// is at a lower point in the tree
if (p_bits >= c_bits) {
return false;
}
// Calculate difference in bits
// which represents the number of
// levels in between the child and parent
int diff = c_bits - p_bits;
// Shift child lineage to
// the right by that much
// to get rid of those bits, and
// only leave the amount of
// bits they should have in
// common
c_lin >>= diff;
// First N bits should be
// exactly the same
if (c_lin == p_lin) {
return true;
}
// If we got here, the child`
// is lower in the tree, but
// there is no path from the
// ancestor to the child
return false;
}
以下log2()
来自What's the quickest way to compute log2 of an integer in C#?
O(1)
int log2(uint32_t n) {
int bits = 0;
if (n > 0xffff) {
n >>= 16;
bits = 0x10;
}
if (n > 0xff) {
n >>= 8;
bits |= 0x8;
}
if (n > 0xf) {
n >>= 4;
bits |= 0x4;
}
if (n > 0x3) {
n >>= 2;
bits |= 0x2;
}
if (n > 0x1) {
bits |= 0x1;
}
return bits;
}
使用:
#include <stdio.h>
#include <stdlib.h>
#include <cstdint>
#include "tree.c" // Your tree
int main() {
/* Let us create following BST
50
/ \
30 70
/ \ / \
20 40 60 80 */
struct node *root = NULL;
root = insert(root, 50);
insert(root, 30);
insert(root, 20);
insert(root, 40);
insert(root, 70);
insert(root, 60);
insert(root, 80);
printf("preorder:\n");
preorder(root);
struct node* parent = get(root, 30);
struct node* child = get(root, 40);
bool ancestor = is_ancestor(parent, child);
printf("\n %d is a child or %d: %d\n", parent->key, child->key, ancestor);
return 0;
}
输出:
preorder:
k: 50 lineage: 1
k: 30 lineage: 2
k: 20 lineage: 4
k: 40 lineage: 5
k: 70 lineage: 3
k: 60 lineage: 6
k: 80 lineage: 7
30 is a child or 40: 1
如果它有用,我可以给你完整的代码tree.c
,这样你就可以自己试试。这只是我尝试在一棵小树上的想法。很抱歉很长的解释,但我也对这个问题感兴趣。