散列中的单独链接

separate chaining in hashing

本文关键字:单独 链接      更新时间:2023-10-16

我正在读Robert Sedwick关于C++中算法的书中关于哈希的内容

我们可能使用头节点来简化插入代码到有序列表中,但我们可能不想使用M个标头节点单独链接中的单个列表。事实上,我们甚至可以消除M通过列表中的第一个节点链接到列表包括表

class ST
{
    struct node
    {
        Item item;
        node* next;
        node(Item x, node* t)
            { item = x; next = t; }
     };
     typedef node *link;
  private:
     link* heads;
     int N, M;
     Item searchR(link t, Key v)
     {
          if (t == 0) return nullItem;
          if (t->item.key() == v) return t->item;
          return searchR(t->next, v);
     }
   public:
     ST(int maxN)
     {
          N = 0; M = maxN/5;
          heads = new link[M];
          for (int i = 0; i < M; i++) heads[i] = 0;
     }
     Item search(Key v)
         { return searchR(heads[hash(v, M)], v); }
     void insert(Item item)
         { int i = hash(item.key(), M);
           heads[i] = new node(item, heads[i]); N++; }
};

我对以上文本的两个问题作者所说的是什么意思

  1. "我们甚至可以通过让列表中的第一个节点组成表来消除到列表的M个链接。"我们如何修改上面的代码?

  2. "我们可能不想在单独的链接中为单个列表使用M个头节点。"这句话的意思是什么。

"我们甚至可以通过让列表中的第一个节点组成表来消除到列表的M个链接。"

考虑Node* x[n]Node x[n]:前者需要为每个非空元素的头Node分配额外的指针和插入内存,以及为每个哈希表操作分配额外的间接性,而后者消除了n指针,但要求任何未使用的元素将能够被置于某种可辨别的未使用状态(其跟踪可能需要或可能不需要额外的存储器),并且如果sizeof(Node)大小大于sizeof(Node*),则无论如何都可能更浪费存储器。内存使用的差异也会影响缓存使用的效率:如果表的元素与桶的比率很高,那么Node[]会将Node数据放入更少的连续内存页中,如果你正在迭代(按未排序的顺序),那么它的缓存效率非常高,而Node*[]将跳转到可能到处都是的单独的内存分配(或者另一方面,在一些实际有用的情况下,可能实际上非常接近:例如,如果访问模式和动态内存分配地址都与对象创建的时间相关

我们如何修改上面的代码?

首先,您现有的代码有一个问题:heads[i] = new node(item, heads[i]);覆盖哈希表中的一个条目,而不首先检查它是否为空。。。如果有任何内容,那么您应该添加到列表中,而不是覆盖数组。

设计变更讨论需求:

link* heads;

更改为…

node* head;

你可以这样初始化:

head = new node[M];

它需要一个额外的node构造函数(如果item有一个等效的默认构造函数,你可以在下面省略它的初始化)

node() : item(nullItem), next(nullptr) { }

然后,对代码的其余部分进行一些易于处理的连锁更改。基本上,你要去掉一层指针。

"我们可能不想在单独的链接中为单个列表使用M个头节点。"这句话的意思是什么。

我没有写它,所以不能权威地说,但它似乎是在说,在设计列表代码时,即使在空列表中,也可能会决定有一个初始节点,因为这简化了几个列表操作的代码。虽然在考虑列表的"常规"使用时,额外的无数据节点似乎是一个合理的价格,但哈希表是不寻常的,因为你希望由桶组成的大多数列表都有0或1个元素,而指数级的更少应该越来越长。因此,这样的列表实现不太适合在哈希表中使用。