【问题标题】:Managing duplicates in a binary tree with memory efficiency以内存效率管理二叉树中的重复项
【发布时间】:2019-03-12 00:13:35
【问题描述】:

我有一个自平衡键值二叉树(类似于 Tarjan 的 Zip Tree),其中会有重复的键。为了确保 O(log N) 性能,我唯一能想到的就是为每个节点维护三个指针;小于、大于和“等于”。 equals 指针是指向具有相同键的成员的链表的指针。

这对我来说似乎内存效率低下,因为我将在整个树中的每个节点有额外的 8 个字节来处理不常见的重复事件。有没有更好的方法不涉及“作弊”,例如位敲击左指针或右指针以用作标志?

【问题讨论】:

  • 我们在谈论多少个节点?
  • 当然你可以说 if(data == node->value) return;
  • @jwdonahue 。我用 50,000,000 进行测试。
  • 节点的值数据局部性是否满足要求?
  • 使用指向重复列表的指针的一字节或二字节哈希。没有欺骗时将其设置为零。

标签: c algorithm


【解决方案1】:
  • 当发生碰撞插入时,分配新缓冲区,复制新数据。
  • 将新数据指针散列到一或两个字节。您需要一个仅在零输入时返回零的哈希!
  • 将哈希值存储在您的节点中。如果没有冲突数据,该字段将为零,因此对于所有没有额外数据元素的键,您是 O(log KeyCount)。你最糟糕的情况是记录 KeyCount 加上你的哈希算法在查找时产生的任何东西,这可能是一个接近 1 个额外步骤的常数,直到你的表必须调整大小。

显然,在这里选择散列算法至关重要。在您所针对的任何架构上寻找一个对指针值有好处的架构。对于不同的架构,您可能需要不同的哈希值。

您可以通过仅使用一个字节的哈希值获得哈希表,然后使用键哈希(可以是更大的整数)来查找指向附加数据的指针,从而进一步实现这一点。当哈希表填满时,将一个新的插入到父表中。我会把数学留给你。

关于数据局部性。由于节点数据很大,无论如何您已经没有对实际数据局部性的良好节点记录。此方案不会改变这一点,除非您有多个数据节点用于特定键,在这种情况下,您可能会缓存未命中节点中嵌入的变量数组的正确索引。这种方案避免了在冲突时重新分配节点,并且可能不会对您的缓存未命中率产生严重影响。

【讨论】:

  • 这样的好处是,如果没有遇到任何欺骗,性能永远不会退化。您在任何地方都可以获得良好的数据局部性,除非您必须使用散列来查找另一个表或其他数据,然后通过仔细选择散列算法可以将惩罚保持在很小的水平。您的空间要求也很低。
  • 谢谢,我正在收集你的答案,很快就会回来。
  • 如果你查看你的指针,你会发现一些最高有效位对于给定的堆是固定的。由于您还有大数据块,因此 LSB 端有一堆位也不能对哈希值做出任何贡献。修剪这些,然后将其余的散列到更小的数据大小,你就解决了你的问题。换句话说,您可以使用掩码、移位和截断将散列函数输入大小减少一个或多个字节。然后,您可以通过将其散列到更小的大小来进一步减小存储在节点中的引用的大小。
  • 指针不是整数,它们没有随机分布。在散列之前对指针进行预处理,这将有很大帮助。
  • 所有好主意。谢谢!我考虑过像一些红黑树实现那样对左或右节点指针进行位打击以添加标志,但缺点是它要求每个人都知道指针在标志位被清除之前是无效的。
【解决方案2】:

我通常在执行二叉搜索树时使用此设置,它会在数组中跳过重复值:

#include <stdio.h>
#include <stdlib.h>

#define SIZE 13

typedef struct Node
{
  struct Node * right;
  struct Node * left;
  int value;
}TNode;

typedef  TNode * Nodo;

void bst(int data, Nodo * p )
{
Nodo pp = *p;
  if(pp == NULL)
  {
    pp = (Nodo)malloc(sizeof(struct Node));
    pp->right = NULL;
    pp->left = NULL;
    pp->value = data;
    *p = pp;
  }
  else if(data == pp->value)
    {
      return;
    }
  else  if(data > pp->value)
    {
      bst(data, &pp->right);
    }
  else
    {
      bst(data, &pp->left);
    }

  }

void displayDesc(Nodo p)
{
  if(p != NULL)
  {

  displayDesc(p->right);
  printf("%d\n", p->value);
  displayDesc(p->left);

  }
}

void displayAsc(Nodo p)
{
  if(p != NULL)
  {

  displayAsc(p->left);
  printf("%d\n", p->value);
  displayAsc(p->right);

  }
}


int main()
{
  int arr[SIZE] = {4,1,0,7,5,88,8,9,55,42,0,5,6};

Nodo head = NULL;

for(int i = 0; i < SIZE; i++)
  {
    bst(arr[i], &head);
  }

displayAsc(head);
  exit(0);
}

【讨论】:

  • 我相信 OP 暗示了键和数据之间的一对多关系。
  • 正如我上面所说,我将键和值存储在树中,键可能是重复的,但值是有效负载。这些值也可能是重复的。您的代码只会重复返回。
猜你喜欢
  • 2016-10-28
  • 1970-01-01
  • 2021-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 2012-06-05
  • 1970-01-01
相关资源
最近更新 更多