Skip to content

Commit f9f450c

Browse files
Add Circular Linked List Data Structure (#476)
1 parent 93febae commit f9f450c

File tree

4 files changed

+360
-0
lines changed

4 files changed

+360
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
using System;
2+
using DataStructures.LinkedList.CircularLinkedList;
3+
using NUnit.Framework;
4+
5+
namespace DataStructures.Tests.LinkedList;
6+
7+
[TestFixture]
8+
public static class CircularLinkedListTests
9+
{
10+
[Test]
11+
public static void TestInsertAtBeginning()
12+
{
13+
var cll = new CircularLinkedList<int>();
14+
cll.InsertAtBeginning(10);
15+
cll.InsertAtBeginning(20);
16+
cll.InsertAtBeginning(30);
17+
18+
Assert.That("30 20 10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
19+
}
20+
21+
[Test]
22+
public static void TestInsertAtEnd()
23+
{
24+
var cll = new CircularLinkedList<int>();
25+
cll.InsertAtEnd(10);
26+
cll.InsertAtEnd(20);
27+
cll.InsertAtEnd(30);
28+
29+
Assert.That("10 20 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
30+
}
31+
32+
[Test]
33+
public static void TestInsertAfter()
34+
{
35+
var cll = new CircularLinkedList<int>();
36+
cll.InsertAtEnd(10);
37+
cll.InsertAtEnd(20);
38+
cll.InsertAtEnd(30);
39+
cll.InsertAfter(20, 25);
40+
41+
Assert.That("10 20 25 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
42+
}
43+
44+
[Test]
45+
public static void TestInsertAtBeginningInEmptyList()
46+
{
47+
var cll = new CircularLinkedList<int>();
48+
cll.InsertAtBeginning(10);
49+
50+
Assert.That("10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
51+
}
52+
53+
[Test]
54+
public static void TestInsertAtEndInEmptyList()
55+
{
56+
var cll = new CircularLinkedList<int>();
57+
cll.InsertAtEnd(10);
58+
59+
Assert.That("10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
60+
}
61+
62+
[Test]
63+
public static void TestInsertAfterInEmptyList()
64+
{
65+
var cll = new CircularLinkedList<int>();
66+
var ex = Assert.Throws<InvalidOperationException>(() => cll.InsertAfter(10, 20));
67+
68+
Assert.That(ex!.Message, Is.EqualTo("List is empty."));
69+
}
70+
71+
[Test]
72+
public static void TestInsertAfterSpecificNode()
73+
{
74+
var cll = new CircularLinkedList<int>();
75+
cll.InsertAtEnd(10);
76+
cll.InsertAtEnd(20);
77+
cll.InsertAtEnd(30);
78+
cll.InsertAfter(20, 25); // Insert after node with value 20
79+
80+
Assert.That("10 20 25 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
81+
}
82+
83+
[Test]
84+
public static void TestInsertAfterOnNonExistingValue()
85+
{
86+
var cll = new CircularLinkedList<int>();
87+
cll.InsertAtEnd(10);
88+
cll.InsertAfter(99, 25); // 99 does not exist
89+
90+
Assert.That("10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
91+
}
92+
93+
[Test]
94+
public static void TestDeleteNode()
95+
{
96+
var cll = new CircularLinkedList<int>();
97+
cll.InsertAtEnd(10);
98+
cll.InsertAtEnd(20);
99+
cll.InsertAtEnd(30);
100+
cll.DeleteNode(20);
101+
102+
Assert.That("10 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
103+
}
104+
105+
[Test]
106+
public static void TestDeleteOnlyNode()
107+
{
108+
var cll = new CircularLinkedList<int>();
109+
cll.InsertAtBeginning(10);
110+
cll.DeleteNode(10);
111+
112+
Assert.That(cll.IsEmpty(), Is.EqualTo(true));
113+
}
114+
115+
[Test]
116+
public static void TestDeleteHeadNode()
117+
{
118+
var cll = new CircularLinkedList<int>();
119+
cll.InsertAtEnd(10);
120+
cll.InsertAtEnd(20);
121+
cll.InsertAtEnd(30);
122+
cll.DeleteNode(10);
123+
124+
Assert.That("20 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
125+
}
126+
127+
[Test]
128+
public static void TestDeleteTailNode()
129+
{
130+
var cll = new CircularLinkedList<int>();
131+
cll.InsertAtEnd(10);
132+
cll.InsertAtEnd(20);
133+
cll.InsertAtEnd(30);
134+
cll.DeleteNode(30);
135+
136+
Assert.That("10 20", Is.EqualTo(GetDisplayOutput(cll).Trim()));
137+
}
138+
139+
[Test]
140+
public static void TestDeleteFromEmptyList()
141+
{
142+
var cll = new CircularLinkedList<int>();
143+
var ex = Assert.Throws<InvalidOperationException>(() => cll.DeleteNode(10));
144+
145+
Assert.That(ex!.Message, Is.EqualTo("List is empty."));
146+
}
147+
148+
[Test]
149+
public static void TestDeleteNonExistentNode()
150+
{
151+
var cll = new CircularLinkedList<int>();
152+
cll.InsertAtEnd(10);
153+
cll.InsertAtEnd(20);
154+
cll.InsertAtEnd(30);
155+
cll.DeleteNode(40); // Attempting to delete a node that doesn't exist
156+
157+
Assert.That("10 20 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
158+
}
159+
160+
private static string GetDisplayOutput<T>(CircularLinkedList<T> list)
161+
{
162+
var head = list.GetHead();
163+
if (head == null)
164+
{
165+
return string.Empty;
166+
}
167+
168+
var current = head;
169+
var result = new System.Text.StringBuilder();
170+
171+
do
172+
{
173+
result.Append(current!.Data + " ");
174+
current = current.Next;
175+
}
176+
while (current != head);
177+
178+
return result.ToString().Trim();
179+
}
180+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System;
2+
3+
namespace DataStructures.LinkedList.CircularLinkedList
4+
{
5+
/// <summary>
6+
/// CircularLinkedList.
7+
/// @author Mohit Singh. <a href="https://github.com/mohit-gogitter">mohit-gogitter</a>
8+
/// </summary>
9+
/// <typeparam name="T">The generic type parameter.</typeparam>
10+
public class CircularLinkedList<T>
11+
{
12+
/// <summary>
13+
/// Points to the last node in the Circular Linked List.
14+
/// </summary>
15+
private CircularLinkedListNode<T>? tail;
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="CircularLinkedList{T}"/> class.
19+
/// </summary>
20+
public CircularLinkedList()
21+
{
22+
tail = null;
23+
}
24+
25+
/// <summary>
26+
/// Gets the head node (tail.Next) of the Circular Linked List.
27+
/// </summary>
28+
public CircularLinkedListNode<T>? GetHead()
29+
{
30+
return tail?.Next;
31+
}
32+
33+
/// <summary>
34+
/// Determines whether the Circular Linked List is empty.
35+
/// </summary>
36+
/// <returns>True if the list is empty; otherwise, false.</returns>
37+
public bool IsEmpty()
38+
{
39+
return tail == null;
40+
}
41+
42+
/// <summary>
43+
/// Inserts a new node at the beginning of the Circular Linked List.
44+
/// </summary>
45+
/// <param name="data">The data to insert into the new node.</param>
46+
public void InsertAtBeginning(T data)
47+
{
48+
var newNode = new CircularLinkedListNode<T>(data);
49+
if (IsEmpty())
50+
{
51+
tail = newNode;
52+
tail.Next = tail;
53+
}
54+
else
55+
{
56+
newNode.Next = tail!.Next;
57+
tail.Next = newNode;
58+
}
59+
}
60+
61+
/// <summary>
62+
/// Inserts a new node at the end of the Circular Linked List.
63+
/// </summary>
64+
/// <param name="data">The data to insert into the new node.</param>
65+
public void InsertAtEnd(T data)
66+
{
67+
var newNode = new CircularLinkedListNode<T>(data);
68+
if (IsEmpty())
69+
{
70+
tail = newNode;
71+
tail.Next = tail;
72+
}
73+
else
74+
{
75+
newNode.Next = tail!.Next;
76+
tail.Next = newNode;
77+
tail = newNode;
78+
}
79+
}
80+
81+
/// <summary>
82+
/// Inserts a new node after a specific value in the list.
83+
/// </summary>
84+
/// <param name="value">The value to insert the node after.</param>
85+
/// <param name="data">The data to insert into the new node.</param>
86+
public void InsertAfter(T value, T data)
87+
{
88+
if (IsEmpty())
89+
{
90+
throw new InvalidOperationException("List is empty.");
91+
}
92+
93+
var current = tail!.Next;
94+
do
95+
{
96+
if (current!.Data!.Equals(value))
97+
{
98+
var newNode = new CircularLinkedListNode<T>(data);
99+
newNode.Next = current.Next;
100+
current.Next = newNode;
101+
102+
return;
103+
}
104+
105+
current = current.Next;
106+
}
107+
while (current != tail.Next);
108+
}
109+
110+
/// <summary>
111+
/// Deletes a node with a specific value from the list.
112+
/// </summary>
113+
/// <param name="value">The value of the node to delete.</param>
114+
public void DeleteNode(T value)
115+
{
116+
if (IsEmpty())
117+
{
118+
throw new InvalidOperationException("List is empty.");
119+
}
120+
121+
var current = tail!.Next;
122+
var previous = tail;
123+
124+
do
125+
{
126+
if (current!.Data!.Equals(value))
127+
{
128+
if (current == tail && current.Next == tail)
129+
{
130+
tail = null;
131+
}
132+
else if (current == tail)
133+
{
134+
previous!.Next = tail.Next;
135+
tail = previous;
136+
}
137+
else if (current == tail.Next)
138+
{
139+
tail.Next = current.Next;
140+
}
141+
else
142+
{
143+
previous!.Next = current.Next;
144+
}
145+
146+
return;
147+
}
148+
149+
previous = current;
150+
current = current.Next;
151+
}
152+
while (current != tail!.Next);
153+
}
154+
}
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace DataStructures.LinkedList.CircularLinkedList
2+
{
3+
/// <summary>
4+
/// Represents a node in the Circular Linked List.
5+
/// Each node contains generic data and a reference to the next node.
6+
/// </summary>
7+
/// <typeparam name="T">The type of the data stored in the node.</typeparam>
8+
/// <remarks>
9+
/// Initializes a new instance of the <see cref="CircularLinkedListNode{T}"/> class.
10+
/// </remarks>
11+
/// <param name="data">The data to be stored in the node.</param>
12+
public class CircularLinkedListNode<T>(T data)
13+
{
14+
/// <summary>
15+
/// Gets or sets the data for the node.
16+
/// </summary>
17+
public T Data { get; set; } = data;
18+
19+
/// <summary>
20+
/// Gets or sets the reference to the next node in the list.
21+
/// </summary>
22+
public CircularLinkedListNode<T>? Next { get; set; }
23+
}
24+
}

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ find more than one implementation for the same objective but using different alg
270270
* [Singly Linked List](./DataStructures/LinkedList/SinglyLinkedList/SinglyLinkedList.cs)
271271
* [Doubly Linked List](./DataStructures/LinkedList/DoublyLinkedList/DoublyLinkedList.cs)
272272
* [Skip List](./DataStructures/LinkedList/SkipList/SkipList.cs)
273+
* [Circular Linked List](./DataStructures/LinkedList/CircularLinkedList/CircularLinkedList.cs)
273274
* [Graph](./DataStructures/Graph)
274275
* [Directed Weighted Graph Via Adjacency Matrix](./DataStructures/Graph/DirectedWeightedGraph.cs)
275276
* [Disjoint Set](./DataStructures/DisjointSet)

0 commit comments

Comments
 (0)