你将如何在x语言中实现哈希表?

时间:2008-08-24 18:58:16

标签: arrays hash language-agnostic

这个问题的关键是使用不同语言的数组收集哈希表实现的示例列表。如果有人能够详细了解它们的工作原理以及每个例子发生了什么,那也很好。

修改:

为什么不使用特定语言的内置哈希函数?

因为我们应该知道哈希表是如何工作的并且能够实现它们。这可能看起来不是一个非常重要的主题,但了解最常用的数据结构之一对我来说非常重要。如果这是成为编程的维基百科,那么这些是我将来到这里的一些类型的问题。我不是要在这里写一本CS书。我可以下载现成的算法Intro并阅读有关哈希表的章节并获取该类型的信息。更具体地说,我正在寻找的是代码示例。不仅对我来说,而且对于那些可能有一天会搜索类似信息并偶然发现此页面的人也是如此。

更具体一点:如果您来实现它们,并且无法使用内置函数,那么您将如何做?

您无需在此处输入代码。把它放在pastebin中,然后链接它。

9 个答案:

答案 0 :(得分:19)

哈希表一种数据结构,允许在恒定时间内查找项目。它通过散列值并将该值转换为数组中的偏移量来工作。哈希表的概念相当容易理解,但实现起来显然更难。我不是在这里粘贴整个哈希表,但这里是我几周前在C中制作的哈希表的一些片段......

创建哈希表的基础之一是具有良好的哈希函数。我在哈希表中使用了djb2哈希函数:

int ComputeHash(char* key)
{
    int hash = 5381;
    while (*key)
    hash = ((hash << 5) + hash) + *(key++);
    return hash % hashTable.totalBuckets;
}

然后是用于在表中创建和管理存储桶的实际代码本身

typedef struct HashTable{
    HashTable* nextEntry;
    char*      key;
    char*      value;
}HashBucket;

typedef struct HashTableEntry{
    int totalBuckets;       // Total number of buckets allocated for the hash table
    HashTable** hashBucketArray; // Pointer to array of buckets
}HashTableEntry;
HashTableEntry hashTable;

bool InitHashTable(int totalBuckets)
{
    if(totalBuckets > 0)
    {
        hashTable.totalBuckets = totalBuckets;
        hashTable.hashBucketArray = (HashTable**)malloc(totalBuckets * sizeof(HashTable));
        if(hashTable.hashBucketArray != NULL)
        {
            memset(hashTable.hashBucketArray, 0, sizeof(HashTable) * totalBuckets);
            return true;
        }
    }
    return false;
}

bool AddNode(char* key, char* value)
{
    int offset = ComputeHash(key);
    if(hashTable.hashBucketArray[offset] == NULL)
    {
        hashTable.hashBucketArray[offset] = NewNode(key, value);
        if(hashTable.hashBucketArray[offset] != NULL)
            return true;
    }

    else
    {
        if(AppendLinkedNode(hashTable.hashBucketArray[offset], key, value) != NULL)
            return true;
    }
    return false;
}

HashTable* NewNode(char* key, char* value)
{
    HashTable* tmpNode = (HashTable*)malloc(sizeof(HashTable));
    if(tmpNode != NULL)
    {
        tmpNode->nextEntry = NULL;
        tmpNode->key   = (char*)malloc(strlen(key));
        tmpNode->value = (char*)malloc(strlen(value));

        strcpy(tmpNode->key, key);
        strcpy(tmpNode->value, value);
    }
    return tmpNode;
}

AppendLinkedNode查找链接列表中的最后一个节点并向其添加新节点。

代码将像这样使用:

if(InitHashTable(100) == false) return -1;
AddNode("10", "TEN");

查找节点非常简单:

HashTable* FindNode(char* key)
{
    int offset = ComputeHash(key);
    HashTable* tmpNode = hashTable.hashBucketArray[offset];
    while(tmpNode != NULL)
    {
        if(strcmp(tmpNode->key, key) == 0)
            return tmpNode;
        tmpNode = tmpNode->nextEntry;
    }
    return NULL;
}

使用如下:

char* value = FindNode("10");

答案 1 :(得分:8)

&lt;中的Java实现60 LoC

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


public class HashTable {

    class KeyValuePair {

        Object key;

        Object value;

        public KeyValuePair(Object key, Object value) {
            this.key = key;
            this.value = value;
        }
    }

