Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions DataStructures.Tests/LinkedList/CircularLinkedListTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
using System;
using DataStructures.LinkedList.CircularLinkedList;
using NUnit.Framework;

namespace DataStructures.Tests.LinkedList;

[TestFixture]
public static class CircularLinkedListTests
{
[Test]
public static void TestInsertAtBeginning()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtBeginning(10);
cll.InsertAtBeginning(20);
cll.InsertAtBeginning(30);

Assert.That("30 20 10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestInsertAtEnd()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);

Assert.That("10 20 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestInsertAfter()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);
cll.InsertAfter(20, 25);

Assert.That("10 20 25 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestInsertAtBeginningInEmptyList()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtBeginning(10);

Assert.That("10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestInsertAtEndInEmptyList()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);

Assert.That("10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestInsertAfterInEmptyList()
{
var cll = new CircularLinkedList<int>();
var ex = Assert.Throws<InvalidOperationException>(() => cll.InsertAfter(10, 20));

Assert.That(ex!.Message, Is.EqualTo("List is empty."));
}

[Test]
public static void TestInsertAfterSpecificNode()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);
cll.InsertAfter(20, 25); // Insert after node with value 20

Assert.That("10 20 25 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestInsertAfterOnNonExistingValue()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAfter(99, 25); // 99 does not exist

Assert.That("10", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestDeleteNode()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);
cll.DeleteNode(20);

Assert.That("10 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestDeleteOnlyNode()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtBeginning(10);
cll.DeleteNode(10);

Assert.That(cll.IsEmpty(), Is.EqualTo(true));
}

[Test]
public static void TestDeleteHeadNode()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);
cll.DeleteNode(10);

Assert.That("20 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestDeleteTailNode()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);
cll.DeleteNode(30);

Assert.That("10 20", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

[Test]
public static void TestDeleteFromEmptyList()
{
var cll = new CircularLinkedList<int>();
var ex = Assert.Throws<InvalidOperationException>(() => cll.DeleteNode(10));

Assert.That(ex!.Message, Is.EqualTo("List is empty."));
}

[Test]
public static void TestDeleteNonExistentNode()
{
var cll = new CircularLinkedList<int>();
cll.InsertAtEnd(10);
cll.InsertAtEnd(20);
cll.InsertAtEnd(30);
cll.DeleteNode(40); // Attempting to delete a node that doesn't exist

Assert.That("10 20 30", Is.EqualTo(GetDisplayOutput(cll).Trim()));
}

private static string GetDisplayOutput<T>(CircularLinkedList<T> list)
{
var head = list.GetHead();
if (head == null)
{
return string.Empty;
}

var current = head;
var result = new System.Text.StringBuilder();

do
{
result.Append(current!.Data + " ");
current = current.Next;
}
while (current != head);

return result.ToString().Trim();
}
}
155 changes: 155 additions & 0 deletions DataStructures/LinkedList/CircularLinkedList/CircularLinkedList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;

namespace DataStructures.LinkedList.CircularLinkedList
{
/// <summary>
/// CircularLinkedList.
/// @author Mohit Singh. <a href="https://github.com/mohit-gogitter">mohit-gogitter</a>
/// </summary>
/// <typeparam name="T">The generic type parameter.</typeparam>
public class CircularLinkedList<T>
{
/// <summary>
/// Points to the last node in the Circular Linked List.
/// </summary>
private CircularLinkedListNode<T>? tail;

/// <summary>
/// Initializes a new instance of the <see cref="CircularLinkedList{T}"/> class.
/// </summary>
public CircularLinkedList()
{
tail = null;
}

/// <summary>
/// Gets the head node (tail.Next) of the Circular Linked List.
/// </summary>
public CircularLinkedListNode<T>? GetHead()
{
return tail?.Next;
}

/// <summary>
/// Determines whether the Circular Linked List is empty.
/// </summary>
/// <returns>True if the list is empty; otherwise, false.</returns>
public bool IsEmpty()
{
return tail == null;
}

/// <summary>
/// Inserts a new node at the beginning of the Circular Linked List.
/// </summary>
/// <param name="data">The data to insert into the new node.</param>
public void InsertAtBeginning(T data)
{
var newNode = new CircularLinkedListNode<T>(data);
if (IsEmpty())
{
tail = newNode;
tail.Next = tail;
}
else
{
newNode.Next = tail!.Next;
tail.Next = newNode;
}
}

/// <summary>
/// Inserts a new node at the end of the Circular Linked List.
/// </summary>
/// <param name="data">The data to insert into the new node.</param>
public void InsertAtEnd(T data)
{
var newNode = new CircularLinkedListNode<T>(data);
if (IsEmpty())
{
tail = newNode;
tail.Next = tail;
}
else
{
newNode.Next = tail!.Next;
tail.Next = newNode;
tail = newNode;
}
}

/// <summary>
/// Inserts a new node after a specific value in the list.
/// </summary>
/// <param name="value">The value to insert the node after.</param>
/// <param name="data">The data to insert into the new node.</param>
public void InsertAfter(T value, T data)
{
if (IsEmpty())
{
throw new InvalidOperationException("List is empty.");
}

var current = tail!.Next;
do
{
if (current!.Data!.Equals(value))
{
var newNode = new CircularLinkedListNode<T>(data);
newNode.Next = current.Next;
current.Next = newNode;

return;
}

current = current.Next;
}
while (current != tail.Next);
}

/// <summary>
/// Deletes a node with a specific value from the list.
/// </summary>
/// <param name="value">The value of the node to delete.</param>
public void DeleteNode(T value)
{
if (IsEmpty())
{
throw new InvalidOperationException("List is empty.");
}

var current = tail!.Next;
var previous = tail;

do
{
if (current!.Data!.Equals(value))
{
if (current == tail && current.Next == tail)
{
tail = null;
}
else if (current == tail)
{
previous!.Next = tail.Next;
tail = previous;
}
else if (current == tail.Next)
{
tail.Next = current.Next;
}
else
{
previous!.Next = current.Next;
}

return;
}

previous = current;
current = current.Next;
}
while (current != tail!.Next);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace DataStructures.LinkedList.CircularLinkedList
{
/// <summary>
/// Represents a node in the Circular Linked List.
/// Each node contains generic data and a reference to the next node.
/// </summary>
/// <typeparam name="T">The type of the data stored in the node.</typeparam>
/// <remarks>
/// Initializes a new instance of the <see cref="CircularLinkedListNode{T}"/> class.
/// </remarks>
/// <param name="data">The data to be stored in the node.</param>
public class CircularLinkedListNode<T>(T data)
{
/// <summary>
/// Gets or sets the data for the node.
/// </summary>
public T Data { get; set; } = data;

/// <summary>
/// Gets or sets the reference to the next node in the list.
/// </summary>
public CircularLinkedListNode<T>? Next { get; set; }
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ find more than one implementation for the same objective but using different alg
* [Singly Linked List](./DataStructures/LinkedList/SinglyLinkedList/SinglyLinkedList.cs)
* [Doubly Linked List](./DataStructures/LinkedList/DoublyLinkedList/DoublyLinkedList.cs)
* [Skip List](./DataStructures/LinkedList/SkipList/SkipList.cs)
* [Circular Linked List](./DataStructures/LinkedList/CircularLinkedList/CircularLinkedList.cs)
* [Graph](./DataStructures/Graph)
* [Directed Weighted Graph Via Adjacency Matrix](./DataStructures/Graph/DirectedWeightedGraph.cs)
* [Disjoint Set](./DataStructures/DisjointSet)
Expand Down
Loading