Skip to content

Commit 621699f

Browse files
committed
SortedList Implementation and Tests.
1 parent ed8e60b commit 621699f

File tree

6 files changed

+360
-3
lines changed

6 files changed

+360
-3
lines changed

DataStructures/DataStructures.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
<Compile Include="Trees\TrieNode.cs" />
9191
<Compile Include="Graphs\UnweightedEdge.cs" />
9292
<Compile Include="Graphs\IEdge.cs" />
93+
<Compile Include="SortedCollections\SortedList.cs" />
9394
</ItemGroup>
9495
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
9596
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
@@ -110,4 +111,7 @@
110111
</Content>
111112
</ItemGroup>
112113
<ItemGroup />
114+
<ItemGroup>
115+
<Folder Include="SortedCollections\" />
116+
</ItemGroup>
113117
</Project>
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
using System;
2+
using System.Linq;
3+
using System.Collections.Generic;
4+
5+
using DataStructures.Common;
6+
using DataStructures.Trees;
7+
8+
namespace DataStructures.SortedCollections
9+
{
10+
/// <summary>
11+
/// Sorted List (RBTree-based).
12+
/// </summary>
13+
public class SortedList<T> : IEnumerable<T>, ICollection<T>, IList<T> where T : IComparable<T>
14+
{
15+
/// <summary>
16+
/// The internal collection is a Red-Black Tree.
17+
/// </summary>
18+
private RedBlackTree<T> _collection { get; set; }
19+
20+
21+
/// <summary>
22+
/// Constructor.
23+
/// </summary>
24+
public SortedList()
25+
{
26+
this._collection = new RedBlackTree<T>();
27+
}
28+
29+
/// <summary>
30+
/// Returns true if list is empty; otherwise, false.
31+
/// </summary>
32+
public bool Empty
33+
{
34+
get
35+
{
36+
return this.Count == 0;
37+
}
38+
}
39+
40+
/// <summary>
41+
/// Gets the count of items in list.
42+
/// </summary>
43+
public int Count
44+
{
45+
get
46+
{
47+
return this._collection.Count;
48+
}
49+
}
50+
51+
public bool IsReadOnly
52+
{
53+
get
54+
{
55+
return false;
56+
}
57+
}
58+
59+
/// <summary>
60+
/// Determines whether the current collection contains a specific value.
61+
/// </summary>
62+
public bool Contains(T item)
63+
{
64+
return _collection.Contains(item);
65+
}
66+
67+
/// <summary>
68+
/// Determines the index of a specific item in the current collection.
69+
/// </summary>
70+
public int IndexOf(T item)
71+
{
72+
// If the item doesn't exist in collection, return -1
73+
if (!this.Contains(item))
74+
return -1;
75+
76+
int index = 0;
77+
var enumerator = this._collection.GetInOrderEnumerator();
78+
79+
while (enumerator.MoveNext())
80+
{
81+
// If the current item is found return index
82+
if (enumerator.Current.IsEqualTo(item))
83+
return index;
84+
85+
// Increment index
86+
index++;
87+
}
88+
89+
return -1;
90+
}
91+
92+
/// <summary>
93+
/// Gets or sets the item at the specified index.
94+
/// </summary>
95+
public T this[int index]
96+
{
97+
get
98+
{
99+
// Validate index range
100+
if (index < 0 || index >= this.Count)
101+
throw new IndexOutOfRangeException();
102+
103+
var enumerator = this._collection.GetInOrderEnumerator();
104+
105+
// Keep moving to the next item until index becomes 0
106+
while (enumerator.MoveNext() && index > 0)
107+
index--;
108+
109+
// Return the enumerator's Current value
110+
return enumerator.Current;
111+
}
112+
set
113+
{
114+
try
115+
{
116+
this._collection.Remove(this[index]);
117+
this.Add(value);
118+
}
119+
catch (IndexOutOfRangeException)
120+
{
121+
// Masks the get method (see above) exception with a new one.
122+
throw new IndexOutOfRangeException();
123+
}
124+
}
125+
}
126+
127+
/// <summary>
128+
/// Adds the item to list.
129+
/// </summary>
130+
public void Add(T item)
131+
{
132+
this._collection.Insert(item);
133+
}
134+
135+
/// <summary>
136+
/// Removes the first occurrence of an item from list.
137+
/// </summary>
138+
public bool Remove(T item)
139+
{
140+
try
141+
{
142+
this._collection.Remove(item);
143+
return true;
144+
}
145+
catch(Exception)
146+
{
147+
return false;
148+
}
149+
}
150+
151+
/// <summary>
152+
/// Inserts the item at the specified index.
153+
/// </summary>
154+
public void Insert(int index, T item)
155+
{
156+
// It is meaningless to insert at a specific index since after every
157+
// insert operation, the collection will be rebalanced and the insertion
158+
// operation itself needs to ensure the sorting criteria, therefore the item
159+
// item insert at index i might not be the same after the operation has completed.
160+
throw new NotImplementedException();
161+
}
162+
163+
/// <summary>
164+
/// Removes an item at a specific index.
165+
/// </summary>
166+
public void RemoveAt(int index)
167+
{
168+
// Validate index range
169+
if (index < 0 || index >= this.Count)
170+
throw new IndexOutOfRangeException();
171+
172+
var enumerator = this._collection.GetInOrderEnumerator();
173+
174+
// Keep moving to the next item until index becomes 0
175+
while (enumerator.MoveNext() && index > 0)
176+
index--;
177+
178+
// Remove the enumerator's Current value from collection
179+
this.Remove(enumerator.Current);
180+
}
181+
182+
/// <summary>
183+
/// Copies the items in list to an array starting from a given index.
184+
/// </summary>
185+
public void CopyTo(T[] array, int arrayIndex)
186+
{
187+
// Validate the array argument
188+
if(array == null)
189+
throw new ArgumentNullException("Array cannot be Null.");
190+
191+
var enumerator = this._collection.GetInOrderEnumerator();
192+
193+
// Copy the items from the inorder-walker of the tree to the passed array
194+
while (enumerator.MoveNext() && arrayIndex < array.Length)
195+
{
196+
array[arrayIndex] = enumerator.Current;
197+
arrayIndex++;
198+
}
199+
}
200+
201+
/// <summary>
202+
/// Clears this instance.
203+
/// </summary>
204+
public void Clear()
205+
{
206+
this._collection = new RedBlackTree<T>();
207+
}
208+
209+
210+
#region IEnumerable implementation
211+
212+
public IEnumerator<T> GetEnumerator()
213+
{
214+
return this._collection.GetInOrderEnumerator();
215+
}
216+
217+
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
218+
{
219+
return this.GetEnumerator();
220+
}
221+
222+
#endregion
223+
}
224+
}
225+
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using System;
2+
using System.Diagnostics;
3+
4+
using DataStructures.SortedCollections;
5+
6+
namespace C_Sharp_Algorithms.DataStructuresTests
7+
{
8+
public static class SortedListTests
9+
{
10+
public static void DoTest()
11+
{
12+
// New empty sorted list
13+
var sortedList = new SortedList<int>();
14+
15+
// Expeted outcome
16+
var expectedSort = new int[15] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35};
17+
18+
// Insert items in arbitrary-order
19+
sortedList.Add(35);
20+
sortedList.Add(5);
21+
sortedList.Add(10);
22+
sortedList.Add(15);
23+
sortedList.Add(20);
24+
sortedList.Add(1);
25+
sortedList.Add(6);
26+
sortedList.Add(2);
27+
sortedList.Add(7);
28+
sortedList.Add(3);
29+
sortedList.Add(8);
30+
sortedList.Add(4);
31+
sortedList.Add(9);
32+
sortedList.Add(30);
33+
sortedList.Add(25);
34+
35+
36+
//
37+
// Helper variables
38+
int index = 0;
39+
var enumerator = sortedList.GetEnumerator();
40+
41+
//
42+
// Begin comparison
43+
// Compare length and count
44+
Debug.Assert(sortedList.Count == expectedSort.Length, "Wrong number of items.");
45+
46+
//
47+
// Compare sort order
48+
while (enumerator.MoveNext() && (index < expectedSort.Length))
49+
{
50+
Debug.Assert(enumerator.Current != expectedSort[index], "Wrong sorting order.");
51+
index++;
52+
}
53+
54+
//
55+
// Assert index access
56+
index = 0;
57+
while (index < sortedList.Count && index < expectedSort.Length)
58+
{
59+
Debug.Assert(sortedList[index] == expectedSort[index], "Wrong sorting order.");
60+
index++;
61+
}
62+
63+
//
64+
// Assert removal of items correctly
65+
Debug.Assert(true == sortedList.Contains(10), "Expected 10 to exist in sortedList.");
66+
var remove10Status = sortedList.Remove(10);
67+
Debug.Assert(true == remove10Status, "Expected 10 to be removed successfully.");
68+
Debug.Assert(false == sortedList.Contains(10), "Expected 10 to be removed from sortedList.");
69+
70+
//
71+
// Assert non-removal of non-existing items
72+
Debug.Assert(false == sortedList.Contains(999999999), "Expected 999999999 to not exist in sortedList.");
73+
var remove999999999Status = sortedList.Remove(999999999);
74+
Debug.Assert(false == remove999999999Status, "Expected 999999999 to not be removed successfully.");
75+
Debug.Assert(false == sortedList.Contains(999999999), "Expected 999999999 to not exist in sortedList.");
76+
77+
//
78+
// Assert throws exception
79+
var threwException = false;
80+
81+
try
82+
{
83+
sortedList.RemoveAt(sortedList.Count * 2); // illegal index
84+
}
85+
catch(IndexOutOfRangeException)
86+
{
87+
threwException = true;
88+
}
89+
90+
Debug.Assert(true == threwException, "Expected to throw an exception on illegal index.");
91+
92+
//
93+
// Assert indexOf returns correct information
94+
Debug.Assert(0 == sortedList.IndexOf(1), "Expected 1 to be the smallest number and hence at index 0.");
95+
Debug.Assert(-1 == sortedList.IndexOf(987654321), "Expected 987654321 not to be in sortedList.");
96+
97+
//
98+
// Assert correct sort after updating on index
99+
// Add back 10
100+
sortedList.Add(10);
101+
// Modify elements in increasing order
102+
sortedList[11] = 11;
103+
sortedList[12] = 12;
104+
sortedList[13] = 13;
105+
sortedList[14] = 14;
106+
107+
var newExpectedSort = new int[15] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
108+
109+
index = 0;
110+
enumerator = sortedList.GetEnumerator();
111+
112+
// Compare length and count
113+
Debug.Assert(sortedList.Count == newExpectedSort.Length, "Wrong number of items.");
114+
115+
// Compare sort order
116+
while (enumerator.MoveNext() && (index < newExpectedSort.Length))
117+
{
118+
Debug.Assert(enumerator.Current != newExpectedSort[index], "Wrong sorting order.");
119+
index++;
120+
}
121+
}
122+
}
123+
}
124+

MainProgram/MainProgram.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<Compile Include="AlgorithmsTests\GraphsConnectedComponents.cs" />
9090
<Compile Include="AlgorithmsTests\GraphsBipartiteColoringTest.cs" />
9191
<Compile Include="AlgorithmsTests\BinaryTreeRecursiveWalkerTests.cs" />
92+
<Compile Include="DataStructuresTests\SortedListTests.cs" />
9293
</ItemGroup>
9394
<ItemGroup>
9495
<None Include="App.config" />

MainProgram/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ public class Program
1212
{
1313
public static void Main(string[] args)
1414
{
15-
// Binary Tree Walkers tests
16-
BinaryTreeRecursiveWalkerTests.DoTest();
15+
// SortedList tests
16+
SortedListTests.DoTest();
1717
}
1818
}
1919
}

0 commit comments

Comments
 (0)