Отсортированная структура данных

Я ищу отсортированную структуру данных с ключами в .Net 4.0, поддерживающую следующие функции:

  1. Создайте структуру за O(n log n) время
  2. Получить предмет по ключу за O(log n) времени
  3. Найдите наименьший элемент в коллекции, больший или равный заданному аргументу за O(log n) времени (скорее всего, мы введем его с помощью double)
  4. Найдите самый большой элемент меньше заданного аргумента в O(log n)
  5. Для данного элемента в коллекции получить следующий и предыдущий элемент
  6. Ключи должны быть уникальными в коллекции

Я бегло взглянул на SortedDictionary и SortedList, но, похоже, они не предоставляют (3) и (4) из приведенного выше списка. SortedDictionary не поддерживает (5), и я не уверен, поддерживает ли SortedList (6).

К сожалению, мы ограничены .Net4.


person Grzenio    schedule 19.03.2014    source источник
comment
Звучит так, будто вам нужно построить такую ​​структуру данных самостоятельно.   -  person D.R.    schedule 19.03.2014
comment
Я понятия не имею, есть ли у них такая структура, но смотрели ли вы C5 Collections? Там много хорошего, у них тоже есть пакет NuGet PM> Install-Package C5   -  person Binary Worrier    schedule 19.03.2014
comment
Является ли коллекция просто ключом от Double? Найти самый большой элемент меньшего размера, вы имеете в виду поиск по ключу?   -  person paparazzo    schedule 19.03.2014
comment
@Блам, да, это правильно. double будет ключом, но у нас все еще есть связанные с ним значения.   -  person Grzenio    schedule 19.03.2014
comment
Я понимаю, что это, вероятно, не новость для вас, но самый большой предмет меньше, а самый маленький предмет больше - это сложная часть. Невозможно сделать это с помощью hashbuckets. Для O (log n), я думаю, это должно быть похоже на поиск по индексу. Вы смотрели базу данных в памяти, как Redis.io?   -  person paparazzo    schedule 19.03.2014
comment
@Blam, это легко реализовать в виде древовидной структуры данных. Я действительно хотел избежать необходимости реализовывать и тестировать его самостоятельно.   -  person Grzenio    schedule 20.03.2014


Ответы (2)


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

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

person Servy    schedule 19.03.2014

Я создал для вас отсортированный словарь. Я надеюсь, что это соответствует вашим потребностям.

