找出节点是否是二进制树中另一个节点的祖先



我需要向树中的每个节点添加一个恒定的字段,因此当给出两个节点x, y时,我需要找出x是否是y中CC_3的祖先。

我的第一个想法是为每个节点添加一个"深度"字段。然后,如果x.depth >= y.depth,我可以直接消除查询,但这显然还不够...

假设您要建立一棵树:

              a
             / 
            /    
           /     
          b       c
        /       / 
       /       /   
      d      e f     g
                   /  
       h           i    j
       /
      k

每个节点都有一个额外的字段,一个uint32_t int lineage。如果您用数组实现树,则不需要此额外的字段。出于所有意图和目的,都假定我们正在使用节点。

您可以使用类似的想法:

left  = 2 * parent + 1;
right = 2 * parent + 2;

但是,在根节点上,让谱系等于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,我们知道一个节点是祖先或节点,二进制谱系(或l r tattery(必须是子节点的子弦乐,从左右,条件是祖先的二进制字符串严格少于孩子的条件。

但是,我们使用整数而不是字符串,以便我们可以弄清楚它是否是恒定时间的子字符串。

重要部分

// 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;
}

O(1)中的log2()来自:计算C#中整数的log2的最快方法是什么?

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: %dn", 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,以便您可以自己尝试。这只是我尝试在一棵树上尝试的一个想法。很抱歉长期解释,但我也对这个问题感兴趣。

相关内容

最新更新