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
}