From 36a7dd66f654a8c00c8c1550204b71e21946bf03 Mon Sep 17 00:00:00 2001 From: Mohit Singh <168663521+mohit-gogitter@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:52:15 +0530 Subject: [PATCH] Add BalancedParenthesesChecker, NextGreaterElement, and ReverseStack (#473) --- .../Stack/BalancedParenthesesCheckerTests.cs | 124 ++++++++++++++++++ .../Stack/NextGreaterElementTests.cs | 100 ++++++++++++++ Algorithms.Tests/Stack/ReverseStackTests.cs | 84 ++++++++++++ .../Stack/BalancedParenthesesChecker.cs | 90 +++++++++++++ Algorithms/Stack/NextGreaterElement.cs | 47 +++++++ Algorithms/Stack/ReverseStack.cs | 44 +++++++ README.md | 4 + 7 files changed, 493 insertions(+) create mode 100644 Algorithms.Tests/Stack/BalancedParenthesesCheckerTests.cs create mode 100644 Algorithms.Tests/Stack/NextGreaterElementTests.cs create mode 100644 Algorithms.Tests/Stack/ReverseStackTests.cs create mode 100644 Algorithms/Stack/BalancedParenthesesChecker.cs create mode 100644 Algorithms/Stack/NextGreaterElement.cs create mode 100644 Algorithms/Stack/ReverseStack.cs diff --git a/Algorithms.Tests/Stack/BalancedParenthesesCheckerTests.cs b/Algorithms.Tests/Stack/BalancedParenthesesCheckerTests.cs new file mode 100644 index 00000000..64c3876d --- /dev/null +++ b/Algorithms.Tests/Stack/BalancedParenthesesCheckerTests.cs @@ -0,0 +1,124 @@ +using System; +using Algorithms.Stack; +using NUnit.Framework; + +namespace Algorithms.Tests.Stack +{ + [TestFixture] + public class BalancedParenthesesCheckerTests + { + public static bool IsBalanced(string expression) + { + var checker = new BalancedParenthesesChecker(); + return checker.IsBalanced(expression); + } + + [Test] + public void IsBalanced_EmptyString_ThrowsArgumentException() + { + // Arrange + var expression = string.Empty; + + // Act & Assert + var ex = Assert.Throws(() => IsBalanced(expression)); + + if(ex!=null) + { + Assert.That(ex.Message, Is.EqualTo("The input expression cannot be null or empty.")); + } + + } + + [Test] + public void IsBalanced_ValidBalancedExpression_ReturnsTrue() + { + // Arrange + var expression = "{[()]}"; + + // Act + var result = IsBalanced(expression); + + // Assert + Assert.That(result, Is.EqualTo(true)); + } + + [Test] + public void IsBalanced_ValidUnbalancedExpression_ReturnsFalse() + { + // Arrange + var expression = "{[(])}"; + + // Act + var result = IsBalanced(expression); + + // Assert + Assert.That(result, Is.EqualTo(false)); + } + + [Test] + public void IsBalanced_UnbalancedWithExtraClosingBracket_ReturnsFalse() + { + // Arrange + var expression = "{[()]}]"; + + // Act + var result = IsBalanced(expression); + + // Assert + Assert.That(result, Is.EqualTo(false)); + } + + [Test] + public void IsBalanced_ExpressionWithInvalidCharacters_ThrowsArgumentException() + { + // Arrange + var expression = "{[a]}"; + + // Act & Assert + var ex = Assert.Throws(() => IsBalanced(expression)); + if (ex != null) + { + Assert.That(ex.Message, Is.EqualTo("Invalid character 'a' found in the expression.")); + } + } + + [Test] + public void IsBalanced_SingleOpeningBracket_ReturnsFalse() + { + // Arrange + var expression = "("; + + // Act + var result = IsBalanced(expression); + + // Assert + Assert.That(result, Is.EqualTo(false)); + } + + [Test] + public void IsBalanced_SingleClosingBracket_ReturnsFalse() + { + // Arrange + var expression = ")"; + + // Act + var result = IsBalanced(expression); + + // Assert + Assert.That(result, Is.EqualTo(false)); + } + + [Test] + public void IsBalanced_ExpressionWithMultipleBalancedBrackets_ReturnsTrue() + { + // Arrange + var expression = "[{()}]()"; + + // Act + var result = IsBalanced(expression); + + // Assert + Assert.That(result, Is.EqualTo(true)); + } + } +} diff --git a/Algorithms.Tests/Stack/NextGreaterElementTests.cs b/Algorithms.Tests/Stack/NextGreaterElementTests.cs new file mode 100644 index 00000000..09df3649 --- /dev/null +++ b/Algorithms.Tests/Stack/NextGreaterElementTests.cs @@ -0,0 +1,100 @@ +using System; +using Algorithms.Stack; +using NUnit.Framework; + +namespace Algorithms.Tests.Stack +{ + [TestFixture] + public class NextGreaterElementTests + { + private static int[] FindNextGreaterElement(int[] input) + { + var obj = new NextGreaterElement(); + return obj.FindNextGreaterElement(input); + } + + [Test] + public void FindNextGreaterElement_InputIsEmpty_ReturnsEmptyArray() + { + // Arrange + int[] input = Array.Empty(); + int[] expected = Array.Empty(); + + // Act + var result = FindNextGreaterElement(input); + + // Assert + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void FindNextGreaterElement_BasicScenario_ReturnsCorrectResult() + { + // Arrange + int[] input = { 4, 5, 2, 25 }; + int[] expected = { 5, 25, 25, -1 }; + + // Act + var result = FindNextGreaterElement(input); + + // Assert + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void FindNextGreaterElement_NoNextGreaterElement_ReturnsCorrectResult() + { + // Arrange + int[] input = { 13, 7, 6, 12 }; + int[] expected = { -1, 12, 12, -1 }; + + // Act + var result = FindNextGreaterElement(input); + + // Assert + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void FindNextGreaterElement_AllElementsHaveNoGreaterElement_ReturnsAllNegativeOnes() + { + // Arrange + int[] input = { 5, 4, 3, 2, 1 }; + int[] expected = { -1, -1, -1, -1, -1 }; + + // Act + var result = FindNextGreaterElement(input); + + // Assert + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void FindNextGreaterElement_InputWithDuplicates_ReturnsCorrectResult() + { + // Arrange + int[] input = { 4, 4, 3, 2, 4 }; + int[] expected = { -1, -1, 4, 4, -1 }; + + // Act + var result = FindNextGreaterElement(input); + + // Assert + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void FindNextGreaterElement_SingleElementArray_ReturnsNegativeOne() + { + // Arrange + int[] input = { 10 }; + int[] expected = { -1 }; + + // Act + var result = FindNextGreaterElement(input); + + // Assert + Assert.That(result, Is.EqualTo(expected)); + } + } +} diff --git a/Algorithms.Tests/Stack/ReverseStackTests.cs b/Algorithms.Tests/Stack/ReverseStackTests.cs new file mode 100644 index 00000000..8312efac --- /dev/null +++ b/Algorithms.Tests/Stack/ReverseStackTests.cs @@ -0,0 +1,84 @@ +using Algorithms.Stack; +using NUnit.Framework; +using System.Collections.Generic; + + +namespace Algorithms.Tests.Stack +{ + public class ReverseStackTests + { + public static void Reverse(Stack stack) + { + var obj = new ReverseStack(); + obj.Reverse(stack); + } + + [Test] + public void Reverse_EmptyStack_DoesNotChangeStack() + { + // Arrange + Stack stack = new Stack(); + + // Act + Reverse(stack); + + // Assert + Assert.That(stack.Count, Is.EqualTo(0)); + } + + [Test] + public void Reverse_SingleElementStack_DoesNotChangeStack() + { + // Arrange + Stack stack = new Stack(); + stack.Push(1); + + // Act + Reverse(stack); + + // Assert + Assert.That(stack.Count, Is.EqualTo(1)); + Assert.That(stack.Peek(), Is.EqualTo(1)); + } + + [Test] + public void Reverse_MultipleElementStack_ReturnsCorrectOrder() + { + // Arrange + Stack stack = new Stack(); + stack.Push(1); + stack.Push(2); + stack.Push(3); + // The stack is now [3, 2, 1] (top to bottom) + + // Act + Reverse(stack); + + // Assert + Assert.That(stack.Count, Is.EqualTo(3)); + Assert.That(stack.Pop(), Is.EqualTo(1)); // Should return 1 + Assert.That(stack.Pop(), Is.EqualTo(2)); // Should return 2 + Assert.That(stack.Pop(), Is.EqualTo(3)); // Should return 3 + } + + [Test] + public void Reverse_StackWithDuplicates_ReturnsCorrectOrder() + { + // Arrange + Stack stack = new Stack(); + stack.Push(1); + stack.Push(2); + stack.Push(1); + // The stack is now [1, 2, 1] (top to bottom) + + // Act + Reverse(stack); + + // Assert + Assert.That(stack.Count, Is.EqualTo(3)); + Assert.That(stack.Pop(), Is.EqualTo(1)); // Should return 1 + Assert.That(stack.Pop(), Is.EqualTo(2)); // Should return 2 + Assert.That(stack.Pop(), Is.EqualTo(1)); // Should return 1 + } + } +} diff --git a/Algorithms/Stack/BalancedParenthesesChecker.cs b/Algorithms/Stack/BalancedParenthesesChecker.cs new file mode 100644 index 00000000..2f2b8311 --- /dev/null +++ b/Algorithms/Stack/BalancedParenthesesChecker.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; + +namespace Algorithms.Stack +{ + /// + /// It checks if an expression has matching and balanced parentheses. + /// @author Mohit Singh. mohit-gogitter + /// + public class BalancedParenthesesChecker + { + private static readonly Dictionary ParenthesesMap = new Dictionary + { + { '(', ')' }, + { '{', '}' }, + { '[', ']' }, + }; + + /// + /// Determines if a given string expression containing brackets is balanced. + /// A string is considered balanced if all opening brackets have corresponding closing brackets + /// in the correct order. The supported brackets are '()', '{}', and '[]'. + /// + /// + /// The input string expression containing the brackets to check for balance. + /// + /// + /// true if the brackets in the expression are balanced; otherwise, false. + /// + /// + /// Thrown when the input expression contains invalid characters or is null/empty. + /// Only '(', ')', '{', '}', '[', ']' characters are allowed. + /// + public bool IsBalanced(string expression) + { + if (string.IsNullOrEmpty(expression)) + { + throw new ArgumentException("The input expression cannot be null or empty."); + } + + Stack stack = new Stack(); + foreach (char c in expression) + { + if (IsOpeningParenthesis(c)) + { + stack.Push(c); + } + else if (IsClosingParenthesis(c)) + { + if (!IsBalancedClosing(stack, c)) + { + return false; + } + } + else + { + throw new ArgumentException($"Invalid character '{c}' found in the expression."); + } + } + + return stack.Count == 0; + } + + private static bool IsOpeningParenthesis(char c) + { + return c == '(' || c == '{' || c == '['; + } + + private static bool IsClosingParenthesis(char c) + { + return c == ')' || c == '}' || c == ']'; + } + + private static bool IsBalancedClosing(Stack stack, char close) + { + if (stack.Count == 0) + { + return false; + } + + char open = stack.Pop(); + return IsMatchingPair(open, close); + } + + private static bool IsMatchingPair(char open, char close) + { + return ParenthesesMap.ContainsKey(open) && ParenthesesMap[open] == close; + } + } +} diff --git a/Algorithms/Stack/NextGreaterElement.cs b/Algorithms/Stack/NextGreaterElement.cs new file mode 100644 index 00000000..0e5bbecb --- /dev/null +++ b/Algorithms/Stack/NextGreaterElement.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace Algorithms.Stack +{ + /// + /// For each element in an array, the utility finds the next greater element on the right side using a stack. + /// @author Mohit Singh. mohit-gogitter + /// + public class NextGreaterElement + { + /// + /// Finds the next greater element for each element in the input array. + /// The next greater element for an element x is the first element greater than x to its right. + /// If there is no greater element, -1 is returned for that element. + /// + /// The array of integers to find the next greater elements for. + /// An array where each index contains the next greater element of the corresponding element in the input array, or -1 if no such element exists. + /// Thrown when the input array is null. + public int[] FindNextGreaterElement(int[] nums) + { + int[] result = new int[nums.Length]; + Stack stack = new Stack(); + + // Initialize all elements in the result array to -1 + for (int i = 0; i < nums.Length; i++) + { + result[i] = -1; + } + + for (int i = 0; i < nums.Length; i++) + { + // While the stack is not empty and the current element is greater than the element + // corresponding to the index stored at the top of the stack + while (stack.Count > 0 && nums[i] > nums[stack.Peek()]) + { + int index = stack.Pop(); + result[index] = nums[i]; // Set the next greater element + } + + stack.Push(i); // Push current index to stack + } + + return result; + } + } +} diff --git a/Algorithms/Stack/ReverseStack.cs b/Algorithms/Stack/ReverseStack.cs new file mode 100644 index 00000000..af2f4e26 --- /dev/null +++ b/Algorithms/Stack/ReverseStack.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace Algorithms.Stack +{ + /// + /// Reverses the elements in a stack using recursion. + /// @author Mohit Singh. mohit-gogitter + /// + public class ReverseStack + { + /// + /// Recursively reverses the elements of the specified stack. + /// + /// The type of elements in the stack. + /// The stack to be reversed. This parameter cannot be null. + /// Thrown when the stack parameter is null. + public void Reverse(Stack stack) + { + if (stack.Count == 0) + { + return; + } + + T temp = stack.Pop(); + Reverse(stack); + InsertAtBottom(stack, temp); + } + + private void InsertAtBottom(Stack stack, T value) + { + if (stack.Count == 0) + { + stack.Push(value); + } + else + { + T temp = stack.Pop(); + InsertAtBottom(stack, value); + stack.Push(temp); + } + } + } +} diff --git a/README.md b/README.md index 13c2542b..f13516de 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,10 @@ find more than one implementation for the same objective but using different alg * [A057588 Kummer Numbers](./Algorithms/Sequences/KummerNumbersSequence.cs) * [A019434 Fermat Primes](./Algorithms/Sequences/FermatPrimesSequence.cs) * [A181391 Van Eck's](./Algorithms/Sequences/VanEcksSequence.cs) + * [Stack](./Algorithms/Stack) + * [Next Greater Element](./Algorithms/Stack/NextGreaterElement.cs) + * [Balanced Parentheses Checker](./Algorithms/Stack/BalancedParenthesesChecker.cs) + * [Reverse Stack](./Algorithms/Stack/ReverseStack.cs) * [String](./Algorithms/Strings) * [Similarity](./Algorithms/Strings/Similarity/) * [Cosine Similarity](./Algorithms/Strings/Similarity/CosineSimilarity.cs)