diff --git a/DataStructures/Lists/SkipList.cs b/DataStructures/Lists/SkipList.cs index 57822aca..dbb57bc0 100644 --- a/DataStructures/Lists/SkipList.cs +++ b/DataStructures/Lists/SkipList.cs @@ -28,22 +28,6 @@ public class SkipList : ICollection, IEnumerable where T : IComparable< private readonly int MaxLevel = 32; private readonly double Probability = 0.5; - - /// - /// Private helper. Used in Add method. - /// - /// - private int _getNextLevel() - { - int lvl = 0; - - while (_randomizer.NextDouble() < Probability && lvl <= _currentMaxLevel && lvl < MaxLevel) - ++lvl; - - return lvl; - } - - /// /// CONSTRUCTOR /// @@ -53,9 +37,6 @@ public SkipList() _currentMaxLevel = 1; _randomizer = new Random(); _firstNode = new SkipListNode(default(T), MaxLevel); - - for (int i = 0; i < MaxLevel; ++i) - _firstNode.Forwards[i] = _firstNode; } @@ -94,12 +75,15 @@ public int Level /// /// Access elements by index /// - public T this[int index] + public T this[T item] { get { - // TODO: - throw new NotImplementedException(); + return Find(item, out var result) ? result : throw new KeyNotFoundException(); + } + set + { + Add(item); } } @@ -111,38 +95,43 @@ public void Add(T item) var current = _firstNode; var toBeUpdated = new SkipListNode[MaxLevel]; + // Get nodes for updated for (int i = _currentMaxLevel - 1; i >= 0; --i) { - while (current.Forwards[i] != _firstNode && current.Forwards[i].Value.IsLessThan(item)) + while (current.Forwards[i] != null && current.Forwards[i].Value.IsLessThan(item)) + { current = current.Forwards[i]; + } toBeUpdated[i] = current; } + // Desired position to insert key current = current.Forwards[0]; - // Get the next node level, and update list level if required. - int lvl = _getNextLevel(); - if (lvl > _currentMaxLevel) + if(current == null || !current.Value.Equals(item)) { - for (int i = _currentMaxLevel; i < lvl; ++i) - toBeUpdated[i] = _firstNode; - - _currentMaxLevel = lvl; + // Get the next node level, and update list level if required. + int lvl = _getNextLevel(); + if (lvl > _currentMaxLevel) + { + for (int i = _currentMaxLevel; i < lvl; ++i) + toBeUpdated[i] = _firstNode; + + _currentMaxLevel = lvl; + } + + var newNode = new SkipListNode(item, lvl); + + // Insert the new node into the skip list + for (int i = 0; i < lvl; ++i) + { + newNode.Forwards[i] = toBeUpdated[i].Forwards[i]; + toBeUpdated[i].Forwards[i] = newNode; + } + + ++_count; } - - // New node - var newNode = new SkipListNode(item, lvl); - - // Insert the new node into the skip list - for (int i = 0; i < lvl; ++i) - { - newNode.Forwards[i] = toBeUpdated[i].Forwards[i]; - toBeUpdated[i].Forwards[i] = newNode; - } - - // Increment the count - ++_count; } /// @@ -150,8 +139,7 @@ public void Add(T item) /// public bool Remove(T item) { - T deleted; - return Remove(item, out deleted); + return Remove(item, out var _); } /// @@ -167,7 +155,7 @@ public bool Remove(T item, out T deleted) // Mark all nodes as toBeUpdated. for (int i = _currentMaxLevel - 1; i >= 0; --i) { - while (current.Forwards[i] != _firstNode && current.Forwards[i].Value.IsLessThan(item)) + while (current.Forwards[i] != null && current.Forwards[i].Value.IsLessThan(item)) current = current.Forwards[i]; toBeUpdated[i] = current; @@ -176,7 +164,7 @@ public bool Remove(T item, out T deleted) current = current.Forwards[0]; // Return default value of T if the item was not found - if (current.Value.IsEqualTo(item) == false) + if (current == null || current.Value.IsEqualTo(item) == false) { deleted = default(T); return false; @@ -185,15 +173,21 @@ public bool Remove(T item, out T deleted) // We know that the node is in the list. // Unlink it from the levels where it exists. for (int i = 0; i < _currentMaxLevel; ++i) - if (toBeUpdated[i].Forwards[i] == current) - toBeUpdated[i].Forwards[i] = current.Forwards[i]; + { + if (toBeUpdated[i].Forwards[i] != current) + { + break; + } + + toBeUpdated[i].Forwards[i] = current.Forwards[i]; + } // Decrement the count --_count; // Check to see if we've deleted the highest-level node // Decrement level - while (_currentMaxLevel > 1 && _firstNode.Forwards[_currentMaxLevel - 1] == _firstNode) + while (_currentMaxLevel > 1 && _firstNode.Forwards[_currentMaxLevel - 1] == null) --_currentMaxLevel; // Assign the deleted output parameter to the node.Value @@ -206,8 +200,7 @@ public bool Remove(T item, out T deleted) /// public bool Contains(T item) { - T itemOut; - return Find(item, out itemOut); + return Find(item, out var _); } /// @@ -215,23 +208,41 @@ public bool Contains(T item) /// public bool Find(T item, out T result) { + result = default(T); + var current = _firstNode; + // If find null element then check first element after first node + if (item == null) + { + current = current.Forwards[0]; + return current != null && current.Value == null; + } + + // Skip null element (in first postion) if contain + if (!IsEmpty && current.Forwards[0].Value == null) + { + current = current.Forwards[0]; + } + // Walk after all the nodes that have values less than the node we are looking for for (int i = _currentMaxLevel - 1; i >= 0; --i) - while (current.Forwards[i] != _firstNode && current.Forwards[i].Value.IsLessThan(item)) + { + while (current.Forwards[i] != null && current.Forwards[i].Value.IsLessThan(item)) + { current = current.Forwards[i]; + } + } current = current.Forwards[0]; // Return true if we found the element; false otherwise - if (current.Value.IsEqualTo(item)) + if (current != null && current.Value.IsEqualTo(item)) { result = current.Value; return true; } - result = default(T); return false; } @@ -302,7 +313,7 @@ public bool TryPeek(out T result) public IEnumerator GetEnumerator() { var node = _firstNode; - while (node.Forwards[0] != null && node.Forwards[0] != _firstNode) + while (node.Forwards[0] != null && node.Forwards[0] != null) { node = node.Forwards[0]; yield return node.Value; @@ -361,12 +372,24 @@ public void Clear() _currentMaxLevel = 1; _randomizer = new Random(); _firstNode = new SkipListNode(default(T), MaxLevel); - - for (int i = 0; i < MaxLevel; ++i) - _firstNode.Forwards[i] = _firstNode; } #endregion + /// + /// Private helper. Used in Add method. + /// + /// + private int _getNextLevel() + { + int lvl = 1; + + while (_randomizer.NextDouble() < Probability && lvl <= _currentMaxLevel && lvl < MaxLevel) + { + ++lvl; + } + + return lvl; + } } } diff --git a/DataStructures/Lists/SkipListNode.cs b/DataStructures/Lists/SkipListNode.cs index 8188c747..b847b821 100644 --- a/DataStructures/Lists/SkipListNode.cs +++ b/DataStructures/Lists/SkipListNode.cs @@ -4,12 +4,6 @@ namespace DataStructures.Lists { public class SkipListNode : IComparable> where T : IComparable { - /// - /// Instance variables - /// - private T _value; - private SkipListNode[] _forwards; - /// /// CONSTRUCTORS /// @@ -25,20 +19,12 @@ public SkipListNode(T value, int level) /// /// Get and set node's value /// - public virtual T Value - { - get { return this._value; } - private set { this._value = value; } - } + public virtual T Value { get; private set; } /// /// Get and set node's forwards links /// - public virtual SkipListNode[] Forwards - { - get { return this._forwards; } - private set { this._forwards = value; } - } + public virtual SkipListNode[] Forwards { get; private set; } /// /// Return level of node. @@ -55,7 +41,7 @@ public int CompareTo(SkipListNode other) { if (other == null) return -1; - + return this.Value.CompareTo(other.Value); } } diff --git a/UnitTest/DataStructuresTests/SkipListTest.cs b/UnitTest/DataStructuresTests/SkipListTest.cs index 97ab6378..cbbe3e15 100644 --- a/UnitTest/DataStructuresTests/SkipListTest.cs +++ b/UnitTest/DataStructuresTests/SkipListTest.cs @@ -6,30 +6,193 @@ namespace UnitTest.DataStructuresTests public static class SkipListTest { [Fact] - public static void DoTest() + public static void Initialization_ListIsEmpty() { var skipList = new SkipList(); - for (int i = 100; i >= 50; --i) - skipList.Add(i); + Assert.True(skipList.Count == 0); + Assert.True(skipList.IsEmpty); + Assert.DoesNotContain(0, skipList); + } - for (int i = 0; i <= 35; ++i) - skipList.Add(i); + [Fact] + public static void InitializationWithReferencyTypeAsGeneric_ListIsEmpty() + { + var skipList = new SkipList(); - for (int i = -15; i <= 0; ++i) - skipList.Add(i); + Assert.True(skipList.Count == 0); + Assert.True(skipList.IsEmpty); + Assert.DoesNotContain(null, skipList); + Assert.DoesNotContain(string.Empty, skipList); + } - for (int i = -15; i >= -35; --i) - skipList.Add(i); + [Fact] + public static void Add_NullElement_ListContainNullElement() + { + var skipList = new SkipList(); - Assert.True(skipList.Count == 124); + skipList.Add(null); - skipList.Clear(); + Assert.True(skipList.Count == 1); + Assert.Contains(null, skipList); + } + + [Fact] + public static void Add_NullElementAfterNonNull_ListContainNullElement() + { + var skipList = new SkipList(); + + skipList.Add("1"); + skipList.Add(null); + + Assert.True(skipList.Count == 2); + Assert.Contains(null, skipList); + Assert.Contains("1", skipList); + } + + [Theory] + [InlineData(10)] + [InlineData(-10)] + public static void Add_OneElement_ListContainOneElement(int testValue) + { + var skipList = new SkipList(); + + skipList.Add(testValue); + + Assert.True(skipList.Count == 1); + Assert.Contains(testValue, skipList); + } + + [Fact] + public static void Add_OneElementTwice_ListContainOneElement() + { + var skipList = new SkipList(); + + skipList.Add(10); + skipList.Add(10); + + Assert.True(skipList.Count == 1); + Assert.Contains(10, skipList); + } + + [Fact] + public static void Add_DefaultElement_ListContainOneElement() + { + var skipList = new SkipList(); - for (int i = 100; i >= 0; --i) + skipList.Add(default(int)); + + Assert.True(skipList.Count == 1); + Assert.Contains(0, skipList); + } + + [Fact] + public static void Add_SomeElements_ListContainTheseElements() + { + var skipList = new SkipList(); + skipList.Add(0); + skipList.Add(-1); + skipList.Add(1); + skipList.Add(2); + + Assert.True(skipList.Count == 4); + int checkValue = -1; + foreach(var item in skipList) + { + Assert.Equal(checkValue, item); + checkValue++; + } + } + + [Fact] + public static void Remove_RemoveOneElement_ListIsEmpty() + { + var skipList = new SkipList(); + skipList.Add(-10); + + skipList.Remove(-10); + + Assert.True(skipList.Count == 0); + Assert.DoesNotContain(0, skipList); + Assert.DoesNotContain(-10, skipList); + } + + [Fact] + public static void Remove_RemoveDefaultElement_DoNoRemoveAndListIsEmptyAndAddingIsWork() + { + var skipList = new SkipList(); + + Assert.DoesNotContain(0, skipList); + skipList.Remove(default(int)); + + Assert.True(skipList.Count == 0); + Assert.DoesNotContain(0, skipList); + + skipList.Add(10); + Assert.True(skipList.Count == 1); + Assert.Contains(10, skipList); + } + + [Fact] + public static void Remove_RemoveSomeElements_ListContainOneElement() + { + var skipList = new SkipList(); + skipList.Add(0); + skipList.Add(-1); + skipList.Add(2); + skipList.Add(3); + + skipList.Remove(2); + skipList.Remove(-1); + skipList.Remove(3); + + Assert.True(skipList.Count == 1); + Assert.Contains(0, skipList); + } + + [Fact] + public static void AddAndRemove_BatchOfElements_ListIsEmpty() + { + var skipList = new SkipList(); + + for (int i = 100; i > 50; --i) + { skipList.Add(i); + } + + Assert.True(skipList.Count == 50); + for (int i = 100; i > 50; --i) + { + Assert.Contains(i, skipList); + } + + for (int i = 100; i > 50; --i) + { + skipList.Remove(i); + } + + for (int i = 100; i > 50; --i) + { + Assert.DoesNotContain(i, skipList); + } + Assert.True(skipList.Count == 0); + } + + [Fact] + public static void ClearList() + { + var skipList = new SkipList(); + + skipList.Add(1); + skipList.Add(2); + skipList.Add(3); + skipList.Clear(); - Assert.True(skipList.Count == 101); + Assert.True(skipList.Count == 0); + Assert.True(skipList.IsEmpty); + Assert.DoesNotContain(1, skipList); + Assert.DoesNotContain(2, skipList); + Assert.DoesNotContain(3, skipList); } } }