    private Object[] values;

    private int capacity;

    public HashTable(int capacity) {
        values = new Object[capacity];
        this.capacity = capacity;
    }

    private int hash(Object key) {
        return Math.abs(key.hashCode()) % capacity;
    }

    public void add(Object key, Object value) throws IllegalArgumentException {

        if (key == null || value == null)
            throw new IllegalArgumentException("key or value is null");

        int index = hash(key);

        List<KeyValuePair> list;
        if (values[index] == null) {
            list = new ArrayList<KeyValuePair>();
            values[index] = list;

        } else {
            // collision
            list = (List<KeyValuePair>) values[index];
        }

        list.add(new KeyValuePair(key, value));
    }

    public Object get(Object key) {
        List<KeyValuePair> list = (List<KeyValuePair>) values[hash(key)];
        if (list == null) {
            return null;
        }
        for (KeyValuePair kvp : list) {
            if (kvp.key.equals(key)) {
                return kvp.value;
            }
        }
        return null;
    }

    /**
     * Test
     */
    public static void main(String[] args) {

        HashTable ht = new HashTable(100);

        for (int i = 1; i <= 1000; i++) {
            ht.add("key" + i, "value" + i);
        }

        Random random = new Random();
        for (int i = 1; i <= 10; i++) {
            String key = "key" + random.nextInt(1000);
            System.out.println("ht.get(\"" + key + "\") = " + ht.get(key));
        }   
    }
}

答案 2 :(得分:7)

HashTable是一个非常简单的概念:它是一个数组,通过以下方案将键和值对放入(如关联数组):

哈希函数将一个(希望)未使用的索引的密钥哈希到数组中。然后将该值放入该特定索引处的数组中。

数据检索很容易,因为可以通过哈希函数计算数组的索引,因此查找是~O(1)。

当哈希函数将2个不同的键映射到同一个索引时会出现问题...有很多方法可以解决这个问题,我在这里不详述......

哈希表是快速存储和检索数据的基本方法,并且几乎在所有编程语言库中都“内置”。

答案 3 :(得分:3)

我一直在寻找一个完全可移植的C哈希表实现,并开始对如何自己实现它感兴趣。经过一番搜索,我发现: Julienne Walker's The Art of Hashing有一些关于散列和散列表的精彩教程。实现它们比我想象的要复杂一些,但这是一个很好的阅读。

答案 4 :(得分:1)

这是我用Java实现的哈希表的代码。遭受轻微的故障 - 键和值字段不一样。可能会在将来编辑它。

public class HashTable
{
    private LinkedList[] hashArr=new LinkedList[128];
    public static int HFunc(int key)
    {
        return key%128;
    }


    public boolean Put(Val V)
    {

        int hashval = HFunc(V.getKey());
        LinkedNode ln = new LinkedNode(V,null);
        hashArr[hashval].Insert(ln);
        System.out.println("Inserted!");
        return true;            
    }

    public boolean Find(Val V)
    {
        int hashval = HFunc(V.getKey());
        if (hashArr[hashval].getInitial()!=null && hashArr[hashval].search(V)==true)
        {
            System.out.println("Found!!");
            return true;
        }
        else
        {
            System.out.println("Not Found!!");
            return false;
        }

    }
    public boolean delete(Val v)
    {
        int hashval = HFunc(v.getKey());
        if (hashArr[hashval].getInitial()!=null && hashArr[hashval].delete(v)==true)
        {
            System.out.println("Deleted!!");
            return true;
        }
        else 
        {
            System.out.println("Could not be found. How can it be deleted?");
            return false;
        }
    }

    public HashTable()
    {
        for(int i=0; i<hashArr.length;i++)
            hashArr[i]=new LinkedList();
    }

}

class Val
{
    private int key;
    private int val;
    public int getKey()
    {
        return key;
    }
    public void setKey(int k)
    {
        this.key=k;
    }
    public int getVal()
    {
        return val;
    }
    public void setVal(int v)
    {
        this.val=v;
    }
    public Val(int key,int value)
    {
        this.key=key;
        this.val=value;
    }
    public boolean equals(Val v1)
    {
        if (v1.getVal()==this.val)
        {
            //System.out.println("hello im here");
            return true;
        }
        else 
            return false;
    }
}

class LinkedNode
{
    private LinkedNode next;
    private Val obj;
    public LinkedNode(Val v,LinkedNode next)
    {
        this.obj=v;
        this.next=next;
    }
    public LinkedNode()
    {
        this.obj=null;
        this.next=null;
    }
    public Val getObj()
    {
        return this.obj;    
    }
    public void setNext(LinkedNode ln)
    {
        this.next = ln;
    }

    public LinkedNode getNext()
    {
        return this.next;
    }
    public boolean equals(LinkedNode ln1, LinkedNode ln2)
    {
        if (ln1.getObj().equals(ln2.getObj()))
        {
            return true;
        }
        else 
            return false;

    }

}

class LinkedList
{
    private LinkedNode initial;
    public LinkedList()
    {
        this.initial=null;
    }
    public LinkedList(LinkedNode initial)
    {
        this.initial = initial;
    }
    public LinkedNode getInitial()
    {
        return this.initial;
    }
    public void Insert(LinkedNode ln)
    {
        LinkedNode temp = this.initial;
        this.initial = ln;
        ln.setNext(temp);
    }
    public boolean search(Val v)
    {
        if (this.initial==null)
            return false;
        else 
        {
            LinkedNode temp = this.initial;
            while (temp!=null)
            {
                //System.out.println("encountered one!");
                if (temp.getObj().equals(v))
                    return true;
                else 
                    temp=temp.getNext();
            }
            return false;
        }

    }
    public boolean delete(Val v)
    {
        if (this.initial==null)
            return false;
        else 
        {
            LinkedNode prev = this.initial;
            if (prev.getObj().equals(v))
            {
                this.initial = null;
                return true;
            }
            else
            {
                LinkedNode temp = this.initial.getNext();
            while (temp!=null)
            {
                if (temp.getObj().equals(v))
                {
                    prev.setNext(temp.getNext());
                    return true;
                }
                else
                {
                    prev=temp;
                    temp=temp.getNext();
                }
            }
            return false;
            }
        }
    }
}

答案 5 :(得分:0)

我认为你需要更具体一点。关于以下选项的哈希表有几种变体

  • 哈希表是固定大小还是动态?
  • 使用什么类型的哈希函数?
  • 调整哈希表的大小时是否有任何性能限制?

列表可以继续。这些约束中的每一个都可能导致任何语言的多种实现。

就个人而言,我只会使用以我选择的语言提供的内置哈希表。我甚至考虑实现自己的唯一原因是由于性能问题,即使这样,也很难击败大多数现有的实现。

答案 6 :(得分:0)

对于那些可能使用上述代码的人,请注意内存泄漏:

tmpNode->key   = (char*)malloc(strlen(key)+1);   //must have +1 for '\0'
tmpNode->value = (char*)malloc(strlen(value)+1); //must have +1
strcpy(tmpNode->key, key);
strcpy(tmpNode->value, value);

并完成代码:

HashNode* AppendLinkedNode(HashNode* ptr, char* key, char* value)
{
    //follow pointer till end
    while (ptr->nextEntry!=NULL)
    {
        if (strcmp(ptr->value,value)==0) return NULL;
        ptr=ptr->nextEntry;
    }
    ptr->nextEntry=newNode(key,value);
    return ptr->nextEntry;
}

答案 7 :(得分:0)

F#中的最小实现,作为从给定的键值对序列构建哈希表并返回一个函数,该函数在哈希表中搜索与给定键相关联的值:

> let hashtbl xs =
    let spine = [|for _ in xs -> ResizeArray()|]
    let f k = spine.[abs(k.GetHashCode()) % spine.Length]
    Seq.iter (fun (k, v) -> (f k).Add (k, v)) xs
    fun k -> Seq.pick (fun (k', v) -> if k=k' then Some v else None) (f k);;
val hashtbl : seq<'a * 'b> -> ('a -> 'b) when 'a : equality

答案 8 :(得分:-1)

我去了一些关于哈希的维基百科页面:http://en.wikipedia.org/wiki/Hash_table。在这里为哈希表设置代码似乎需要做很多工作,特别是因为我使用的大多数语言都已经内置了。为什么要在这里实现?这个东西真的属于语言库。

请详细说明您的预期解决方案应包括哪些内容:

  • 哈希函数
  • 可变桶数
  • 碰撞行为

还说明在这里收集它们的目的是什么。任何严肃的实现都很容易让人满意=这将导致很长的答案(可能每页长几页)。您可能也会诱使人们从库中复制代码......