From 3d3f61adf45b207809cff681bb15fe0ecd313515 Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 21:42:23 +0530 Subject: [PATCH 1/8] feat: Add ValidParentheses utility with tests --- .../stacks/ValidParentheses.java | 68 +++++++++++++++++++ .../stacks/ValidParenthesesTest.java | 44 ++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/main/java/com/thealgorithms/stacks/ValidParentheses.java create mode 100644 src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java new file mode 100644 index 000000000000..2b69ef9ef123 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -0,0 +1,68 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Utility class for checking if a given string of parentheses is valid. + *

+ * A string containing just the characters '(', ')', '{', '}', '[' and ']' is valid if: + *

+ *

+ * Example: + *

+ *   Input: "()[]{}"
+ *   Output: true
+ * 
+ *

+ * For more details, see + * LeetCode: Valid Parentheses. + *

+ */ +public final class ValidParentheses { + + private ValidParentheses() { + // prevent instantiation + } + + /** + * Checks whether the input string has valid parentheses. + * + * @param str input string containing parentheses + * @return true if the string is valid, false otherwise + */ + public static boolean isValid(final String str) { + Stack stack = new Stack<>(); + + for (char ch : str.toCharArray()) { + if (ch == '(' || ch == '{' || ch == '[') { + stack.push(ch); + } else { + if (stack.isEmpty()) { + return false; + } + char top = stack.peek(); + if ((top == '(' && ch == ')') || + (top == '{' && ch == '}') || + (top == '[' && ch == ']')) { + stack.pop(); + } else { + return false; + } + } + } + return stack.isEmpty(); + } + + /** + * Example usage. + */ + public static void main(final String[] args) { + System.out.println(isValid("()[]{}")); // true + System.out.println(isValid("(]")); // false + System.out.println(isValid("([)]")); // false + System.out.println(isValid("{[]}")); // true + } +} diff --git a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java new file mode 100644 index 000000000000..93cc5fca9111 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.stacks; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ValidParenthesesTest { + + @ParameterizedTest + @MethodSource("provideValidTestCases") + void testIsValid_ValidCases(String input, boolean expected) { + assertEquals(expected, ValidParentheses.isValid(input)); + } + + static Stream provideValidTestCases() { + return Stream.of( + Arguments.of("()", true), + Arguments.of("()[]{}", true), + Arguments.of("{[]}", true), + Arguments.of("", true) // empty string is valid + ); + } + + @ParameterizedTest + @MethodSource("provideInvalidTestCases") + void testIsValid_InvalidCases(String input) { + assertEquals(false, ValidParentheses.isValid(input)); + } + + static Stream provideInvalidTestCases() { + return Stream.of( + Arguments.of("("), + Arguments.of(")"), + Arguments.of("([)]"), + Arguments.of("{[}]"), + Arguments.of("((()") + ); + } +} From c1e0a524f08f543ab07cef7c9c8b147a22ad5bd9 Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:10:20 +0530 Subject: [PATCH 2/8] style: fix checkstyle violations in ValidParentheses and tests --- .../java/com/thealgorithms/stacks/ValidParentheses.java | 6 +++--- .../java/com/thealgorithms/stacks/ValidParenthesesTest.java | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index 2b69ef9ef123..e601aa942275 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -44,9 +44,9 @@ public static boolean isValid(final String str) { return false; } char top = stack.peek(); - if ((top == '(' && ch == ')') || - (top == '{' && ch == '}') || - (top == '[' && ch == ']')) { + if ((top == '(' && ch == ')') + || (top == '{' && ch == '}') + || (top == '[' && ch == ']')) { stack.pop(); } else { return false; diff --git a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java index 93cc5fca9111..f95a16a3f259 100644 --- a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java +++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java @@ -2,8 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -13,7 +11,7 @@ public class ValidParenthesesTest { @ParameterizedTest @MethodSource("provideValidTestCases") - void testIsValid_ValidCases(String input, boolean expected) { + void testIsValidValidCases(String input, boolean expected) { assertEquals(expected, ValidParentheses.isValid(input)); } @@ -28,7 +26,7 @@ static Stream provideValidTestCases() { @ParameterizedTest @MethodSource("provideInvalidTestCases") - void testIsValid_InvalidCases(String input) { + void testIsValidInvalidCases(String input) { assertEquals(false, ValidParentheses.isValid(input)); } From 12286cfd1a44d66c27c9bb79b0306500ab31cdac Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:16:43 +0530 Subject: [PATCH 3/8] fix: resolve Checkstyle, Clang format, and SpotBugs issues --- .../stacks/ValidParentheses.java | 45 ++++++------------- .../stacks/ValidParenthesesTest.java | 29 ++++++------ 2 files changed, 29 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index e601aa942275..889b88a16757 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -3,47 +3,31 @@ import java.util.Stack; /** - * Utility class for checking if a given string of parentheses is valid. - *

- * A string containing just the characters '(', ')', '{', '}', '[' and ']' is valid if: - *

    - *
  • Open brackets are closed by the same type of brackets.
  • - *
  • Open brackets are closed in the correct order.
  • - *
- *

- * Example: - *

- *   Input: "()[]{}"
- *   Output: true
- * 
- *

- * For more details, see - * LeetCode: Valid Parentheses. - *

+ * Utility class to check for valid parentheses in a string. */ public final class ValidParentheses { private ValidParentheses() { - // prevent instantiation + throw new AssertionError("Cannot instantiate utility class"); } /** - * Checks whether the input string has valid parentheses. + * Returns true if the input string has valid parentheses. * - * @param str input string containing parentheses - * @return true if the string is valid, false otherwise + * @params the string containing parentheses + * @return true if valid, false otherwise */ - public static boolean isValid(final String str) { - Stack stack = new Stack<>(); + public static boolean isValid(final String s) { + final Stack stack = new Stack<>(); - for (char ch : str.toCharArray()) { + for (final char ch : s.toCharArray()) { if (ch == '(' || ch == '{' || ch == '[') { stack.push(ch); - } else { + } else if (ch == ')' || ch == '}' || ch == ']') { if (stack.isEmpty()) { return false; } - char top = stack.peek(); + final char top = stack.peek(); if ((top == '(' && ch == ')') || (top == '{' && ch == '}') || (top == '[' && ch == ']')) { @@ -56,13 +40,10 @@ public static boolean isValid(final String str) { return stack.isEmpty(); } - /** - * Example usage. - */ public static void main(final String[] args) { System.out.println(isValid("()[]{}")); // true - System.out.println(isValid("(]")); // false - System.out.println(isValid("([)]")); // false - System.out.println(isValid("{[]}")); // true + System.out.println(isValid("(]")); // false + System.out.println(isValid("([)]")); // false + System.out.println(isValid("{[]}")); // true } } diff --git a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java index f95a16a3f259..e179e130f3f3 100644 --- a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java +++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java @@ -1,42 +1,45 @@ package com.thealgorithms.stacks; - import static org.junit.jupiter.api.Assertions.assertEquals; + import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +/** + * Tests for {@link ValidParentheses}. + */ public class ValidParenthesesTest { @ParameterizedTest @MethodSource("provideValidTestCases") - void testIsValidValidCases(String input, boolean expected) { + void testIsValidValidCases(String input, Boolean expected) { assertEquals(expected, ValidParentheses.isValid(input)); } static Stream provideValidTestCases() { return Stream.of( - Arguments.of("()", true), - Arguments.of("()[]{}", true), - Arguments.of("{[]}", true), - Arguments.of("", true) // empty string is valid + Arguments.of("()", Boolean.TRUE), + Arguments.of("()[]{}", Boolean.TRUE), + Arguments.of("{[]}", Boolean.TRUE), + Arguments.of("", Boolean.TRUE) // empty string is valid ); } @ParameterizedTest @MethodSource("provideInvalidTestCases") - void testIsValidInvalidCases(String input) { - assertEquals(false, ValidParentheses.isValid(input)); + void testIsValidInvalidCases(String input, Boolean expected) { + assertEquals(expected, ValidParentheses.isValid(input)); } static Stream provideInvalidTestCases() { return Stream.of( - Arguments.of("("), - Arguments.of(")"), - Arguments.of("([)]"), - Arguments.of("{[}]"), - Arguments.of("((()") + Arguments.of("(", Boolean.FALSE), + Arguments.of(")", Boolean.FALSE), + Arguments.of("([)]", Boolean.FALSE), + Arguments.of("{[}]", Boolean.FALSE), + Arguments.of("((()", Boolean.FALSE) ); } } From f56c2490986b2b352c97360b8501ba6fa8f3ddde Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:22:20 +0530 Subject: [PATCH 4/8] fix: remove redundant main method to satisfy PMD rules --- .../java/com/thealgorithms/stacks/ValidParentheses.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index 889b88a16757..9169bf8b2f2f 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -14,7 +14,7 @@ private ValidParentheses() { /** * Returns true if the input string has valid parentheses. * - * @params the string containing parentheses + * @param s the string containing parentheses * @return true if valid, false otherwise */ public static boolean isValid(final String s) { @@ -39,11 +39,4 @@ public static boolean isValid(final String s) { } return stack.isEmpty(); } - - public static void main(final String[] args) { - System.out.println(isValid("()[]{}")); // true - System.out.println(isValid("(]")); // false - System.out.println(isValid("([)]")); // false - System.out.println(isValid("{[]}")); // true - } } From 4c819ee3c1e5b13140b0f618b89ecec1cef9b6b3 Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:37:18 +0530 Subject: [PATCH 5/8] fix: update ValidParentheses with Deque and improved tests --- .../stacks/ValidParentheses.java | 35 +++++++------- .../stacks/ValidParenthesesTest.java | 47 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index 9169bf8b2f2f..52265f5f1968 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -1,9 +1,11 @@ package com.thealgorithms.stacks; -import java.util.Stack; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Map; /** - * Utility class to check for valid parentheses in a string. + * Utility class to check if a string has valid parentheses. */ public final class ValidParentheses { @@ -11,28 +13,27 @@ private ValidParentheses() { throw new AssertionError("Cannot instantiate utility class"); } + private static final Map PAIRS = Map.of( + ')', '(', '}', '{', ']', '[' + ); + /** - * Returns true if the input string has valid parentheses. + * Checks if the input string has valid parentheses. * - * @param s the string containing parentheses + * @param s the input string * @return true if valid, false otherwise */ public static boolean isValid(final String s) { - final Stack stack = new Stack<>(); + if (s == null) { + throw new NullPointerException("Input cannot be null"); + } - for (final char ch : s.toCharArray()) { - if (ch == '(' || ch == '{' || ch == '[') { + Deque stack = new ArrayDeque<>(); + for (char ch : s.toCharArray()) { + if (PAIRS.containsValue(ch)) { // opening bracket stack.push(ch); - } else if (ch == ')' || ch == '}' || ch == ']') { - if (stack.isEmpty()) { - return false; - } - final char top = stack.peek(); - if ((top == '(' && ch == ')') - || (top == '{' && ch == '}') - || (top == '[' && ch == ']')) { - stack.pop(); - } else { + } else if (PAIRS.containsKey(ch)) { // closing bracket + if (stack.isEmpty() || stack.pop() != PAIRS.get(ch)) { return false; } } diff --git a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java index e179e130f3f3..95156e58c643 100644 --- a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java +++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java @@ -1,11 +1,10 @@ package com.thealgorithms.stacks; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.CsvSource; /** * Tests for {@link ValidParentheses}. @@ -13,33 +12,33 @@ public class ValidParenthesesTest { @ParameterizedTest - @MethodSource("provideValidTestCases") - void testIsValidValidCases(String input, Boolean expected) { + @CsvSource({ + "'()', true", + "'()[]{}', true", + "'{[]}', true", + "'', true" + }) + void testValidParentheses(String input, boolean expected) { assertEquals(expected, ValidParentheses.isValid(input)); } - static Stream provideValidTestCases() { - return Stream.of( - Arguments.of("()", Boolean.TRUE), - Arguments.of("()[]{}", Boolean.TRUE), - Arguments.of("{[]}", Boolean.TRUE), - Arguments.of("", Boolean.TRUE) // empty string is valid - ); - } - @ParameterizedTest - @MethodSource("provideInvalidTestCases") - void testIsValidInvalidCases(String input, Boolean expected) { + @CsvSource({ + "'(', false", + "')', false", + "'([)]', false", + "'{[}]', false", + "'((()', false" + }) + void testInvalidParentheses(String input, boolean expected) { assertEquals(expected, ValidParentheses.isValid(input)); } - static Stream provideInvalidTestCases() { - return Stream.of( - Arguments.of("(", Boolean.FALSE), - Arguments.of(")", Boolean.FALSE), - Arguments.of("([)]", Boolean.FALSE), - Arguments.of("{[}]", Boolean.FALSE), - Arguments.of("((()", Boolean.FALSE) - ); + @ParameterizedTest + @CsvSource({ + "null" + }) + void testNullInput(String input) { + assertThrows(NullPointerException.class, () -> ValidParentheses.isValid(null)); } } From 47d1ec64e908630c0947cb07d77b48ed319d5dad Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:39:36 +0530 Subject: [PATCH 6/8] fix: update ValidParentheses and tests for coverage & formatting --- .../stacks/ValidParentheses.java | 17 +++++------------ .../stacks/ValidParenthesesTest.java | 19 +++---------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index 52265f5f1968..189e110c5d60 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -13,9 +13,7 @@ private ValidParentheses() { throw new AssertionError("Cannot instantiate utility class"); } - private static final Map PAIRS = Map.of( - ')', '(', '}', '{', ']', '[' - ); + private static final Map PAIRS = Map.of(')', '(', '}', '{', ']', '['); /** * Checks if the input string has valid parentheses. @@ -24,18 +22,13 @@ private ValidParentheses() { * @return true if valid, false otherwise */ public static boolean isValid(final String s) { - if (s == null) { - throw new NullPointerException("Input cannot be null"); - } - + if (s == null) throw new NullPointerException("Input cannot be null"); Deque stack = new ArrayDeque<>(); for (char ch : s.toCharArray()) { - if (PAIRS.containsValue(ch)) { // opening bracket + if (PAIRS.containsValue(ch)) { stack.push(ch); - } else if (PAIRS.containsKey(ch)) { // closing bracket - if (stack.isEmpty() || stack.pop() != PAIRS.get(ch)) { - return false; - } + } else if (PAIRS.containsKey(ch)) { + if (stack.isEmpty() || stack.pop() != PAIRS.get(ch)) return false; } } return stack.isEmpty(); diff --git a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java index 95156e58c643..b3689b07d716 100644 --- a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java +++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java @@ -12,32 +12,19 @@ public class ValidParenthesesTest { @ParameterizedTest - @CsvSource({ - "'()', true", - "'()[]{}', true", - "'{[]}', true", - "'', true" - }) + @CsvSource({"'()', true", "'()[]{}', true", "'{[]}', true", "'', true"}) void testValidParentheses(String input, boolean expected) { assertEquals(expected, ValidParentheses.isValid(input)); } @ParameterizedTest - @CsvSource({ - "'(', false", - "')', false", - "'([)]', false", - "'{[}]', false", - "'((()', false" - }) + @CsvSource({"'(', false", "')', false", "'([)]', false", "'{[}]', false", "'((()', false"}) void testInvalidParentheses(String input, boolean expected) { assertEquals(expected, ValidParentheses.isValid(input)); } @ParameterizedTest - @CsvSource({ - "null" - }) + @CsvSource({"null"}) void testNullInput(String input) { assertThrows(NullPointerException.class, () -> ValidParentheses.isValid(null)); } From 50e3c9d32d3247bd4dad412034f1690e3dcfacae Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:42:34 +0530 Subject: [PATCH 7/8] fix: add braces to satisfy Checkstyle NeedBraces rule --- .../thealgorithms/stacks/ValidParentheses.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index 189e110c5d60..f062be9a3b59 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -4,9 +4,6 @@ import java.util.Deque; import java.util.Map; -/** - * Utility class to check if a string has valid parentheses. - */ public final class ValidParentheses { private ValidParentheses() { @@ -15,20 +12,19 @@ private ValidParentheses() { private static final Map PAIRS = Map.of(')', '(', '}', '{', ']', '['); - /** - * Checks if the input string has valid parentheses. - * - * @param s the input string - * @return true if valid, false otherwise - */ public static boolean isValid(final String s) { - if (s == null) throw new NullPointerException("Input cannot be null"); + if (s == null) { + throw new NullPointerException("Input cannot be null"); + } + Deque stack = new ArrayDeque<>(); for (char ch : s.toCharArray()) { if (PAIRS.containsValue(ch)) { stack.push(ch); } else if (PAIRS.containsKey(ch)) { - if (stack.isEmpty() || stack.pop() != PAIRS.get(ch)) return false; + if (stack.isEmpty() || stack.pop() != PAIRS.get(ch)) { + return false; + } } } return stack.isEmpty(); From de372751350523fc6e1e049ea3810d1c7bb71ae5 Mon Sep 17 00:00:00 2001 From: Ruturaj-007 Date: Sat, 11 Oct 2025 22:48:01 +0530 Subject: [PATCH 8/8] fix: refactor if statements to satisfy PMD CollapsibleIfStatements --- .../com/thealgorithms/stacks/ValidParentheses.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java index f062be9a3b59..8ea583cc6475 100644 --- a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -19,10 +19,15 @@ public static boolean isValid(final String s) { Deque stack = new ArrayDeque<>(); for (char ch : s.toCharArray()) { - if (PAIRS.containsValue(ch)) { + if (PAIRS.containsValue(ch)) { // opening bracket stack.push(ch); - } else if (PAIRS.containsKey(ch)) { - if (stack.isEmpty() || stack.pop() != PAIRS.get(ch)) { + } else if (PAIRS.containsKey(ch)) { // closing bracket + // Split logic to satisfy PMD + if (stack.isEmpty()) { + return false; + } + Character top = stack.pop(); + if (top != PAIRS.get(ch)) { return false; } }