From 204800cb88f1169bd2b99cfb21fc2308556596ef Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:46:06 +0300 Subject: [PATCH 1/3] Priority Queue implementation --- .../GenericPriorityQueueTests.cs | 44 +++++++++ .../PriorityQueue.Tests/GlobalSuppressions.cs | 8 ++ .../PriorityQueue.Tests.csproj | 27 ++++++ .../TypedPriorityQueueTests.cs | 9 ++ PriorityQueue/PriorityQueue.sln | 28 ++++++ PriorityQueue/PriorityQueue/PriorityQueue.cs | 91 +++++++++++++++++++ .../PriorityQueue/PriorityQueue.csproj | 9 ++ 7 files changed, 216 insertions(+) create mode 100644 PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs create mode 100644 PriorityQueue/PriorityQueue.Tests/GlobalSuppressions.cs create mode 100644 PriorityQueue/PriorityQueue.Tests/PriorityQueue.Tests.csproj create mode 100644 PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs create mode 100644 PriorityQueue/PriorityQueue.sln create mode 100644 PriorityQueue/PriorityQueue/PriorityQueue.cs create mode 100644 PriorityQueue/PriorityQueue/PriorityQueue.csproj diff --git a/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs b/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs new file mode 100644 index 0000000..fb03269 --- /dev/null +++ b/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs @@ -0,0 +1,44 @@ +namespace PriorityQueue.Tests; + +public abstract class GenericPriorityQueueTests +{ + private PriorityQueue queue; + + public virtual IComparer? Comparer { get; } + + public abstract ItemPair SingleTestPair { get; } + + [SetUp] + public void Setup() + { + queue = Comparer == null ? new() : new(Comparer); + } + + [Test] + public void Empty_ShouldBeTrue_ForEmptyQueue() + { + Assert.That(queue.Empty, Is.True); + } + + [Test] + public void Empty_ShouldBeFalse_ForNonEmptyQueue() + { + queue.Enqueue(SingleTestPair.Element, SingleTestPair.Priority); + Assert.That(queue.Empty, Is.False); + } + + [Test] + public void Dequeue_ShouldThrow_IfQueueIsEmpty() + { + Assert.Throws(() => queue.Dequeue()); + } + + [Test] + public void Dequeue_ShouldReturn_EnqueuedItem() + { + queue.Enqueue(SingleTestPair.Element, SingleTestPair.Priority); + Assert.That(queue.Dequeue(), Is.EqualTo(SingleTestPair.Element)); + } + + public readonly record struct ItemPair(TElement Element, TPriority Priority); +} diff --git a/PriorityQueue/PriorityQueue.Tests/GlobalSuppressions.cs b/PriorityQueue/PriorityQueue.Tests/GlobalSuppressions.cs new file mode 100644 index 0000000..4d30dc0 --- /dev/null +++ b/PriorityQueue/PriorityQueue.Tests/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is project with tests")] diff --git a/PriorityQueue/PriorityQueue.Tests/PriorityQueue.Tests.csproj b/PriorityQueue/PriorityQueue.Tests/PriorityQueue.Tests.csproj new file mode 100644 index 0000000..88f20c7 --- /dev/null +++ b/PriorityQueue/PriorityQueue.Tests/PriorityQueue.Tests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + diff --git a/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs b/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs new file mode 100644 index 0000000..ba935a5 --- /dev/null +++ b/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs @@ -0,0 +1,9 @@ +namespace PriorityQueue.Tests; + +public static class TypedPriorityQueueTests +{ + public class IntPriorityQueue : GenericPriorityQueueTests + { + public override ItemPair SingleTestPair => new(123, 456); + } +} diff --git a/PriorityQueue/PriorityQueue.sln b/PriorityQueue/PriorityQueue.sln new file mode 100644 index 0000000..26be7f5 --- /dev/null +++ b/PriorityQueue/PriorityQueue.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriorityQueue", "PriorityQueue\PriorityQueue.csproj", "{DACEF6B2-BF52-446A-B201-C0D652F8A1FC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriorityQueue.Tests", "PriorityQueue.Tests\PriorityQueue.Tests.csproj", "{072C7A26-3045-4432-B398-4AC1353DF895}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DACEF6B2-BF52-446A-B201-C0D652F8A1FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DACEF6B2-BF52-446A-B201-C0D652F8A1FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DACEF6B2-BF52-446A-B201-C0D652F8A1FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DACEF6B2-BF52-446A-B201-C0D652F8A1FC}.Release|Any CPU.Build.0 = Release|Any CPU + {072C7A26-3045-4432-B398-4AC1353DF895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {072C7A26-3045-4432-B398-4AC1353DF895}.Debug|Any CPU.Build.0 = Debug|Any CPU + {072C7A26-3045-4432-B398-4AC1353DF895}.Release|Any CPU.ActiveCfg = Release|Any CPU + {072C7A26-3045-4432-B398-4AC1353DF895}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/PriorityQueue/PriorityQueue/PriorityQueue.cs b/PriorityQueue/PriorityQueue/PriorityQueue.cs new file mode 100644 index 0000000..36e406e --- /dev/null +++ b/PriorityQueue/PriorityQueue/PriorityQueue.cs @@ -0,0 +1,91 @@ +namespace PriorityQueue; + +/// +/// Prioirty queue data structure. +/// +/// Type of elements. +/// Type of priority associated with each element. +public class PriorityQueue +{ + private IComparer comparer; + private Item? head; + + /// + /// Initializes a new instance of the class. + /// + /// Comparer to use; if , defaults to . + public PriorityQueue(IComparer? comparer = null) + { + head = null; + this.comparer = comparer ?? Comparer.Default; + } + + /// + /// Initializes a new instance of the class. + /// + public PriorityQueue() + : this(null) + { + } + + /// + /// Gets a value indicating whether queue is empty. + /// + public bool Empty => head == null; + + /// + /// Enqueues element into the queue. + /// + /// Element to enqueue. + /// Priority of the . + public void Enqueue(TElement element, TPriority priority) + { + var item = head; + if (item == null) + { + item = new(element, priority, null); + head = item; + return; + } + + if (comparer.Compare(priority, item.Priority) < 0) + { + var newHead = new Item(element, priority, item); + head = newHead; + return; + } + + while (item.Next != null && comparer.Compare(priority, item.Priority) >= 0) + { + item = item.Next; + } + + var newItem = new Item(element, priority, null); + item.Next = newItem; + } + + /// + /// Dequeues element with the least priority from the queue. + /// + /// Element with the least priority . + public TElement Dequeue() + { + if (head == null) + { + throw new InvalidOperationException("Queue is empty"); + } + + var item = head; + head = item.Next; + return item.Element; + } + + private class Item(TElement element, TPriority priority, Item? next) + { + public TElement Element => element; + + public TPriority Priority => priority; + + public Item? Next { get; set; } = next; + } +} diff --git a/PriorityQueue/PriorityQueue/PriorityQueue.csproj b/PriorityQueue/PriorityQueue/PriorityQueue.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/PriorityQueue/PriorityQueue/PriorityQueue.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + From f158f2796211230cb5e20263f402bc72ecbdf0ad Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:11:43 +0300 Subject: [PATCH 2/3] Added more tests (priority queue) --- .../GenericPriorityQueueTests.cs | 32 +++++++++++++++++++ .../TypedPriorityQueueTests.cs | 23 +++++++++++++ 2 files changed, 55 insertions(+) diff --git a/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs b/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs index fb03269..64484fe 100644 --- a/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs +++ b/PriorityQueue/PriorityQueue.Tests/GenericPriorityQueueTests.cs @@ -8,6 +8,10 @@ public abstract class GenericPriorityQueueTests public abstract ItemPair SingleTestPair { get; } + public abstract IEnumerable SamePriorityTestPairs { get; } + + public abstract IEnumerable IncreasingPriorityTestPairs { get; } + [SetUp] public void Setup() { @@ -40,5 +44,33 @@ public void Dequeue_ShouldReturn_EnqueuedItem() Assert.That(queue.Dequeue(), Is.EqualTo(SingleTestPair.Element)); } + [Test] + public void Dequeue_ShouldReturn_EnqueuedItems_InSameOrder_IfPriorityIsSame() + { + foreach (var (element, priority) in SamePriorityTestPairs) + { + queue.Enqueue(element, priority); + } + + foreach (var (element, priority) in SamePriorityTestPairs) + { + Assert.That(queue.Dequeue(), Is.EqualTo(element)); + } + } + + [Test] + public void Dequeue_ShouldReturn_EnqueuedItems_InReverseOrder_IfPriorityIsDecreasing() + { + foreach (var (element, priority) in IncreasingPriorityTestPairs.Reverse()) + { + queue.Enqueue(element, priority); + } + + foreach (var (element, priority) in IncreasingPriorityTestPairs) + { + Assert.That(queue.Dequeue(), Is.EqualTo(element)); + } + } + public readonly record struct ItemPair(TElement Element, TPriority Priority); } diff --git a/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs b/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs index ba935a5..e65f9d5 100644 --- a/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs +++ b/PriorityQueue/PriorityQueue.Tests/TypedPriorityQueueTests.cs @@ -5,5 +5,28 @@ public static class TypedPriorityQueueTests public class IntPriorityQueue : GenericPriorityQueueTests { public override ItemPair SingleTestPair => new(123, 456); + + public override IEnumerable SamePriorityTestPairs => Enumerable.Range(1, 64).Select(x => new ItemPair(x, 123)); + + public override IEnumerable IncreasingPriorityTestPairs => Enumerable.Range(1, 64).Select(x => new ItemPair(x, x)); + } + + public class StringPriorityQueue : GenericPriorityQueueTests + { + public override IComparer? Comparer { get; } = new NumberStringComparer(); + + public override ItemPair SingleTestPair => new("1234", "4444"); + + public override IEnumerable SamePriorityTestPairs => Enumerable.Range(1, 64) + .Select(x => x.ToString().PadLeft(3, '0')).Select(x => new ItemPair(x, x)); + + public override IEnumerable IncreasingPriorityTestPairs => Enumerable.Range(1, 8) + .Select(x => new string('1', x)).Select(x => new ItemPair(x, x)); + + private class NumberStringComparer : IComparer + { + public int Compare(string? x, string? y) + => int.Parse(x ?? "0") - int.Parse(y ?? "0"); + } } } From b319fea929bb4eebea3ca07ca2e7dfbf346ff832 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:15:42 +0300 Subject: [PATCH 3/3] Fixed queue (priority queue) --- PriorityQueue/PriorityQueue/PriorityQueue.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PriorityQueue/PriorityQueue/PriorityQueue.cs b/PriorityQueue/PriorityQueue/PriorityQueue.cs index 36e406e..8b2c095 100644 --- a/PriorityQueue/PriorityQueue/PriorityQueue.cs +++ b/PriorityQueue/PriorityQueue/PriorityQueue.cs @@ -55,12 +55,12 @@ public void Enqueue(TElement element, TPriority priority) return; } - while (item.Next != null && comparer.Compare(priority, item.Priority) >= 0) + while (item.Next != null && comparer.Compare(priority, item.Next.Priority) >= 0) { item = item.Next; } - var newItem = new Item(element, priority, null); + var newItem = new Item(element, priority, item.Next); item.Next = newItem; }