namespace System.Collections.Generic; /// /// Skip list implementation. /// /// Provided type. [Serializable] public sealed class SkipList : ICollection where T : IComparable { #region Fields [Serializable] private class NodeSet { public TKey Key; public int Level; public NodeSet[] Next; public NodeSet(TKey key, int level) { this.Key = key; this.Level = level; this.Next = new NodeSet[level + 1]; } } private int count; private readonly Random random; private readonly NodeSet head; private readonly NodeSet end; private readonly int maxLevel = 10; private int level; #endregion #region Properties /// /// Number of elements in the list. /// public int Count { get => this.count; } /// /// Specifies if the collection can be modified. /// public bool IsReadOnly { get; set; } #endregion #region Constructors /// /// Creates a new instance of SkipList collection. /// /// Maximum level of the skip list. public SkipList(int maxLevel = 10) { this.maxLevel = maxLevel; this.random = new Random(); this.head = new NodeSet(default!, maxLevel); this.end = this.head; for (var i = 0; i <= maxLevel; i++) { this.head.Next[i] = this.end; } } #endregion #region Public Methods /// /// Adds an item to the collection. /// /// Item to be added. public void Add(T item) { var curNode = this.head; var newLevel = 0; while (this.random.Next(0, 2) > 0 && newLevel < this.maxLevel) { newLevel++; } if (newLevel > this.level) { this.level = newLevel; } var newNode = new NodeSet(item, newLevel); for (var i = 0; i <= newLevel; i++) { if (i > curNode.Level) { curNode = this.head; } while (curNode.Next[i] != this.end && curNode.Next[i].Key.CompareTo(item) < 0) { curNode = curNode.Next[i]; } newNode.Next[i] = curNode.Next[i]; curNode.Next[i] = newNode; } this.count++; } /// /// Removes provided item from the collection. /// /// Item to be removed. /// True if removal was successful. public bool Remove(T item) { var removed = false; var curNode = this.head; for (var i = 0; i <= this.maxLevel; i++) { if (i > curNode.Level) { curNode = this.head; } while (curNode.Next[i] != this.end && curNode.Next[i].Key.CompareTo(item) < 0) { curNode = curNode.Next[i]; } if (curNode.Next[i].Key.CompareTo(item) == 0) //Item is present on this level { curNode.Next[i] = curNode.Next[i].Next[i]; removed = true; } else { break; } } if (removed) { this.count--; return true; } else { return false; } } /// /// Clears the collection. /// public void Clear() { for (var i = 0; i < this.maxLevel; i++) { this.head.Next[i] = this.end; } this.count = 0; } /// /// Checks if item is present in the collection. /// /// Item to be checked. /// True if item is present in the collection. public bool Contains(T item) { if (this.Find(item) != null) { return true; } return false; } /// /// Copies the skip list contents onto the provided array. /// /// Array to hold the values from the list. /// Index to start insertion in the array. public void CopyTo(T[] array, int arrayIndex) { var node = this.head.Next[0]; while (node != this.end) { array[arrayIndex] = node.Key; arrayIndex++; node = node.Next[0]; } } /// /// Copies the elements from the collection into an array. /// /// Array filled with elements from the collection. public T[] ToArray() { var array = new T[this.count]; var index = 0; var curNode = this.head.Next[0]; while (curNode != this.end) { array[index] = curNode.Key; index++; curNode = curNode.Next[0]; } return array; } /// /// Enumerator that iterates over the collection. /// /// public IEnumerator GetEnumerator() { var curNode = this.head.Next[0]; while (curNode != this.end) { yield return curNode.Key; curNode = curNode.Next[0]; } } #endregion #region Private Methods IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } private NodeSet Find(T key) { var curNode = this.head; for (var i = this.level; i >= 0; i--) { while (curNode.Next[i] != this.end) { if (curNode.Next[i].Key.CompareTo(key) > 0) { break; } else if (curNode.Next[i].Key.CompareTo(key) == 0) { return curNode.Next[i]; } curNode = curNode.Next[i]; } } curNode = curNode.Next[0]; if (curNode != this.end && curNode.Key.CompareTo(key) == 0) { return curNode; } return default!; } #endregion }