Почему SortedDictionary<K, V>.GetEnumerator O (log n), а SortedSet<t>.GetEnumerator O (1)?

Из SortedSet<T>.GetEnumerator документации:

Этот метод является операцией O (1)

Из SortedDictionary<K, V>.GetEnumerator документации:

Этот метод является операцией O (log n), где n - это число.

Могут ли оба утверждения быть правдой, учитывая, что SortedDictionary<K, V> внутренне реализован как SortedSet<KeyValuePair<K, V>? Я проверил код GetEnumerator класса SortedDictionary - он напрямую использует перечислитель SortedSet. Я заметил реализацию перечислителя SortedSet, и мне показалось, что он имеет O (log n) характеристику (вот код):

public SortedSet<T>.Enumerator GetEnumerator()
{
  return new SortedSet<T>.Enumerator(this);
}

//which calls this constructor:
internal Enumerator(SortedSet<T> set)
{
  this.tree = set;
  this.tree.VersionCheck();
  this.version = this.tree.version;
  this.stack = new Stack<SortedSet<T>.Node>(2 * SortedSet<T>.log2(set.Count + 1));
  this.current = (SortedSet<T>.Node) null;
  this.reverse = false;
  this.siInfo = (SerializationInfo) null;
  this.Intialize();
}

private void Intialize()
{
  this.current = (SortedSet<T>.Node) null;
  SortedSet<T>.Node node1 = this.tree.root;
  while (node1 != null)
  {
    SortedSet<T>.Node node2 = this.reverse ? node1.Right : node1.Left;
    SortedSet<T>.Node node3 = this.reverse ? node1.Left : node1.Right;
    if (this.tree.IsWithinRange(node1.Item))
    {
      this.stack.Push(node1);
      node1 = node2;
    }
    else
      node1 = node2 == null || !this.tree.IsWithinRange(node2.Item) ? node3 : node2;
  }
}

Разве это не означает, что документация неверна и SortedSet<T>.GetEnumerator - это O (log n)? Ничего особенного в производительности вызова GetEnumerator, просто для гарантии того, что я правильно понимаю.

Ответ 1

Я полностью согласен с вами.

SortedSet использует внутреннюю структуру красного черного дерева, которая гарантированно будет сбалансированной (Википедия; Красно-черные деревья, Р. Седжвик, Принстонский университет).
Поэтому высота ограничена 2 * log2(n + 1). Даже кодовый комментарий в SortedSet.cs указывает на это, и размер стека перечислителя устанавливается соответствующим образом.

Цикл while, который подготавливает стек, равен O (log n) как при инициализации, так и при обработке (MoveNext) перечислителя.

Отзыв о документации MSDN со ссылкой на данное обсуждение.

Обновление:

К сегодняшнему дню Microsoft наконец-то обновила документацию. Для версии 4.0 он все еще заявляет, что это операция O (1). Хотя я сомневаюсь в этом, я могу оставить все как есть.