简体   繁体   中英

Sorted data structure

I am looking for a sorted keyed data structure in .Net 4.0 supporting the following functionality:

  1. Create the structure in O(n log n) time
  2. Get the item by key in O(log n) time
  3. Find the smallest item in the collection greater or equal to a given argument in O(log n) time (we will key it using double most probably)
  4. Find the biggest item smaller than a given argument in O(log n)
  5. For a given item in the collection, get the next and previous item
  6. Keys need to be unique in the collection

I took a quick look at the SortedDictionary and SortedList , but they don't seem to provide (3) and (4) from the list above. SortedDictionary doesn't seem to support (5), and I am not sure if SortedList supports (6).

We are limited to .Net4 unfortunately.

You're going to need to write your own collection. Conceptually what you want appears to be a tree based structure, which is how SortedDictionary is implemented. The underlying structure has the potential for all of these tasks, the .NET implementation simply doesn't expose them all, nor does it provide access to enough of the underlying tools to accomplish those goals, forcing you to start from scratch.

Fortunately, building such tree based structures is a common task for introductory programmers, so as a result you'll find plenty of open source 3rd party implementations to look through, either that will accomplish your goals, or as a starting place. You could also consider grabbing the source of SortedDictionary and re-compiling your own version, if you want.

I've created a sorted dictionary for you. I hope it meets your needs.

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

The requirements:

  1. NOT Satisfied (I'm working on it). At the moment, initializing via MyDictionary(TKey[] keys, TItem[] items) constructor is on average an O(n log n) operation, in the worst case it is an O(n ^ 2) operation. Adding an individual item is an O(n) operation.
  2. Satisfied.
  3. Satisfied ( GetGreaterOrEqualIndex method).
  4. Satisfied ( GetSmallerOrEqualIndex method).
  5. Not satisfied directly, but satisfied for the item's index, if I understood "given item" correctly.
  6. Satisfied.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM