Быстрый способ найти первый неиспользованный ключ в SortedDictionary?

Если у меня есть SortedDictionary<int, object>Какой самый быстрый способ найти самый низкий ключ, который в настоящее время не используется? Очевидно, мы могли бы перебрать счетчик i из 0->int.MaxValue и сбежать, если !Keys.Contains(i), но это было бы очень медленно, если бы нам не повезло, и первый запасной ключ оказался в начале последовательности ключей. Может быть, даже другой класс.NET уже делает это для нас?

4 ответа

Решение

Так что, если я вас правильно понимаю, ключи могут быть где угодно 0 в int.MaxValue, В этом случае вы должны найти первую "дыру" в последовательности клавиш.

Это должно сделать работу эффективно:

public static int GetFirstUnusedKey<TValue>(SortedDictionary<int, TValue> dict)
{
    if (dict.Comparer != Comparer<int>.Default)
        throw new NotSupportedException("Unsupported comparer");

    using (var enumerator = dict.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            return 0;

        var nextKeyInSequence = enumerator.Current.Key + 1;

        if (nextKeyInSequence < 1)
            throw new InvalidOperationException("The dictionary contains keys less than 0");

        if (nextKeyInSequence != 1)
            return 0;

        while (enumerator.MoveNext())
        {
            var key = enumerator.Current.Key;
            if (key > nextKeyInSequence)
                return nextKeyInSequence;

            ++nextKeyInSequence;
        }

        return nextKeyInSequence;
    }
}

Я добавил пару проверок, чтобы убедиться, что предварительные условия верны.

Почему не бинарный поиск по ключам?

Вы можете использовать метод ElementAt() для определения значения ключа в индексе. Если значение ключа больше индекса, выполните поиск в левом под-словаре или выберите правое и продолжайте в том же духе, пока не найдете индекс, по которому вы наблюдаете первое различие между индексом и значением в индексе.

Не ищите неиспользованный ключ, начинающийся с 0.

Вместо этого итерируйте используемые ключи, начиная с самого низкого (они отсортированы).

Либо первый>0, затем 0 - ваш самый низкий неиспользованный.

или между двумя клавишами есть пробел, тогда +1 - это то, что вы искали.

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

var sd = new SortedDictionary<int, object>();

sd.Add(1, 1);
sd.Add(2, 1);
sd.Add(4, 1);
sd.Add(5, 1);

var e = Enumerable.Range(1, 5).Except(sd.Keys).First();

е = 3 в этом случае, как и предполагалось. Я ожидаю, что есть лучшие решения, хотя.

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