Как бы вы реализовали хеш-таблицу на языке x?

Суть этого вопроса - собрать список примеров реализации хеш-таблиц с использованием массивов на разных языках. Также было бы неплохо, если бы кто-нибудь мог дать довольно подробный обзор того, как они работают, и что происходит с каждым примером.

Редактировать:

Почему бы просто не использовать встроенные хэш-функции на вашем конкретном языке?

Потому что мы должны знать, как работают хеш-таблицы, и уметь их реализовывать. Это может показаться не очень важной темой, но мне кажется очень важным знать, как работает одна из наиболее часто используемых структур данных. Если это должно стать Википедией программирования, то вот некоторые из вопросов, по которым я приду сюда. Я не ищу здесь для написания книги по CS. Я мог бы взять «Введение в алгоритмы» с полки, прочитать главу о хэш-таблицах и получить такую ​​информацию. В частности, я ищу примеры кода. Не только для меня в частности, но и для других, которые, возможно, однажды будут искать похожую информацию и наткнуться на эту страницу.

Чтобы быть более конкретным: если бы вы использовали имел для их реализации и не могли бы использовать встроенные функции, как бы вы это сделали?

Код здесь вводить не нужно. Поместите его в pastebin и просто свяжите.

Идея состоит в том, чтобы органически стал Википедией программирования. Не навязывайте вопросы; это пахнет выращиванием кармы.

xanadont 18.07.2009 18:36
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
34
1
27 781
9

Ответы 9

Я думаю, вам нужно быть более конкретным. Существует несколько вариантов хэш-таблиц в отношении следующих параметров.

  • Хеш-таблица фиксированного размера или динамическая?
  • Какой тип хэш-функции используется?
  • Есть ли ограничения производительности при изменении размера хеш-таблицы?

Список можно продолжать и продолжать. Каждое из этих ограничений может привести к множеству реализаций на любом языке.

Лично я бы просто использовал встроенную хеш-таблицу, доступную на моем выбранном языке. Единственная причина, по которой я бы даже подумал о реализации своей собственной, - это проблемы с производительностью, и даже в этом случае трудно превзойти большинство существующих реализаций.

Я пошел и прочитал несколько страниц в Википедии о хешировании: http://en.wikipedia.org/wiki/Hash_table. Похоже, что разместить здесь код для хэш-таблицы - это большая работа, тем более, что большинство языков, которые я использую, уже имеют их встроенные. Зачем вам нужны здесь реализации? Этот материал действительно принадлежит языковой библиотеке.

Пожалуйста, уточните, какие решения должны включать в себя ваши ожидаемые:

  • хеш-функция
  • переменное количество ведер
  • поведение при столкновении

Также укажите, какова цель их сбора. Любая серьезная реализация легко окажется утомительной - это приведет к очень длинным ответам (возможно, по несколько страниц каждый). Вы также можете побуждать людей копировать код из библиотеки ...

HashTable - это действительно простая концепция: это массив, в который помещаются пары ключ и значение (как ассоциативный массив) по следующей схеме:

Хеш-функция хеширует ключ (надеюсь) неиспользуемого индекса в массив. затем значение помещается в массив по этому конкретному индексу.

Поиск данных прост, поскольку индекс в массиве может быть вычислен с помощью хэш-функции, поэтому поиск выполняется ~ O (1).

Проблема возникает, когда хеш-функция отображает 2 разных ключа на один и тот же индекс ... есть много способов справиться с этим, которые я не буду здесь подробно описывать ...

Хеш-таблицы - это фундаментальный способ быстрого хранения и извлечения данных, и они «встроены» почти во все библиотеки языков программирования.

Как это связано с вопросом?

tymtam 21.06.2012 17:39

Согласен, это может не ответить на вопрос, и что вопрос может быть неконструктивным, но, по крайней мере, я наконец понимаю, почему хеш-таблицы так быстро извлекают значения! Очень обидно, что до сих пор я думал, что это вуду.

Rob Grant 05.01.2015 11:32

Хэш-таблица - это структура данных, которая позволяет выполнять поиск элементов в постоянное время. Он работает путем хеширования значения и преобразования этого значения в смещение в массиве. Концепцию хеш-таблицы довольно легко понять, но реализовать ее, очевидно, сложнее. Я не вставляю сюда всю хеш-таблицу, но вот несколько фрагментов хеш-таблицы, которые я сделал на 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");

Я не уверен, что вы могли бы использовать char* value = FindNode("10");, поскольку FindNode возвращает HashTable*. Итак, вы бы посмотрели на что-то вроде: char* value = FindNode("10")->value;

Jimmy Huch 17.01.2014 09:25

Я искал полностью переносимую реализацию хеш-таблицы C и заинтересовался, как реализовать ее самостоятельно. Немного поискав, я обнаружил: Жюльен Уокер Искусство хеширования, в котором есть несколько отличных руководств по хешированию и хеш-таблицам. Их реализация немного сложнее, чем я думал, но это было отличное чтение.

Для тех, кто может использовать приведенный выше код, обратите внимание на утечку памяти:

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

Вот мой код для хеш-таблицы, реализованный на 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;
            }
        }
    }
}

Минимальная реализация в 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

В строке 3 приведенного выше фрагмента требуется небольшое исправление: k.GetHashCode() может возвращать отрицательное число (например, "Key with negative hashcode".GetHashCode() возвращает -648767821), что, в свою очередь, вызовет исключение System.IndexOutOfRangeException при вычислении номера сегмента для такого ключа с помощью функция f.

Gene Belitski 02.09.2011 15:16

Реализация Java в

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

Не знаете, что делает list.add(new KeyValuePair(key, value)); в конце функции add?

Rob Grant 05.01.2015 11:33

Я действительно не понимаю этого вопроса. Он добавляет отображение ключа -> значения во внутренний массив, чтобы его можно было получить позже при вызове get ().

Korbi 14.01.2015 18:46

Извините, я неправильно подумал. Я думал, что он был определен в этом методе, поэтому он будет просто сборщиком мусора, но вы используете его как дескриптор элемента в переменной-члене values.

Rob Grant 15.01.2015 15:52

Другие вопросы по теме