programming-examples/c-sharp/Others/SkipList.cs

255 lines
8.9 KiB
C#
Raw Normal View History

2019-11-15 12:59:38 +01:00
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace DataStructures.SkipListSpace
{
/// <summary>
/// Key value pair implemented in Skip List
/// </summary>
/// <typeparam name="TKey">Key in key value pair</typeparam>
/// <typeparam name="TValue">Value in key value pair</typeparam>
[Serializable]
public partial class SkipList<TKey, TValue> : IEnumerable<TValue>, ICollection<TValue>
where TKey : IComparable<TKey>
{
private readonly object lockObject = new object();
private readonly int maxLevel;
private int level;
private SkipNode<TKey, TValue> header;
private readonly NullSkipNode<TKey, TValue> NullNode;
private double probability;
private const double Probability = 0.5;
private readonly Random random = new Random();
public int Count { get; private set; }
public bool IsSynchronized
{
get { return false; }
}
public object SyncRoot
{
get { return lockObject; }
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(probability > 0);
Contract.Invariant(probability < 1);
Contract.Invariant(maxLevel > 0);
}
private SkipList(double probable, int maxLevel)
{
Contract.Requires<ArgumentOutOfRangeException>(maxLevel > 0);
Contract.Requires<ArgumentOutOfRangeException>(probable < 1);
Contract.Requires<ArgumentOutOfRangeException>(probable > 0);
this.probability = probable;
this.maxLevel = maxLevel;
level = 0;
header = new SkipNode<TKey, TValue>(maxLevel);
NullNode = new NullSkipNode<TKey, TValue>(maxLevel);
//Initially all the forward links node of dummy header is NullNode
for (int i = 0; i < maxLevel; i++)
{
header.Links[i] = NullNode;
}
}
public static SkipList<TKey, TValue> CreateInstance(long maxNodes)
{
return new SkipList<TKey, TValue>(Probability, (int)(Math.Ceiling(Math.Log(maxNodes) /
Math.Log(1 / Probability) - 1)));
}
private int GetRandomLevel()
{
Contract.Ensures(Contract.Result<int>() >= 0);
Contract.Ensures(Contract.Result<int>() < maxLevel);
int newLevel = 0;
double ran = random.NextDouble();
while ((newLevel < maxLevel) && (ran < probability))
{
newLevel++;
}
return newLevel;
}
/// <summary>
/// Insert key value pair in list
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Insert(TKey key, TValue value)
{
Contract.Requires<ArgumentNullException>(key != null, "key");
SkipNode<TKey, TValue>[] update = new SkipNode<TKey, TValue>[maxLevel];
//Start search for each level from dummy header node
SkipNode<TKey, TValue> cursor = header;
//Start from current max initialized header
for (int i = level; i >= 0; i--)
{
//Find the node which is previous node to current code in any level
while (cursor.Links[i].Key.CompareTo(key) == -1)
{
cursor = cursor.Links[i];
}
//Put the predecessor node link in the level of update list
update[i] = cursor;
}
//Check level 0 next whether we already have that element
cursor = cursor.Links[0];
if (cursor.Key.CompareTo(key) == 0)
{
//Assign new value to corresponding key
cursor.Value = value;
}
else
{
//If this is new node, then
//Find random level for insertion
int newLevel = GetRandomLevel();
//New node level is greater then current level
//Update intermediate nodes
if (newLevel > level)
{
//This is a special case, where dummy header links aren't initialized yet
for (int i = level + 1; i < newLevel; i++)
{
//These levels of header aren't initialized yet
update[i] = header;
}
//update current level, until this level from bottom header link is initialized
level = newLevel;
}
//New node which will be inserted into new level, also needs newLevel number of forward edges
cursor = new SkipNode<TKey, TValue>(newLevel, key, value);
//Insert the node
for (int i = 0; i < newLevel; i++)
{
//Update edges of all the levels below to that level
//New node is set to successor node its predecessor
cursor.Links[i] = update[i].Links[i];
//Update forward edges of predecessor to currently inserted node
update[i].Links[i] = cursor;
}
Count++;
}
}
/// <summary>
/// Delete from skip list if element exists
/// </summary>
/// <param name="key">Key to be deleted</param>
public void Delete(TKey key)
{
Contract.Requires<ArgumentNullException>(key != null);
SkipNode<TKey, TValue>[] update = new SkipNode<TKey, TValue>[maxLevel + 1];
SkipNode<TKey, TValue> cursor = header;
for (int i = level; i >= 0; i--)
{
while (cursor.Links[i].Key.CompareTo(key) == -1)
{
cursor = cursor.Links[i];
}
update[i] = cursor;
}
cursor = cursor.Links[0];
//Check is this the element we want to delete?
if (cursor.Key.CompareTo(key) == 0)
{
for (int i = 0; i < level; i++)
{
//If next element is our to be deleted element
//Check prev node point to next node
if (update[i].Links[i] == cursor)
{
update[i].Links[i] = cursor.Links[i];
}
}
//Readjust levels of initialized links of dummy header
while ((level > 0) && (header.Links[level].Key.Equals(NullNode)))
{
level--;
}
Count--;
}
//Element isn't in the list, just return
}
/// <summary>
/// Search for values, given a key
/// </summary>
/// <param name="key">Key to be searched</param>
/// <returns>Value otherwise type default</returns>
public TValue Search(TKey key)
{
Contract.Requires<ArgumentNullException>(key != null);
SkipNode<TKey, TValue> cursor = header;
for (int i = 0; i < level; i++)
{
SkipNode<TKey, TValue> nextElement = cursor.Links[i];
while (nextElement.Key.CompareTo(key) == -1)
{
cursor = nextElement;
nextElement = cursor.Links[i];
}
}
//Got previous element of current key in the list
//So next element must our searched element if list contains that element
cursor = cursor.Links[0];
if (cursor.Key.Equals(key))
{
return cursor.Value;
}
else
{
//Element is not in the list, return default
return default(TValue);
}
}
public void CopyTo(Array array, int index)
{
Contract.Requires<ArgumentNullException>(array != null, "array");
Contract.Requires<ArgumentOutOfRangeException>(index >= 0, "index");
Contract.Requires<ArgumentException>(array.Length < Count);
int i = index;
var node = header.Links[0];
while (node != null)
{
array.SetValue(node.Value, i);
node = node.Links[0];
i++;
}
}
public IEnumerator<TValue> GetEnumerator()
{
var node = header.Links[0];
while (node != null)
{
yield return node.Value;
node = node.Links[0];
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}