public class MyDictionary<TKey, TItem> : IDictionary<TKey, TItem>
    where TKey : IComparable<TKey>
    where TItem : IEquatable<TItem>
{
    private readonly List<TKey> keys;
    private readonly List<TItem> items;
    private readonly ReadOnlyCollection<TKey> roKeys;
    private readonly ReadOnlyCollection<TItem> roItems;

    public MyDictionary()
    {
        keys = new List<TKey>();
        items = new List<TItem>();
        roKeys = new ReadOnlyCollection<TKey>(keys);
        roItems = new ReadOnlyCollection<TItem>(items);
    }

    public MyDictionary(int capacity)
    {
        keys = new List<TKey>(capacity);
        items = new List<TItem>(capacity);
        roKeys = new ReadOnlyCollection<TKey>(keys);
        roItems = new ReadOnlyCollection<TItem>(items);
    }

    public MyDictionary(TKey[] keys, TItem[] items)
    {
        if (keys == null)
            throw new ArgumentNullException("keys");
        if (items == null)
            throw new ArgumentNullException("items");
        if (keys.Length != items.Length)
            throw new ArgumentException("Arrays lengths must be equal.");

        TKey[] keysCopy = new TKey[keys.Length];
        keys.CopyTo(keysCopy, 0);
        TItem[] itemsCopy = new TItem[items.Length];
        items.CopyTo(itemsCopy, 0);

        Array.Sort(keysCopy, itemsCopy);

        this.keys = new List<TKey>(keysCopy);
        this.items = new List<TItem>(itemsCopy);
        roKeys = new ReadOnlyCollection<TKey>(keys);
        roItems = new ReadOnlyCollection<TItem>(items);
    }

    public int BinarySearch(TKey key)
    {
        return keys.BinarySearch(key);
    }

    public bool ContainsKey(TKey key)
    {
        return BinarySearch(key) >= 0;
    }

    public void Add(TKey key, TItem item)
    {
        int index = BinarySearch(key);
        if (index >= 0)
            throw new ArgumentException(String.Format("The key {0} already exists.", key), "key");
        index = ~index;
        keys.Insert(index, key);
        items.Insert(index, item);
    }

    public void Add(KeyValuePair<TKey, TItem> item)
    {
        Add(item.Key, item.Value);
    }

    public bool Remove(TKey key)
    {
        int index = BinarySearch(key);
        if (index < 0)
            return false;
        keys.RemoveAt(index);
        items.RemoveAt(index);
        return true;
    }

    public bool Remove(KeyValuePair<TKey, TItem> item)
    {
        int index = BinarySearch(item.Key);
        if (index < 0)
            return false;
        index = ~index;
        keys.RemoveAt(index);
        items.RemoveAt(index);
        return true;
    }

    public bool Contains(KeyValuePair<TKey, TItem> item)
    {
        int index = BinarySearch(item.Key);
        if (index < 0)
            return false;
        index = ~index;
        return items[index].Equals(item.Value);
    }

    public bool TryGetValue(TKey key, out TItem value)
    {
        int index = BinarySearch(key);
        if (index < 0)
        {
            value = default(TItem);
            return false;
        }
        value = items[index];
        return true;
    }

    public TItem this[TKey key]
    {
        get
        {
            int index = BinarySearch(key);
            if (index < 0)
                throw new ArgumentException(String.Format("The key {0} not found.", key), "key");
            return items[index];
        }
        set
        {
            int index = BinarySearch(key);
            if (index < 0)
                throw new ArgumentException(String.Format("The key {0} not found.", key), "key");
            items[index] = value;
        }
    }

    public ICollection<TKey> Keys
    {
        get { return roKeys; }
    }

    public ICollection<TItem> Values
    {
        get { return roItems; }
    }

    public IEnumerator<KeyValuePair<TKey, TItem>> GetEnumerator()
    {
        return keys.Select((t, i) => new KeyValuePair<TKey, TItem>(t, items[i])).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Clear()
    {
        keys.Clear();
        items.Clear();
    }

    public void CopyTo(KeyValuePair<TKey, TItem>[] array, int arrayIndex)
    {
        Array.Copy(keys.Select((t, i) => new KeyValuePair<TKey, TItem>(t, items[i])).ToArray(), 0, array, arrayIndex, Count);
    }

    public int Count
    {
        get { return keys.Count; } 
    }

    public int Capacity
    {
        get { return keys.Capacity; }
        set
        {
            if (value < 0)
                throw new ArgumentOutOfRangeException("value");
            keys.Capacity = value;
            items.Capacity = value;
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public int GetSmallerOrEqualIndex(TKey key)
    {
        int index = BinarySearch(key);
        if (index >= 0)
            return index;
        index = ~index;
        return index - 1;
    }

    public int GetGreaterOrEqualIndex(TKey key)
    {
        int index = BinarySearch(key);
        if (index >= 0)
            return index;
        index = ~index;
        return index;
    }

    public KeyValuePair<TKey, TItem> GetItem(int index)
    {
        return new KeyValuePair<TKey, TItem>(keys[index], items[index]);
    }
}

Требования:

  1. НЕ доволен (работаю над этим). На данный момент инициализация через конструктор MyDictionary(TKey[] keys, TItem[] items) — это в среднем операция O(n log n), в худшем случае — операция O(n ^ 2). Добавление отдельного элемента — операция O(n).
  2. Довольный.
  3. Удовлетворено (метод GetGreaterOrEqualIndex).
  4. Доволен (метод GetSmallerOrEqualIndex).
  5. Не выполняется напрямую, но выполняется для индекса элемента, если я правильно понял «данный элемент».
  6. Довольный.
person Dmitry    schedule 19.03.2014
comment
№1 не устраивает. Добавление N элементов в эту структуру данных — операция O(n^2). - person Servy; 19.03.2014
comment
Почему? Добавление 1 элемента — операция O(log N). Добавление N элементов - O (N log N). Я что-то пропустил? - person Dmitry; 19.03.2014
comment
Добавление 1 элемента в список — это операция O(n), а не операция O(log(n)). Поиск индекса того, куда его добавить, составляет O (log (n)), но на самом деле его добавление, когда у вас есть индекс, составляет O (n). - person Servy; 19.03.2014
comment
MSDN: если Count меньше Capacity, этот метод является операцией O(1). Но вы правы, я должен добавить этот момент к ответу. - person Dmitry; 19.03.2014
comment
Это верно только при добавлении в конец. Вы не добавляете в конец списка. Вы добавляете в середину списка, что является операцией O(n), даже если новое выделение буфера не требуется. - person Servy; 19.03.2014
comment
Виноват. Ты прав. Я попытаюсь изменить код, чтобы удовлетворить # 1. Спасибо. - person Dmitry; 19.03.2014
comment
Это изначально неизбежная ситуация. Вам нужно будет использовать древовидную структуру, чтобы удовлетворить все требования, в основном полностью переписывая все это. - person Servy; 19.03.2014