Вы не против потратить O (2n) памяти? Вы можете использовать Очередь <> в сочетании со Словарём <,>. Очередь будет обрабатывать операции очереди и удаления из очереди, а словарь будет обеспечивать уникальные записи. Простой класс-обертка может объединить эти два, и он даст вам O (log n) очереди и время ожидания.
Пример:
public class SetQueue<T>
{
private readonly Dictionary<T, bool> duplicates = new Dictionary<T, bool>();
private readonly Queue<T> queue = new Queue<T>();
public bool Enqueue(T item)
{
if (!duplicates.ContainsKey(item))
{
duplicates[item] = true;
queue.Enqueue(item);
return true;
}
return false;
}
public T Dequeue()
{
if (queue.Count >0)
{
var item = queue.Dequeue();
if (!duplicates.ContainsKey(item))
throw new InvalidOperationException("The dictionary should have contained an item");
else
duplicates.Remove(item);
return item;
}
throw new InvalidOperationException("Can't dequeue on an empty queue.");
}
}
Вставка в эту пользовательскую структуру данных проверяет, содержит ли словарь элемент. Эта операция использует метод ContainsKey, который является операцией O (log n). Если элемент уже содержался в структуре данных, метод завершается. Если элемент не содержится, то элемент будет вставлен в очередь, что является постоянной операцией O (1). Он также будет добавлен в словарь. Когда количество словаря меньше емкости, это приблизится к константе, а также к времени вставки O (1). Следовательно, общее время очереди будет O (log n).
То же самое относится и к методу удаления очереди.
Это решение в основном совпадает со встроенной структурой данных OrderedDictionary, однако, поскольку в этом решении используется универсальный тип, нет никаких накладных расходов на упаковку / распаковку в его операциях, что делает его значительно быстрее.