Сортировка хеш-таблицы (карта, словарь) дизайн структуры данных - PullRequest
7 голосов
/ 05 января 2010

Вот описание структуры данных:

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

Например:

  • Я вызываю метод put 1 000 000 раз.
  • Я вызываю метод sort.
  • Я вызываю метод put еще 100 раз.
  • Я вызываю метод sort.

Второй раз, когда я вызываю метод sort, должна быть гораздо более быстрая операция, так как структура карты не сильно изменилась. Обратите внимание, что карта не должна поддерживать отсортированный порядок между вызовами на sort.

Я понимаю, что это может быть невозможно, но я надеюсь на операции O (1) get, put и remove. Что-то вроде TreeMap обеспечивает гарантированные затраты времени O (log (n)) для этих операций, но всегда поддерживает отсортированный порядок (без sort метода).

Так каков дизайн этой структуры данных?

Редактировать 1 - возврат записей top-K

Хотя я бы с удовольствием выслушал ответ на общий случай выше, мой вариант использования стал более конкретным: мне не нужно все сортировать; только верхние элементы K.

Структура данных для эффективного возврата top-K записей хеш-таблицы (карта, словарь)

Спасибо!

Ответы [ 6 ]

8 голосов
/ 05 января 2010

Для операций «O (1) get, put и remove» вам, по сути, нужен поиск O (1), который подразумевает хеш-функцию (как вы знаете), но требования хорошей хеш-функции часто нарушают требование быть легко отсортированным. (Если бы у вас была хеш-таблица, в которой соседние значения сопоставлены с одним и тем же сегментом, она выродилась бы в O (N) для большого количества общих данных, что является худшим случаем, когда обычно требуется избегать хеш-функции.)

Я могу думать о том, как добраться до вас на 90%. Установите хеш-таблицу вместе с параллельным индексом, который отсортирован. Индекс имеет чистую часть (заказанную) и грязную часть (неупорядоченную). Индекс будет сопоставлять ключи со значениями (или ссылками на значения, хранящиеся в хеш-таблице - в зависимости от того, что вам подходит с точки зрения производительности или использования памяти). Когда вы добавляете в хеш-таблицу, новая запись помещается в конец грязного списка. При удалении из хеш-таблицы запись обнуляется / удаляется из чистых и грязных частей индекса. Вы можете отсортировать индекс, который сортирует только грязные записи, а затем объединить их в уже отсортированную «чистую» часть индекса. И, очевидно, вы можете перебрать индекс.

Насколько я вижу, это дает вам O (1) везде, кроме операции удаления, и все еще довольно просто реализовать со стандартными контейнерами (по крайней мере, как предусмотрено C ++, Java или Python). Это также дает вам условие «вторая сортировка дешевле», поскольку нужно только отсортировать грязные записи индекса, а затем позволить вам выполнить слияние O (N). Стоимость всего этого, очевидно, заключается в дополнительной памяти для индекса и дополнительной косвенности при его использовании.

4 голосов
/ 27 января 2010

Почему именно вам нужна функция sort ()?
Что вам, возможно, нужно и нужно, это красно-черное дерево.

http://en.wikipedia.org/wiki/Red-black_tree

Эти деревья автоматически сортируют ваш ввод по компаратору, который вы даете. Они сложны, но имеют отличные O (n) характеристики. Соедините записи дерева в качестве ключа с хешем карта как словарь, и вы получите свою структуру данных.

В Java это реализовано как TreeMap как экземпляр SortedMap.

1 голос
/ 20 января 2010

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

Вот реализация, которая сортирует записи в списке массивов, а не переносит их в древовидную карту. Я думаю, что алгоритм сортировки, используемый Collection.sort, отлично сработает, объединяя новые записи в уже отсортированную часть.

public class SortaSortedMap<K extends Comparable<K>,V> implements Map<K,V> {

    private LinkedHashMap<K,V> innerMap;

    public SortaSortedMap() {
        this.innerMap = new LinkedHashMap<K,V>();
    }

    public SortaSortedMap(Map<K,V> map) {
        this.innerMap = new LinkedHashMap<K,V>(map);
    }

    public Collection<V> values() {
        return innerMap.values();
    }

    public int size() {
        return innerMap.size();
    }

    public V remove(Object key) {
        return innerMap.remove(key);
    }

    public V put(K key, V value) {
        return innerMap.put(key, value);
    }

    public Set<K> keySet() {
        return innerMap.keySet();
    }

    public boolean isEmpty() {
        return innerMap.isEmpty();
    }

    public Set<Entry<K, V>> entrySet() {
        return innerMap.entrySet();
    }

    public boolean containsKey(Object key) {
        return innerMap.containsKey(key);
    }

    public V get(Object key) {
        return innerMap.get(key);
    }

    public boolean containsValue(Object value) {
        return innerMap.containsValue(value);
    }

    public void clear() {
        innerMap.clear();
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        innerMap.putAll(m);
    }

    public void sort() {
        List<Map.Entry<K,V>> entries = new ArrayList<Map.Entry<K,V>>(innerMap.entrySet());
        Collections.sort(entries, new KeyComparator());
        LinkedHashMap<K,V> newMap = new LinkedHashMap<K,V>();
        for (Map.Entry<K,V> e: entries) {
            newMap.put(e.getKey(), e.getValue());
        }
        innerMap = newMap;
    }

    private class KeyComparator implements Comparator<Map.Entry<K,V>> {

        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return o1.getKey().compareTo(o2.getKey());
        }

    }

}
1 голос
/ 20 января 2010

Упорядоченный словарь

Последние версии Python (2.7, 3.1) имеют «упорядоченные словари», которые звучат так, как вы описываете.

Официальная реализация Python "упорядоченный словарь" основана на предыдущих сторонних реализациях, как описано в PEP 372 .

Ссылки:

1 голос
/ 05 января 2010

Я не знаю, есть ли имя, но вы можете сохранить текущий индекс каждого элемента в хэше.

То есть у вас есть HashMap< Object, Pair( Integer, Object ) > а List<Object> объекты

Когда вы put, добавьте в конец или конец списка и вставьте в хеш-карту со своими данными и индексом вставки. Это O(1).

Когда вы get извлекаете хэш-карту и игнорируете индекс. Это O(1).

Когда вы remove, вы тянете с карты. Возьмите указатель и удалите из списка. Это O(1)

Когда вы sort, просто сортируйте список. Обновите индексы на карте во время сортировки или обновите после завершения сортировки. Это не влияет на сортировку O(nlgn), так как это линейный шаг. O(nlgn + n) == O(nlgn)

0 голосов
/ 05 января 2010

Мне неизвестна классификация структуры данных с таким точным поведением, по крайней мере, не в коллекциях Java (или из класса нелинейных структур данных). Возможно, вы сможете реализовать его, и отныне он будет известен как RudigerMap.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...