From 0a5516569231f5c6daa48f9397447cf063c3d6ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:03:51 +0100 Subject: [PATCH 001/122] chore(deps): bump com.mebigfatguy.fb-contrib:fb-contrib from 7.6.15 to 7.7.0 (#7060) * chore(deps): bump com.mebigfatguy.fb-contrib:fb-contrib Bumps [com.mebigfatguy.fb-contrib:fb-contrib](https://github.com/mebigfatguy/fb-contrib) from 7.6.15 to 7.7.0. - [Commits](https://github.com/mebigfatguy/fb-contrib/commits/v7.7.0) --- updated-dependencies: - dependency-name: com.mebigfatguy.fb-contrib:fb-contrib dependency-version: 7.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * fix: suppress new warnings --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: vil02 <65706193+vil02@users.noreply.github.com> --- pom.xml | 2 +- spotbugs-exclude.xml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e936967208b..e99c8762ea3d 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.mebigfatguy.fb-contrib fb-contrib - 7.6.15 + 7.7.0 com.h3xstream.findsecbugs diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index d2e094556d61..3e2f1ff84ca8 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -201,6 +201,12 @@ + + + + + + From b709941eb688f5c5d4e442eb9ab64568c234814f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:30:27 +0100 Subject: [PATCH 002/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 12.1.1 to 12.1.2 (#7063) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 12.1.1 to 12.1.2. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-12.1.1...checkstyle-12.1.2) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 12.1.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e99c8762ea3d..6126bbe4d6c5 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 12.1.1 + 12.1.2 From a008cc2b0889ed51bf47e673e5f056e97f64cae1 Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Wed, 12 Nov 2025 14:22:21 +0100 Subject: [PATCH 003/122] Update DIRECTORY.md (#6809) Co-authored-by: DenizAltunkapan --- DIRECTORY.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index eca2453fad0f..f15d65c18c3f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -13,6 +13,7 @@ - πŸ“„ [AllPathsFromSourceToTarget](src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java) - πŸ“„ [ArrayCombination](src/main/java/com/thealgorithms/backtracking/ArrayCombination.java) - πŸ“„ [Combination](src/main/java/com/thealgorithms/backtracking/Combination.java) + - πŸ“„ [CombinationSum](src/main/java/com/thealgorithms/backtracking/CombinationSum.java) - πŸ“„ [CrosswordSolver](src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java) - πŸ“„ [FloodFill](src/main/java/com/thealgorithms/backtracking/FloodFill.java) - πŸ“„ [KnightsTour](src/main/java/com/thealgorithms/backtracking/KnightsTour.java) @@ -28,6 +29,7 @@ - πŸ“ **bitmanipulation** - πŸ“„ [BcdConversion](src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java) - πŸ“„ [BinaryPalindromeCheck](src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.java) + - πŸ“„ [BitRotate](src/main/java/com/thealgorithms/bitmanipulation/BitRotate.java) - πŸ“„ [BitSwap](src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java) - πŸ“„ [BitwiseGCD](src/main/java/com/thealgorithms/bitmanipulation/BitwiseGCD.java) - πŸ“„ [BooleanAlgebraGates](src/main/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGates.java) @@ -93,6 +95,12 @@ - πŸ“„ [LFSR](src/main/java/com/thealgorithms/ciphers/a5/LFSR.java) - πŸ“„ [Utils](src/main/java/com/thealgorithms/ciphers/a5/Utils.java) - πŸ“ **compression** + - πŸ“„ [ArithmeticCoding](src/main/java/com/thealgorithms/compression/ArithmeticCoding.java) + - πŸ“„ [BurrowsWheelerTransform](src/main/java/com/thealgorithms/compression/BurrowsWheelerTransform.java) + - πŸ“„ [LZ77](src/main/java/com/thealgorithms/compression/LZ77.java) + - πŸ“„ [LZ78](src/main/java/com/thealgorithms/compression/LZ78.java) + - πŸ“„ [LZW](src/main/java/com/thealgorithms/compression/LZW.java) + - πŸ“„ [MoveToFront](src/main/java/com/thealgorithms/compression/MoveToFront.java) - πŸ“„ [RunLengthEncoding](src/main/java/com/thealgorithms/compression/RunLengthEncoding.java) - πŸ“„ [ShannonFano](src/main/java/com/thealgorithms/compression/ShannonFano.java) - πŸ“ **conversions** @@ -173,7 +181,6 @@ - πŸ“„ [FordFulkerson](src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java) - πŸ“„ [Graphs](src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java) - πŸ“„ [HamiltonianCycle](src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java) - - πŸ“„ [HierholzerAlgorithm](src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java) - πŸ“„ [JohnsonsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java) - πŸ“„ [KahnsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java) - πŸ“„ [Kosaraju](src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java) @@ -358,8 +365,10 @@ - πŸ“„ [WildcardMatching](src/main/java/com/thealgorithms/dynamicprogramming/WildcardMatching.java) - πŸ“„ [WineProblem](src/main/java/com/thealgorithms/dynamicprogramming/WineProblem.java) - πŸ“ **geometry** + - πŸ“„ [BentleyOttmann](src/main/java/com/thealgorithms/geometry/BentleyOttmann.java) - πŸ“„ [BresenhamLine](src/main/java/com/thealgorithms/geometry/BresenhamLine.java) - πŸ“„ [ConvexHull](src/main/java/com/thealgorithms/geometry/ConvexHull.java) + - πŸ“„ [DDALine](src/main/java/com/thealgorithms/geometry/DDALine.java) - πŸ“„ [GrahamScan](src/main/java/com/thealgorithms/geometry/GrahamScan.java) - πŸ“„ [Haversine](src/main/java/com/thealgorithms/geometry/Haversine.java) - πŸ“„ [MidpointCircle](src/main/java/com/thealgorithms/geometry/MidpointCircle.java) @@ -372,10 +381,13 @@ - πŸ“„ [Dinic](src/main/java/com/thealgorithms/graph/Dinic.java) - πŸ“„ [Edmonds](src/main/java/com/thealgorithms/graph/Edmonds.java) - πŸ“„ [EdmondsKarp](src/main/java/com/thealgorithms/graph/EdmondsKarp.java) + - πŸ“„ [HierholzerAlgorithm](src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java) + - πŸ“„ [HierholzerEulerianPath](src/main/java/com/thealgorithms/graph/HierholzerEulerianPath.java) - πŸ“„ [HopcroftKarp](src/main/java/com/thealgorithms/graph/HopcroftKarp.java) - πŸ“„ [HungarianAlgorithm](src/main/java/com/thealgorithms/graph/HungarianAlgorithm.java) - πŸ“„ [PredecessorConstrainedDfs](src/main/java/com/thealgorithms/graph/PredecessorConstrainedDfs.java) - πŸ“„ [PushRelabel](src/main/java/com/thealgorithms/graph/PushRelabel.java) + - πŸ“„ [StoerWagner](src/main/java/com/thealgorithms/graph/StoerWagner.java) - πŸ“„ [StronglyConnectedComponentOptimized](src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java) - πŸ“„ [TravelingSalesman](src/main/java/com/thealgorithms/graph/TravelingSalesman.java) - πŸ“„ [YensKShortestPaths](src/main/java/com/thealgorithms/graph/YensKShortestPaths.java) @@ -420,6 +432,7 @@ - πŸ“„ [BinomialCoefficient](src/main/java/com/thealgorithms/maths/BinomialCoefficient.java) - πŸ“„ [CatalanNumbers](src/main/java/com/thealgorithms/maths/CatalanNumbers.java) - πŸ“„ [Ceil](src/main/java/com/thealgorithms/maths/Ceil.java) + - πŸ“„ [ChebyshevIteration](src/main/java/com/thealgorithms/maths/ChebyshevIteration.java) - πŸ“„ [ChineseRemainderTheorem](src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java) - πŸ“„ [CircularConvolutionFFT](src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java) - πŸ“„ [CollatzConjecture](src/main/java/com/thealgorithms/maths/CollatzConjecture.java) @@ -478,6 +491,7 @@ - πŸ“„ [Median](src/main/java/com/thealgorithms/maths/Median.java) - πŸ“„ [MinValue](src/main/java/com/thealgorithms/maths/MinValue.java) - πŸ“„ [Mode](src/main/java/com/thealgorithms/maths/Mode.java) + - πŸ“„ [Neville](src/main/java/com/thealgorithms/maths/Neville.java) - πŸ“„ [NonRepeatingElement](src/main/java/com/thealgorithms/maths/NonRepeatingElement.java) - πŸ“„ [NthUglyNumber](src/main/java/com/thealgorithms/maths/NthUglyNumber.java) - πŸ“„ [NumberOfDigits](src/main/java/com/thealgorithms/maths/NumberOfDigits.java) @@ -531,6 +545,7 @@ - πŸ“„ [ZellersCongruence](src/main/java/com/thealgorithms/maths/ZellersCongruence.java) - πŸ“ **matrix** - πŸ“„ [InverseOfMatrix](src/main/java/com/thealgorithms/matrix/InverseOfMatrix.java) + - πŸ“„ [LUDecomposition](src/main/java/com/thealgorithms/matrix/LUDecomposition.java) - πŸ“„ [MatrixMultiplication](src/main/java/com/thealgorithms/matrix/MatrixMultiplication.java) - πŸ“„ [MatrixRank](src/main/java/com/thealgorithms/matrix/MatrixRank.java) - πŸ“„ [MatrixTranspose](src/main/java/com/thealgorithms/matrix/MatrixTranspose.java) @@ -591,7 +606,6 @@ - πŸ“„ [PageRank](src/main/java/com/thealgorithms/others/PageRank.java) - πŸ“„ [PasswordGen](src/main/java/com/thealgorithms/others/PasswordGen.java) - πŸ“„ [PerlinNoise](src/main/java/com/thealgorithms/others/PerlinNoise.java) - - πŸ“„ [PrintAMatrixInSpiralOrder](src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java) - πŸ“„ [QueueUsingTwoStacks](src/main/java/com/thealgorithms/others/QueueUsingTwoStacks.java) - πŸ“„ [SkylineProblem](src/main/java/com/thealgorithms/others/SkylineProblem.java) - πŸ“„ [TwoPointers](src/main/java/com/thealgorithms/others/TwoPointers.java) @@ -599,7 +613,14 @@ - πŸ“ **cn** - πŸ“„ [HammingDistance](src/main/java/com/thealgorithms/others/cn/HammingDistance.java) - πŸ“ **physics** + - πŸ“„ [CoulombsLaw](src/main/java/com/thealgorithms/physics/CoulombsLaw.java) + - πŸ“„ [DampedOscillator](src/main/java/com/thealgorithms/physics/DampedOscillator.java) + - πŸ“„ [ElasticCollision2D](src/main/java/com/thealgorithms/physics/ElasticCollision2D.java) + - πŸ“„ [Gravitation](src/main/java/com/thealgorithms/physics/Gravitation.java) - πŸ“„ [GroundToGroundProjectileMotion](src/main/java/com/thealgorithms/physics/GroundToGroundProjectileMotion.java) + - πŸ“„ [Kinematics](src/main/java/com/thealgorithms/physics/Kinematics.java) + - πŸ“„ [ProjectileMotion](src/main/java/com/thealgorithms/physics/ProjectileMotion.java) + - πŸ“„ [SimplePendulumRK4](src/main/java/com/thealgorithms/physics/SimplePendulumRK4.java) - πŸ“ **puzzlesandgames** - πŸ“„ [Sudoku](src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java) - πŸ“„ [TowerOfHanoi](src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java) @@ -649,7 +670,7 @@ - πŸ“„ [BoyerMoore](src/main/java/com/thealgorithms/searches/BoyerMoore.java) - πŸ“„ [BreadthFirstSearch](src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java) - πŸ“„ [DepthFirstSearch](src/main/java/com/thealgorithms/searches/DepthFirstSearch.java) - - πŸ“„ [ExponentalSearch](src/main/java/com/thealgorithms/searches/ExponentalSearch.java) + - πŸ“„ [ExponentialSearch](src/main/java/com/thealgorithms/searches/ExponentialSearch.java) - πŸ“„ [FibonacciSearch](src/main/java/com/thealgorithms/searches/FibonacciSearch.java) - πŸ“„ [HowManyTimesRotated](src/main/java/com/thealgorithms/searches/HowManyTimesRotated.java) - πŸ“„ [InterpolationSearch](src/main/java/com/thealgorithms/searches/InterpolationSearch.java) @@ -758,6 +779,7 @@ - πŸ“„ [SortStack](src/main/java/com/thealgorithms/stacks/SortStack.java) - πŸ“„ [StackPostfixNotation](src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java) - πŸ“„ [StackUsingTwoQueues](src/main/java/com/thealgorithms/stacks/StackUsingTwoQueues.java) + - πŸ“„ [TrappingRainwater](src/main/java/com/thealgorithms/stacks/TrappingRainwater.java) - πŸ“ **strings** - πŸ“„ [AhoCorasick](src/main/java/com/thealgorithms/strings/AhoCorasick.java) - πŸ“„ [Alphabetical](src/main/java/com/thealgorithms/strings/Alphabetical.java) @@ -808,6 +830,7 @@ - πŸ“ **backtracking** - πŸ“„ [AllPathsFromSourceToTargetTest](src/test/java/com/thealgorithms/backtracking/AllPathsFromSourceToTargetTest.java) - πŸ“„ [ArrayCombinationTest](src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java) + - πŸ“„ [CombinationSumTest](src/test/java/com/thealgorithms/backtracking/CombinationSumTest.java) - πŸ“„ [CombinationTest](src/test/java/com/thealgorithms/backtracking/CombinationTest.java) - πŸ“„ [CrosswordSolverTest](src/test/java/com/thealgorithms/backtracking/CrosswordSolverTest.java) - πŸ“„ [FloodFillTest](src/test/java/com/thealgorithms/backtracking/FloodFillTest.java) @@ -824,6 +847,7 @@ - πŸ“ **bitmanipulation** - πŸ“„ [BcdConversionTest](src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java) - πŸ“„ [BinaryPalindromeCheckTest](src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.java) + - πŸ“„ [BitRotateTest](src/test/java/com/thealgorithms/bitmanipulation/BitRotateTest.java) - πŸ“„ [BitSwapTest](src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java) - πŸ“„ [BitwiseGCDTest](src/test/java/com/thealgorithms/bitmanipulation/BitwiseGCDTest.java) - πŸ“„ [BooleanAlgebraGatesTest](src/test/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGatesTest.java) @@ -884,6 +908,12 @@ - πŸ“„ [A5KeyStreamGeneratorTest](src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java) - πŸ“„ [LFSRTest](src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java) - πŸ“ **compression** + - πŸ“„ [ArithmeticCodingTest](src/test/java/com/thealgorithms/compression/ArithmeticCodingTest.java) + - πŸ“„ [BurrowsWheelerTransformTest](src/test/java/com/thealgorithms/compression/BurrowsWheelerTransformTest.java) + - πŸ“„ [LZ77Test](src/test/java/com/thealgorithms/compression/LZ77Test.java) + - πŸ“„ [LZ78Test](src/test/java/com/thealgorithms/compression/LZ78Test.java) + - πŸ“„ [LZWTest](src/test/java/com/thealgorithms/compression/LZWTest.java) + - πŸ“„ [MoveToFrontTest](src/test/java/com/thealgorithms/compression/MoveToFrontTest.java) - πŸ“„ [RunLengthEncodingTest](src/test/java/com/thealgorithms/compression/RunLengthEncodingTest.java) - πŸ“„ [ShannonFanoTest](src/test/java/com/thealgorithms/compression/ShannonFanoTest.java) - πŸ“ **conversions** @@ -1114,8 +1144,10 @@ - πŸ“„ [WildcardMatchingTest](src/test/java/com/thealgorithms/dynamicprogramming/WildcardMatchingTest.java) - πŸ“„ [WineProblemTest](src/test/java/com/thealgorithms/dynamicprogramming/WineProblemTest.java) - πŸ“ **geometry** + - πŸ“„ [BentleyOttmannTest](src/test/java/com/thealgorithms/geometry/BentleyOttmannTest.java) - πŸ“„ [BresenhamLineTest](src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java) - πŸ“„ [ConvexHullTest](src/test/java/com/thealgorithms/geometry/ConvexHullTest.java) + - πŸ“„ [DDALineTest](src/test/java/com/thealgorithms/geometry/DDALineTest.java) - πŸ“„ [GrahamScanTest](src/test/java/com/thealgorithms/geometry/GrahamScanTest.java) - πŸ“„ [HaversineTest](src/test/java/com/thealgorithms/geometry/HaversineTest.java) - πŸ“„ [MidpointCircleTest](src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java) @@ -1128,9 +1160,13 @@ - πŸ“„ [DinicTest](src/test/java/com/thealgorithms/graph/DinicTest.java) - πŸ“„ [EdmondsKarpTest](src/test/java/com/thealgorithms/graph/EdmondsKarpTest.java) - πŸ“„ [EdmondsTest](src/test/java/com/thealgorithms/graph/EdmondsTest.java) + - πŸ“„ [HierholzerAlgorithmTest](src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java) + - πŸ“„ [HierholzerEulerianPathTest](src/test/java/com/thealgorithms/graph/HierholzerEulerianPathTest.java) - πŸ“„ [HopcroftKarpTest](src/test/java/com/thealgorithms/graph/HopcroftKarpTest.java) + - πŸ“„ [HungarianAlgorithmTest](src/test/java/com/thealgorithms/graph/HungarianAlgorithmTest.java) - πŸ“„ [PredecessorConstrainedDfsTest](src/test/java/com/thealgorithms/graph/PredecessorConstrainedDfsTest.java) - πŸ“„ [PushRelabelTest](src/test/java/com/thealgorithms/graph/PushRelabelTest.java) + - πŸ“„ [StoerWagnerTest](src/test/java/com/thealgorithms/graph/StoerWagnerTest.java) - πŸ“„ [StronglyConnectedComponentOptimizedTest](src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java) - πŸ“„ [TravelingSalesmanTest](src/test/java/com/thealgorithms/graph/TravelingSalesmanTest.java) - πŸ“„ [YensKShortestPathsTest](src/test/java/com/thealgorithms/graph/YensKShortestPathsTest.java) @@ -1172,6 +1208,7 @@ - πŸ“„ [BinomialCoefficientTest](src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java) - πŸ“„ [CatalanNumbersTest](src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java) - πŸ“„ [CeilTest](src/test/java/com/thealgorithms/maths/CeilTest.java) + - πŸ“„ [ChebyshevIterationTest](src/test/java/com/thealgorithms/maths/ChebyshevIterationTest.java) - πŸ“„ [ChineseRemainderTheoremTest](src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java) - πŸ“„ [CollatzConjectureTest](src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java) - πŸ“„ [CombinationsTest](src/test/java/com/thealgorithms/maths/CombinationsTest.java) @@ -1211,6 +1248,7 @@ - πŸ“„ [HarshadNumberTest](src/test/java/com/thealgorithms/maths/HarshadNumberTest.java) - πŸ“„ [HeronsFormulaTest](src/test/java/com/thealgorithms/maths/HeronsFormulaTest.java) - πŸ“„ [JosephusProblemTest](src/test/java/com/thealgorithms/maths/JosephusProblemTest.java) + - πŸ“„ [JugglerSequenceTest](src/test/java/com/thealgorithms/maths/JugglerSequenceTest.java) - πŸ“„ [KaprekarNumbersTest](src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java) - πŸ“„ [KaratsubaMultiplicationTest](src/test/java/com/thealgorithms/maths/KaratsubaMultiplicationTest.java) - πŸ“„ [KeithNumberTest](src/test/java/com/thealgorithms/maths/KeithNumberTest.java) @@ -1226,6 +1264,7 @@ - πŸ“„ [MedianTest](src/test/java/com/thealgorithms/maths/MedianTest.java) - πŸ“„ [MinValueTest](src/test/java/com/thealgorithms/maths/MinValueTest.java) - πŸ“„ [ModeTest](src/test/java/com/thealgorithms/maths/ModeTest.java) + - πŸ“„ [NevilleTest](src/test/java/com/thealgorithms/maths/NevilleTest.java) - πŸ“„ [NonRepeatingElementTest](src/test/java/com/thealgorithms/maths/NonRepeatingElementTest.java) - πŸ“„ [NthUglyNumberTest](src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java) - πŸ“„ [NumberOfDigitsTest](src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java) @@ -1275,14 +1314,15 @@ - πŸ“„ [PrimeFactorizationTest](src/test/java/com/thealgorithms/maths/prime/PrimeFactorizationTest.java) - πŸ“ **matrix** - πŸ“„ [InverseOfMatrixTest](src/test/java/com/thealgorithms/matrix/InverseOfMatrixTest.java) + - πŸ“„ [LUDecompositionTest](src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java) - πŸ“„ [MatrixMultiplicationTest](src/test/java/com/thealgorithms/matrix/MatrixMultiplicationTest.java) - πŸ“„ [MatrixRankTest](src/test/java/com/thealgorithms/matrix/MatrixRankTest.java) - πŸ“„ [MatrixTransposeTest](src/test/java/com/thealgorithms/matrix/MatrixTransposeTest.java) - πŸ“„ [MatrixUtilTest](src/test/java/com/thealgorithms/matrix/MatrixUtilTest.java) - πŸ“„ [MedianOfMatrixTest](src/test/java/com/thealgorithms/matrix/MedianOfMatrixTest.java) - πŸ“„ [MirrorOfMatrixTest](src/test/java/com/thealgorithms/matrix/MirrorOfMatrixTest.java) + - πŸ“„ [PrintAMatrixInSpiralOrderTest](src/test/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrderTest.java) - πŸ“„ [SolveSystemTest](src/test/java/com/thealgorithms/matrix/SolveSystemTest.java) - - πŸ“„ [TestPrintMatrixInSpiralOrder](src/test/java/com/thealgorithms/matrix/TestPrintMatrixInSpiralOrder.java) - πŸ“ **misc** - πŸ“„ [ColorContrastRatioTest](src/test/java/com/thealgorithms/misc/ColorContrastRatioTest.java) - πŸ“„ [MapReduceTest](src/test/java/com/thealgorithms/misc/MapReduceTest.java) @@ -1323,13 +1363,19 @@ - πŸ“„ [PerlinNoiseTest](src/test/java/com/thealgorithms/others/PerlinNoiseTest.java) - πŸ“„ [QueueUsingTwoStacksTest](src/test/java/com/thealgorithms/others/QueueUsingTwoStacksTest.java) - πŸ“„ [SkylineProblemTest](src/test/java/com/thealgorithms/others/SkylineProblemTest.java) - - πŸ“„ [TestPrintMatrixInSpiralOrder](src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java) - πŸ“„ [TwoPointersTest](src/test/java/com/thealgorithms/others/TwoPointersTest.java) - πŸ“„ [WorstFitCPUTest](src/test/java/com/thealgorithms/others/WorstFitCPUTest.java) - πŸ“ **cn** - πŸ“„ [HammingDistanceTest](src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java) - πŸ“ **physics** + - πŸ“„ [CoulombsLawTest](src/test/java/com/thealgorithms/physics/CoulombsLawTest.java) + - πŸ“„ [DampedOscillatorTest](src/test/java/com/thealgorithms/physics/DampedOscillatorTest.java) + - πŸ“„ [ElasticCollision2DTest](src/test/java/com/thealgorithms/physics/ElasticCollision2DTest.java) + - πŸ“„ [GravitationTest](src/test/java/com/thealgorithms/physics/GravitationTest.java) - πŸ“„ [GroundToGroundProjectileMotionTest](src/test/java/com/thealgorithms/physics/GroundToGroundProjectileMotionTest.java) + - πŸ“„ [KinematicsTest](src/test/java/com/thealgorithms/physics/KinematicsTest.java) + - πŸ“„ [ProjectileMotionTest](src/test/java/com/thealgorithms/physics/ProjectileMotionTest.java) + - πŸ“„ [SimplePendulumRK4Test](src/test/java/com/thealgorithms/physics/SimplePendulumRK4Test.java) - πŸ“ **puzzlesandgames** - πŸ“„ [SudokuTest](src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java) - πŸ“„ [TowerOfHanoiTest](src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java) @@ -1487,6 +1533,7 @@ - πŸ“„ [SortStackTest](src/test/java/com/thealgorithms/stacks/SortStackTest.java) - πŸ“„ [StackPostfixNotationTest](src/test/java/com/thealgorithms/stacks/StackPostfixNotationTest.java) - πŸ“„ [StackUsingTwoQueuesTest](src/test/java/com/thealgorithms/stacks/StackUsingTwoQueuesTest.java) + - πŸ“„ [TrappingRainwaterTest](src/test/java/com/thealgorithms/stacks/TrappingRainwaterTest.java) - πŸ“ **strings** - πŸ“„ [AhoCorasickTest](src/test/java/com/thealgorithms/strings/AhoCorasickTest.java) - πŸ“„ [AlphabeticalTest](src/test/java/com/thealgorithms/strings/AlphabeticalTest.java) From 8a339ef2e2ad0ec7bc3fb9ce8076fc33a61d548e Mon Sep 17 00:00:00 2001 From: Krishna Date: Sat, 15 Nov 2025 14:52:36 +0530 Subject: [PATCH 004/122] Add Temperature Conversion Utility (#7066) - Implements conversions between Celsius, Fahrenheit, and Kelvin - Includes all 6 conversion methods - Adds comprehensive unit tests with edge cases - Fixes #6936 --- .../conversions/TemperatureConverter.java | 46 ++++++++++++++++ .../conversions/TemperatureConverterTest.java | 54 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/main/java/com/thealgorithms/conversions/TemperatureConverter.java create mode 100644 src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java diff --git a/src/main/java/com/thealgorithms/conversions/TemperatureConverter.java b/src/main/java/com/thealgorithms/conversions/TemperatureConverter.java new file mode 100644 index 000000000000..901db17c665d --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/TemperatureConverter.java @@ -0,0 +1,46 @@ +package com.thealgorithms.conversions; + +/** + * A utility class to convert between different temperature units. + * + *

This class supports conversions between the following units: + *

    + *
  • Celsius
  • + *
  • Fahrenheit
  • + *
  • Kelvin
  • + *
+ * + *

This class is final and cannot be instantiated. + * + * @author krishna-medapati (https://github.com/krishna-medapati) + * @see Wikipedia: Temperature Conversion + */ +public final class TemperatureConverter { + + private TemperatureConverter() { + } + + public static double celsiusToFahrenheit(double celsius) { + return celsius * 9.0 / 5.0 + 32.0; + } + + public static double celsiusToKelvin(double celsius) { + return celsius + 273.15; + } + + public static double fahrenheitToCelsius(double fahrenheit) { + return (fahrenheit - 32.0) * 5.0 / 9.0; + } + + public static double fahrenheitToKelvin(double fahrenheit) { + return (fahrenheit - 32.0) * 5.0 / 9.0 + 273.15; + } + + public static double kelvinToCelsius(double kelvin) { + return kelvin - 273.15; + } + + public static double kelvinToFahrenheit(double kelvin) { + return (kelvin - 273.15) * 9.0 / 5.0 + 32.0; + } +} diff --git a/src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java b/src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java new file mode 100644 index 000000000000..24d55b706f36 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class TemperatureConverterTest { + + private static final double DELTA = 0.01; + + @Test + void testCelsiusToFahrenheit() { + assertEquals(32.0, TemperatureConverter.celsiusToFahrenheit(0.0), DELTA); + assertEquals(212.0, TemperatureConverter.celsiusToFahrenheit(100.0), DELTA); + assertEquals(-40.0, TemperatureConverter.celsiusToFahrenheit(-40.0), DELTA); + assertEquals(98.6, TemperatureConverter.celsiusToFahrenheit(37.0), DELTA); + } + + @Test + void testCelsiusToKelvin() { + assertEquals(273.15, TemperatureConverter.celsiusToKelvin(0.0), DELTA); + assertEquals(373.15, TemperatureConverter.celsiusToKelvin(100.0), DELTA); + assertEquals(233.15, TemperatureConverter.celsiusToKelvin(-40.0), DELTA); + } + + @Test + void testFahrenheitToCelsius() { + assertEquals(0.0, TemperatureConverter.fahrenheitToCelsius(32.0), DELTA); + assertEquals(100.0, TemperatureConverter.fahrenheitToCelsius(212.0), DELTA); + assertEquals(-40.0, TemperatureConverter.fahrenheitToCelsius(-40.0), DELTA); + assertEquals(37.0, TemperatureConverter.fahrenheitToCelsius(98.6), DELTA); + } + + @Test + void testFahrenheitToKelvin() { + assertEquals(273.15, TemperatureConverter.fahrenheitToKelvin(32.0), DELTA); + assertEquals(373.15, TemperatureConverter.fahrenheitToKelvin(212.0), DELTA); + assertEquals(233.15, TemperatureConverter.fahrenheitToKelvin(-40.0), DELTA); + } + + @Test + void testKelvinToCelsius() { + assertEquals(0.0, TemperatureConverter.kelvinToCelsius(273.15), DELTA); + assertEquals(100.0, TemperatureConverter.kelvinToCelsius(373.15), DELTA); + assertEquals(-273.15, TemperatureConverter.kelvinToCelsius(0.0), DELTA); + } + + @Test + void testKelvinToFahrenheit() { + assertEquals(32.0, TemperatureConverter.kelvinToFahrenheit(273.15), DELTA); + assertEquals(212.0, TemperatureConverter.kelvinToFahrenheit(373.15), DELTA); + assertEquals(-40.0, TemperatureConverter.kelvinToFahrenheit(233.15), DELTA); + } +} From 98eecb9f16003e612ad8d84ed6f3c78ae10c7005 Mon Sep 17 00:00:00 2001 From: Taranjeet Singh Kalsi Date: Sat, 15 Nov 2025 15:02:06 +0530 Subject: [PATCH 005/122] Added program to check Smith number (#6955) added smith number program --- .../com/thealgorithms/maths/SmithNumber.java | 52 +++++++++++++++++++ .../thealgorithms/maths/SmithNumberTest.java | 22 ++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/SmithNumber.java create mode 100644 src/test/java/com/thealgorithms/maths/SmithNumberTest.java diff --git a/src/main/java/com/thealgorithms/maths/SmithNumber.java b/src/main/java/com/thealgorithms/maths/SmithNumber.java new file mode 100644 index 000000000000..c06e0023d9bb --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SmithNumber.java @@ -0,0 +1,52 @@ +package com.thealgorithms.maths; + +import com.thealgorithms.maths.Prime.PrimeCheck; + +/** + * In number theory, a smith number is a composite number for which, in a given number base, + * the sum of its digits is equal to the sum of the digits in its prime factorization in the same base. + * + * For example, in base 10, 378 = 21 X 33 X 71 is a Smith number since 3 + 7 + 8 = 2 X 1 + 3 X 3 + 7 X 1, + * and 22 = 21 X 111 is a Smith number, because 2 + 2 = 2 X 1 + (1 + 1) X 1. + * + * Wiki: https://en.wikipedia.org/wiki/Smith_number + */ +public final class SmithNumber { + + private SmithNumber() { + } + + private static int primeFactorDigitSum(int n) { + int sum = 0; + int num = n; + + // Factorize the number using trial division + for (int i = 2; i * i <= num; i++) { + while (n % i == 0) { // while i divides n + sum += SumOfDigits.sumOfDigits(i); // add sum of digits of factor + n /= i; // divide n by the factor + } + } + + // If n is still > 1, it itself is a prime factor + if (n > 1) { + sum += SumOfDigits.sumOfDigits(n); + } + + return sum; + } + + /** + * Check if {@code number} is Smith number or not + * + * @param number the number + * @return {@code true} if {@code number} is a Smith number, otherwise false + */ + public static boolean isSmithNumber(int number) { + if (PrimeCheck.isPrime(number)) { + return false; // Smith numbers must be composite + } + + return SumOfDigits.sumOfDigits(number) == primeFactorDigitSum(number); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SmithNumberTest.java b/src/test/java/com/thealgorithms/maths/SmithNumberTest.java new file mode 100644 index 000000000000..4e2ba0b88e33 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SmithNumberTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class SmithNumberTest { + + @ParameterizedTest + @CsvSource({"4", "22", "121", "562", "985", "4937775"}) + void positiveSmithNumbersTest(int n) { + assertTrue(SmithNumber.isSmithNumber(n)); + } + + @ParameterizedTest + @CsvSource({"2", "11", "100", "550", "999", "1234557"}) + void negativeSmithNumbersTest(int n) { + assertFalse(SmithNumber.isSmithNumber(n)); + } +} From 3979e824b7683c5435df1b8d703433123da9a31c Mon Sep 17 00:00:00 2001 From: Krishna Date: Sat, 15 Nov 2025 23:06:01 +0530 Subject: [PATCH 006/122] Add Power of Four Check using bit manipulation (#7065) * Add Power of Four Check using bit manipulation - Implements isPowerOfFour method using bit manipulation - Checks if number is power of two and has bit at even position - Includes comprehensive unit tests - Fixes #6940 * Fix code formatting in PowerOfFourTest * Move PowerOfFour classes to maths package * Fix package declaration in PowerOfFourTest * Fix code formatting in PowerOfFourTest * Remove redundant import from PowerOfFourTest * Remove unrelated file --- .../com/thealgorithms/maths/PowerOfFour.java | 36 +++++++++++++++++++ .../thealgorithms/maths/PowerOfFourTest.java | 36 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/PowerOfFour.java create mode 100644 src/test/java/com/thealgorithms/maths/PowerOfFourTest.java diff --git a/src/main/java/com/thealgorithms/maths/PowerOfFour.java b/src/main/java/com/thealgorithms/maths/PowerOfFour.java new file mode 100644 index 000000000000..e5fe6255821b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PowerOfFour.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths; + +/** + * Utility class for checking if a number is a power of four. + * A power of four is a number that can be expressed as 4^n where n is a non-negative integer. + * This class provides a method to determine if a given integer is a power of four using bit manipulation. + * + * @author krishna-medapati (https://github.com/krishna-medapati) + */ +public final class PowerOfFour { + private PowerOfFour() { + } + + /** + * Checks if the given integer is a power of four. + * + * A number is considered a power of four if: + * 1. It is greater than zero + * 2. It has exactly one '1' bit in its binary representation (power of two) + * 3. The '1' bit is at an even position (0, 2, 4, 6, ...) + * + * The method uses the mask 0x55555555 (binary: 01010101010101010101010101010101) + * to check if the set bit is at an even position. + * + * @param number the integer to check + * @return true if the number is a power of four, false otherwise + */ + public static boolean isPowerOfFour(int number) { + if (number <= 0) { + return false; + } + boolean isPowerOfTwo = (number & (number - 1)) == 0; + boolean hasEvenBitPosition = (number & 0x55555555) != 0; + return isPowerOfTwo && hasEvenBitPosition; + } +} diff --git a/src/test/java/com/thealgorithms/maths/PowerOfFourTest.java b/src/test/java/com/thealgorithms/maths/PowerOfFourTest.java new file mode 100644 index 000000000000..c91f8b3cf1b5 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PowerOfFourTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class PowerOfFourTest { + + @Test + void testPowersOfFour() { + assertTrue(PowerOfFour.isPowerOfFour(1)); + assertTrue(PowerOfFour.isPowerOfFour(4)); + assertTrue(PowerOfFour.isPowerOfFour(16)); + assertTrue(PowerOfFour.isPowerOfFour(64)); + assertTrue(PowerOfFour.isPowerOfFour(256)); + assertTrue(PowerOfFour.isPowerOfFour(1024)); + } + + @Test + void testNonPowersOfFour() { + assertFalse(PowerOfFour.isPowerOfFour(2)); + assertFalse(PowerOfFour.isPowerOfFour(3)); + assertFalse(PowerOfFour.isPowerOfFour(5)); + assertFalse(PowerOfFour.isPowerOfFour(8)); + assertFalse(PowerOfFour.isPowerOfFour(15)); + assertFalse(PowerOfFour.isPowerOfFour(32)); + } + + @Test + void testEdgeCases() { + assertFalse(PowerOfFour.isPowerOfFour(0)); + assertFalse(PowerOfFour.isPowerOfFour(-1)); + assertFalse(PowerOfFour.isPowerOfFour(-4)); + } +} From 93811614b868b2d8f4e1aaa3f4918982036684ff Mon Sep 17 00:00:00 2001 From: GOPISETTI NAVADEEP <2400030007@kluniversity.in> Date: Sun, 16 Nov 2025 17:24:43 +0530 Subject: [PATCH 007/122] feat: add Count Set Bits algorithm (#7072) * feat: add Count Set Bits algorithm (issue #6931) * fix: correct CountSetBits algorithm logic * style: apply clang-format to CountSetBits files * fix: correct test expectations for CountSetBits * fix: correct test expectations for CountSetBits --- .../bitmanipulation/CountSetBits.java | 114 +++++++++--------- .../bitmanipulation/CountSetBitsTest.java | 56 +++++++-- 2 files changed, 100 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java b/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java index 242f35fc35f2..7df522ca8f69 100644 --- a/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java +++ b/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java @@ -1,79 +1,79 @@ package com.thealgorithms.bitmanipulation; -public class CountSetBits { +/** + * Utility class to count total set bits from 1 to N + * A set bit is a bit in binary representation that is 1 + * + * @author navadeep + */ +public final class CountSetBits { + + private CountSetBits() { + // Utility class, prevent instantiation + } /** - * The below algorithm is called as Brian Kernighan's algorithm - * We can use Brian Kernighan’s algorithm to improve the above naive algorithm’s performance. - The idea is to only consider the set bits of an integer by turning off its rightmost set bit - (after counting it), so the next iteration of the loop considers the next rightmost bit. - - The expression n & (n-1) can be used to turn off the rightmost set bit of a number n. This - works as the expression n-1 flips all the bits after the rightmost set bit of n, including the - rightmost set bit itself. Therefore, n & (n-1) results in the last bit flipped of n. - - For example, consider number 52, which is 00110100 in binary, and has a total 3 bits set. - - 1st iteration of the loop: n = 52 - - 00110100 & (n) - 00110011 (n-1) - ~~~~~~~~ - 00110000 + * Counts total number of set bits in all numbers from 1 to n + * Time Complexity: O(log n) + * + * @param n the upper limit (inclusive) + * @return total count of set bits from 1 to n + * @throws IllegalArgumentException if n is negative + */ + public static int countSetBits(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input must be non-negative"); + } + if (n == 0) { + return 0; + } - 2nd iteration of the loop: n = 48 + // Find the largest power of 2 <= n + int x = largestPowerOf2InNumber(n); - 00110000 & (n) - 00101111 (n-1) - ~~~~~~~~ - 00100000 + // Total bits at position x: x * 2^(x-1) + int bitsAtPositionX = x * (1 << (x - 1)); + // Remaining numbers after 2^x + int remainingNumbers = n - (1 << x) + 1; - 3rd iteration of the loop: n = 32 + // Recursively count for the rest + int rest = countSetBits(n - (1 << x)); - 00100000 & (n) - 00011111 (n-1) - ~~~~~~~~ - 00000000 (n = 0) + return bitsAtPositionX + remainingNumbers + rest; + } - * @param num takes Long number whose number of set bit is to be found - * @return the count of set bits in the binary equivalent - */ - public long countSetBits(long num) { - long cnt = 0; - while (num > 0) { - cnt++; - num &= (num - 1); + /** + * Finds the position of the most significant bit in n + * + * @param n the number + * @return position of MSB (0-indexed from right) + */ + private static int largestPowerOf2InNumber(int n) { + int position = 0; + while ((1 << position) <= n) { + position++; } - return cnt; + return position - 1; } /** - * This approach takes O(1) running time to count the set bits, but requires a pre-processing. + * Alternative naive approach - counts set bits by iterating through all numbers + * Time Complexity: O(n log n) * - * So, we divide our 32-bit input into 8-bit chunks, with four chunks. We have 8 bits in each chunk. - * - * Then the range is from 0-255 (0 to 2^7). - * So, we may need to count set bits from 0 to 255 in individual chunks. - * - * @param num takes a long number - * @return the count of set bits in the binary equivalent + * @param n the upper limit (inclusive) + * @return total count of set bits from 1 to n */ - public int lookupApproach(int num) { - int[] table = new int[256]; - table[0] = 0; - - for (int i = 1; i < 256; i++) { - table[i] = (i & 1) + table[i >> 1]; // i >> 1 equals to i/2 + public static int countSetBitsNaive(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input must be non-negative"); } - int res = 0; - for (int i = 0; i < 4; i++) { - res += table[num & 0xff]; - num >>= 8; + int count = 0; + for (int i = 1; i <= n; i++) { + count += Integer.bitCount(i); } - - return res; + return count; } } diff --git a/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java index 61e0757f9c12..757c6edc0151 100644 --- a/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java +++ b/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java @@ -1,26 +1,56 @@ package com.thealgorithms.bitmanipulation; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; -public class CountSetBitsTest { +class CountSetBitsTest { @Test - void testSetBits() { - CountSetBits csb = new CountSetBits(); - assertEquals(1L, csb.countSetBits(16)); - assertEquals(4, csb.countSetBits(15)); - assertEquals(5, csb.countSetBits(10000)); - assertEquals(5, csb.countSetBits(31)); + void testCountSetBitsZero() { + assertEquals(0, CountSetBits.countSetBits(0)); } @Test - void testSetBitsLookupApproach() { - CountSetBits csb = new CountSetBits(); - assertEquals(1L, csb.lookupApproach(16)); - assertEquals(4, csb.lookupApproach(15)); - assertEquals(5, csb.lookupApproach(10000)); - assertEquals(5, csb.lookupApproach(31)); + void testCountSetBitsOne() { + assertEquals(1, CountSetBits.countSetBits(1)); + } + + @Test + void testCountSetBitsSmallNumber() { + assertEquals(4, CountSetBits.countSetBits(3)); // 1(1) + 10(1) + 11(2) = 4 + } + + @Test + void testCountSetBitsFive() { + assertEquals(7, CountSetBits.countSetBits(5)); // 1 + 1 + 2 + 1 + 2 = 7 + } + + @Test + void testCountSetBitsTen() { + assertEquals(17, CountSetBits.countSetBits(10)); + } + + @Test + void testCountSetBitsLargeNumber() { + assertEquals(42, CountSetBits.countSetBits(20)); // Changed from 93 to 42 + } + + @Test + void testCountSetBitsPowerOfTwo() { + assertEquals(13, CountSetBits.countSetBits(8)); // Changed from 9 to 13 + } + + @Test + void testCountSetBitsNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> CountSetBits.countSetBits(-1)); + } + + @Test + void testNaiveApproachMatchesOptimized() { + for (int i = 0; i <= 100; i++) { + assertEquals(CountSetBits.countSetBitsNaive(i), CountSetBits.countSetBits(i), "Mismatch at n = " + i); + } } } From c6880c195d778b1d19fc13d4be1ae09dc355de2f Mon Sep 17 00:00:00 2001 From: GOPISETTI NAVADEEP <2400030007@kluniversity.in> Date: Sun, 16 Nov 2025 17:30:21 +0530 Subject: [PATCH 008/122] feat: add Sieve of Eratosthenes algorithm (#7071) * feat: add Sieve of Eratosthenes algorithm - Implement Sieve of Eratosthenes for finding prime numbers up to n - Add comprehensive unit tests with edge cases - Include JavaDoc documentation - Time complexity: O(n log log n) - Space complexity: O(n) Resolves #6939 * fix: remove trailing spaces * fix: apply clang-format --- .../maths/SieveOfEratosthenes.java | 98 +++++++++++-------- .../maths/SieveOfEratosthenesTest.java | 60 ++++++++---- 2 files changed, 96 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java b/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java index f22d22e8c6af..5a15c4201a15 100644 --- a/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java +++ b/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java @@ -1,66 +1,82 @@ package com.thealgorithms.maths; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; /** - * @brief utility class implementing Sieve of Eratosthenes + * Sieve of Eratosthenes Algorithm + * An efficient algorithm to find all prime numbers up to a given limit. + * + * Algorithm: + * 1. Create a boolean array of size n+1, initially all true + * 2. Mark 0 and 1 as not prime + * 3. For each number i from 2 to sqrt(n): + * - If i is still marked as prime + * - Mark all multiples of i (starting from iΒ²) as not prime + * 4. Collect all numbers still marked as prime + * + * Time Complexity: O(n log log n) + * Space Complexity: O(n) + * + * @author Navadeep0007 + * @see Sieve of Eratosthenes */ public final class SieveOfEratosthenes { + private SieveOfEratosthenes() { + // Utility class, prevent instantiation } - private static void checkInput(int n) { - if (n <= 0) { - throw new IllegalArgumentException("n must be positive."); + /** + * Finds all prime numbers up to n using the Sieve of Eratosthenes algorithm + * + * @param n the upper limit (inclusive) + * @return a list of all prime numbers from 2 to n + * @throws IllegalArgumentException if n is negative + */ + public static List findPrimes(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input must be non-negative"); } - } - private static Type[] sievePrimesTill(int n) { - checkInput(n); - Type[] isPrimeArray = new Type[n + 1]; - Arrays.fill(isPrimeArray, Type.PRIME); - isPrimeArray[0] = Type.NOT_PRIME; - isPrimeArray[1] = Type.NOT_PRIME; + if (n < 2) { + return new ArrayList<>(); + } + + // Create boolean array, initially all true + boolean[] isPrime = new boolean[n + 1]; + for (int i = 2; i <= n; i++) { + isPrime[i] = true; + } - double cap = Math.sqrt(n); - for (int i = 2; i <= cap; i++) { - if (isPrimeArray[i] == Type.PRIME) { - for (int j = 2; i * j <= n; j++) { - isPrimeArray[i * j] = Type.NOT_PRIME; + // Sieve process + for (int i = 2; i * i <= n; i++) { + if (isPrime[i]) { + // Mark all multiples of i as not prime + for (int j = i * i; j <= n; j += i) { + isPrime[j] = false; } } } - return isPrimeArray; - } - - private static int countPrimes(Type[] isPrimeArray) { - return (int) Arrays.stream(isPrimeArray).filter(element -> element == Type.PRIME).count(); - } - private static int[] extractPrimes(Type[] isPrimeArray) { - int numberOfPrimes = countPrimes(isPrimeArray); - int[] primes = new int[numberOfPrimes]; - int primeIndex = 0; - for (int curNumber = 0; curNumber < isPrimeArray.length; ++curNumber) { - if (isPrimeArray[curNumber] == Type.PRIME) { - primes[primeIndex++] = curNumber; + // Collect all prime numbers + List primes = new ArrayList<>(); + for (int i = 2; i <= n; i++) { + if (isPrime[i]) { + primes.add(i); } } + return primes; } /** - * @brief finds all of the prime numbers up to the given upper (inclusive) limit - * @param n upper (inclusive) limit - * @exception IllegalArgumentException n is non-positive - * @return the array of all primes up to the given number (inclusive) + * Counts the number of prime numbers up to n + * + * @param n the upper limit (inclusive) + * @return count of prime numbers from 2 to n */ - public static int[] findPrimesTill(int n) { - return extractPrimes(sievePrimesTill(n)); - } - - private enum Type { - PRIME, - NOT_PRIME, + public static int countPrimes(int n) { + return findPrimes(n).size(); } } diff --git a/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java b/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java index ebbd5df712fc..5d491a493ee7 100644 --- a/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java +++ b/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java @@ -1,46 +1,64 @@ package com.thealgorithms.maths; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.Test; +/** + * Test cases for Sieve of Eratosthenes algorithm + * + * @author Navadeep0007 + */ class SieveOfEratosthenesTest { + + @Test + void testPrimesUpTo10() { + List expected = Arrays.asList(2, 3, 5, 7); + assertEquals(expected, SieveOfEratosthenes.findPrimes(10)); + } + + @Test + void testPrimesUpTo30() { + List expected = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29); + assertEquals(expected, SieveOfEratosthenes.findPrimes(30)); + } + @Test - public void testfFindPrimesTill1() { - assertArrayEquals(new int[] {}, SieveOfEratosthenes.findPrimesTill(1)); + void testPrimesUpTo2() { + List expected = Arrays.asList(2); + assertEquals(expected, SieveOfEratosthenes.findPrimes(2)); } @Test - public void testfFindPrimesTill2() { - assertArrayEquals(new int[] {2}, SieveOfEratosthenes.findPrimesTill(2)); + void testPrimesUpTo1() { + assertTrue(SieveOfEratosthenes.findPrimes(1).isEmpty()); } @Test - public void testfFindPrimesTill4() { - var primesTill4 = new int[] {2, 3}; - assertArrayEquals(primesTill4, SieveOfEratosthenes.findPrimesTill(3)); - assertArrayEquals(primesTill4, SieveOfEratosthenes.findPrimesTill(4)); + void testPrimesUpTo0() { + assertTrue(SieveOfEratosthenes.findPrimes(0).isEmpty()); } @Test - public void testfFindPrimesTill40() { - var primesTill40 = new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; - assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(37)); - assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(38)); - assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(39)); - assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(40)); + void testNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> { SieveOfEratosthenes.findPrimes(-1); }); } @Test - public void testfFindPrimesTill240() { - var primesTill240 = new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239}; - assertArrayEquals(primesTill240, SieveOfEratosthenes.findPrimesTill(239)); - assertArrayEquals(primesTill240, SieveOfEratosthenes.findPrimesTill(240)); + void testCountPrimes() { + assertEquals(4, SieveOfEratosthenes.countPrimes(10)); + assertEquals(25, SieveOfEratosthenes.countPrimes(100)); } @Test - public void testFindPrimesTillThrowsExceptionForNonPositiveInput() { - assertThrows(IllegalArgumentException.class, () -> SieveOfEratosthenes.findPrimesTill(0)); + void testLargeNumber() { + List primes = SieveOfEratosthenes.findPrimes(1000); + assertEquals(168, primes.size()); // There are 168 primes up to 1000 + assertEquals(2, primes.get(0)); // First prime + assertEquals(997, primes.get(primes.size() - 1)); // Last prime up to 1000 } } From cff5d3662e5446ff3371adcb5278ccb2085c956f Mon Sep 17 00:00:00 2001 From: SeungHyeok Yun <162292720+SeungHyeokYoon@users.noreply.github.com> Date: Mon, 17 Nov 2025 03:49:30 +0900 Subject: [PATCH 009/122] feat: add IndexedPriorityQueue implementation and tests (#7062) * feat: add IndexedPriorityQueue implementation and tests * mod : clang-format * Fix Checkstyle naming for IndexedPriorityQueue tests * Align IndexedPriorityQueue tests with Checkstyle and clang-format --- .../heaps/IndexedPriorityQueue.java | 327 ++++++++++++++++ .../heaps/IndexedPriorityQueueTest.java | 350 ++++++++++++++++++ 2 files changed, 677 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java create mode 100644 src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java b/src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java new file mode 100644 index 000000000000..ad7229760fd0 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java @@ -0,0 +1,327 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.IdentityHashMap; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * An addressable (indexed) min-priority queue with O(log n) updates. + * + *

Key features: + *

    + *
  • Each element E is tracked by a handle (its current heap index) via a map, + * enabling O(log n) {@code remove(e)} and O(log n) key updates + * ({@code changeKey/decreaseKey/increaseKey}).
  • + *
  • The queue order is determined by the provided {@link Comparator}. If the + * comparator is {@code null}, elements must implement {@link Comparable} + * (same contract as {@link java.util.PriorityQueue}).
  • + *
  • By default this implementation uses {@link IdentityHashMap} for the index + * mapping to avoid issues with duplicate-equals elements or mutable equals/hashCode. + * If you need value-based equality, switch to {@code HashMap} and read the caveats + * in the class-level Javadoc carefully.
  • + *
+ * + *

IMPORTANT contracts

+ *
    + *
  • Do not mutate comparator-relevant fields of an element directly while it is + * inside the queue. Always use {@code changeKey}/{@code decreaseKey}/{@code increaseKey} + * so the heap can be restored accordingly.
  • + *
  • If you replace {@link IdentityHashMap} with {@link HashMap}, you must ensure: + * (a) no two distinct elements are {@code equals()}-equal at the same time in the queue, and + * (b) {@code equals/hashCode} of elements remain stable while enqueued.
  • + *
  • {@code peek()} returns {@code null} when empty (matching {@link java.util.PriorityQueue}).
  • + *
  • Not thread-safe.
  • + *
+ * + *

Complexities: + * {@code offer, poll, remove(e), changeKey, decreaseKey, increaseKey} are O(log n); + * {@code peek, isEmpty, size, contains} are O(1). + */ +public class IndexedPriorityQueue { + + /** Binary heap storage (min-heap). */ + private Object[] heap; + + /** Number of elements in the heap. */ + private int size; + + /** Comparator used for ordering; if null, elements must be Comparable. */ + private final Comparator cmp; + + /** + * Index map: element -> current heap index. + *

We use IdentityHashMap by default to: + *

    + *
  • allow duplicate-equals elements;
  • + *
  • avoid corruption when equals/hashCode are mutable or not ID-based.
  • + *
+ * If you prefer value-based semantics, replace with HashMap and + * respect the warnings in the class Javadoc. + */ + private final IdentityHashMap index; + + private static final int DEFAULT_INITIAL_CAPACITY = 11; + + public IndexedPriorityQueue() { + this(DEFAULT_INITIAL_CAPACITY, null); + } + + public IndexedPriorityQueue(Comparator cmp) { + this(DEFAULT_INITIAL_CAPACITY, cmp); + } + + public IndexedPriorityQueue(int initialCapacity, Comparator cmp) { + if (initialCapacity < 1) { + throw new IllegalArgumentException("initialCapacity < 1"); + } + this.heap = new Object[initialCapacity]; + this.cmp = cmp; + this.index = new IdentityHashMap<>(); + } + + /** Returns current number of elements. */ + public int size() { + return size; + } + + /** Returns {@code true} if empty. */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the minimum element without removing it, or {@code null} if empty. + * Matches {@link java.util.PriorityQueue#peek()} behavior. + */ + @SuppressWarnings("unchecked") + public E peek() { + return size == 0 ? null : (E) heap[0]; + } + + /** + * Inserts the specified element (O(log n)). + * @throws NullPointerException if {@code e} is null + * @throws ClassCastException if {@code cmp == null} and {@code e} is not Comparable, + * or if incompatible with other elements + */ + public boolean offer(E e) { + Objects.requireNonNull(e, "element is null"); + if (size >= heap.length) { + grow(size + 1); + } + // Insert at the end and bubble up. siftUp will maintain 'index' for all touched nodes. + siftUp(size, e); + size++; + return true; + } + + /** + * Removes and returns the minimum element (O(log n)), or {@code null} if empty. + */ + @SuppressWarnings("unchecked") + public E poll() { + if (size == 0) { + return null; + } + E min = (E) heap[0]; + removeAt(0); // updates map and heap structure + return min; + } + + /** + * Removes one occurrence of the specified element e (O(log n)) if present. + * Uses the index map for O(1) lookup. + */ + public boolean remove(Object o) { + Integer i = index.get(o); + if (i == null) { + return false; + } + removeAt(i); + return true; + } + + /** O(1): returns whether the queue currently contains the given element reference. */ + public boolean contains(Object o) { + return index.containsKey(o); + } + + /** Clears the heap and the index map. */ + public void clear() { + Arrays.fill(heap, 0, size, null); + index.clear(); + size = 0; + } + + // ------------------------------------------------------------------------------------ + // Key update API + // ------------------------------------------------------------------------------------ + + /** + * Changes comparator-relevant fields of {@code e} via the provided {@code mutator}, + * then restores the heap in O(log n) by bubbling in the correct direction. + * + *

IMPORTANT: The mutator must not change {@code equals/hashCode} of {@code e} + * if you migrate this implementation to value-based indexing (HashMap). + * + * @throws IllegalArgumentException if {@code e} is not in the queue + */ + public void changeKey(E e, Consumer mutator) { + Integer i = index.get(e); + if (i == null) { + throw new IllegalArgumentException("Element not in queue"); + } + // Mutate fields used by comparator (do NOT mutate equality/hash if using value-based map) + mutator.accept(e); + // Try bubbling up; if no movement occurred, bubble down. + if (!siftUp(i)) { + siftDown(i); + } + } + + /** + * Faster variant if the new key is strictly smaller (higher priority). + * Performs a single sift-up (O(log n)). + */ + public void decreaseKey(E e, Consumer mutator) { + Integer i = index.get(e); + if (i == null) { + throw new IllegalArgumentException("Element not in queue"); + } + mutator.accept(e); + siftUp(i); + } + + /** + * Faster variant if the new key is strictly larger (lower priority). + * Performs a single sift-down (O(log n)). + */ + public void increaseKey(E e, Consumer mutator) { + Integer i = index.get(e); + if (i == null) { + throw new IllegalArgumentException("Element not in queue"); + } + mutator.accept(e); + siftDown(i); + } + + // ------------------------------------------------------------------------------------ + // Internal utilities + // ------------------------------------------------------------------------------------ + + /** Grows the internal array to accommodate at least {@code minCapacity}. */ + private void grow(int minCapacity) { + int old = heap.length; + int pref = (old < 64) ? old + 2 : old + (old >> 1); // +2 if small, else +50% + int newCap = Math.max(minCapacity, pref); + heap = Arrays.copyOf(heap, newCap); + } + + @SuppressWarnings("unchecked") + private int compare(E a, E b) { + if (cmp != null) { + return cmp.compare(a, b); + } + return ((Comparable) a).compareTo(b); + } + + /** + * Inserts item {@code x} at position {@code k}, bubbling up while maintaining the heap. + * Also maintains the index map for all moved elements. + */ + @SuppressWarnings("unchecked") + private void siftUp(int k, E x) { + while (k > 0) { + int p = (k - 1) >>> 1; + E e = (E) heap[p]; + if (compare(x, e) >= 0) { + break; + } + heap[k] = e; + index.put(e, k); + k = p; + } + heap[k] = x; + index.put(x, k); + } + + /** + * Attempts to bubble up the element currently at {@code k}. + * @return true if it moved; false otherwise. + */ + @SuppressWarnings("unchecked") + private boolean siftUp(int k) { + int orig = k; + E x = (E) heap[k]; + while (k > 0) { + int p = (k - 1) >>> 1; + E e = (E) heap[p]; + if (compare(x, e) >= 0) { + break; + } + heap[k] = e; + index.put(e, k); + k = p; + } + if (k != orig) { + heap[k] = x; + index.put(x, k); + return true; + } + return false; + } + + /** Bubbles down the element currently at {@code k}. */ + @SuppressWarnings("unchecked") + private void siftDown(int k) { + int n = size; + E x = (E) heap[k]; + int half = n >>> 1; // loop while k has at least one child + while (k < half) { + int child = (k << 1) + 1; // assume left is smaller + E c = (E) heap[child]; + int r = child + 1; + if (r < n && compare(c, (E) heap[r]) > 0) { + child = r; + c = (E) heap[child]; + } + if (compare(x, c) <= 0) { + break; + } + heap[k] = c; + index.put(c, k); + k = child; + } + heap[k] = x; + index.put(x, k); + } + + /** + * Removes the element at heap index {@code i}, restoring the heap afterwards. + *

Returns nothing; the standard {@code PriorityQueue} returns a displaced + * element in a rare case to help its iterator. We don't need that here, so + * we keep the API simple. + */ + @SuppressWarnings("unchecked") + private void removeAt(int i) { + int n = --size; // last index after removal + E moved = (E) heap[n]; + E removed = (E) heap[i]; + heap[n] = null; // help GC + index.remove(removed); // drop mapping for removed element + + if (i == n) { + return; // removed last element; done + } + + heap[i] = moved; + index.put(moved, i); + + // Try sift-up first (cheap if key decreased); if no movement, sift-down. + if (!siftUp(i)) { + siftDown(i); + } + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java new file mode 100644 index 000000000000..8d8c4e1db6bd --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java @@ -0,0 +1,350 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.Comparator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link IndexedPriorityQueue}. + * + * Notes: + * - We mainly use a Node class with a mutable "prio" field to test changeKey/decreaseKey/increaseKey. + * - The queue is a min-heap, so smaller "prio" means higher priority. + * - By default the implementation uses IdentityHashMap so duplicate-equals objects are allowed. + */ +public class IndexedPriorityQueueTest { + + // ------------------------ + // Helpers + // ------------------------ + + /** Simple payload with mutable priority. */ + static class Node { + final String id; + int prio; // lower is better (min-heap) + + Node(String id, int prio) { + this.id = id; + this.prio = prio; + } + + @Override + public String toString() { + return id + "(" + prio + ")"; + } + } + + /** Same as Node but overrides equals/hashCode to simulate "duplicate-equals" scenario. */ + static class NodeWithEquals { + final String id; + int prio; + + NodeWithEquals(String id, int prio) { + this.id = id; + this.prio = prio; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof NodeWithEquals)) { + return false; + } + NodeWithEquals other = (NodeWithEquals) o; + // Intentionally naive equality: equal if priority is equal + return this.prio == other.prio; + } + + @Override + public int hashCode() { + return Integer.hashCode(prio); + } + + @Override + public String toString() { + return id + "(" + prio + ")"; + } + } + + private static IndexedPriorityQueue newNodePQ() { + return new IndexedPriorityQueue<>(Comparator.comparingInt(n -> n.prio)); + } + + // ------------------------ + // Basic operations + // ------------------------ + + @Test + void testOfferPollWithIntegersComparableMode() { + // cmp == null -> elements must be Comparable + IndexedPriorityQueue pq = new IndexedPriorityQueue<>(); + Assertions.assertTrue(pq.isEmpty()); + + pq.offer(5); + pq.offer(1); + pq.offer(3); + + Assertions.assertEquals(3, pq.size()); + Assertions.assertEquals(1, pq.peek()); + Assertions.assertEquals(1, pq.poll()); + Assertions.assertEquals(3, pq.poll()); + Assertions.assertEquals(5, pq.poll()); + Assertions.assertNull(pq.poll()); // empty -> null + Assertions.assertTrue(pq.isEmpty()); + } + + @Test + void testPeekAndIsEmpty() { + IndexedPriorityQueue pq = newNodePQ(); + Assertions.assertTrue(pq.isEmpty()); + Assertions.assertNull(pq.peek()); + + pq.offer(new Node("A", 10)); + pq.offer(new Node("B", 5)); + pq.offer(new Node("C", 7)); + + Assertions.assertFalse(pq.isEmpty()); + Assertions.assertEquals("B(5)", pq.peek().toString()); + } + + @Test + void testRemoveSpecificElement() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 10); + Node b = new Node("B", 5); + Node c = new Node("C", 7); + + pq.offer(a); + pq.offer(b); + pq.offer(c); + + // remove by reference (O(log n)) + Assertions.assertTrue(pq.remove(b)); + Assertions.assertEquals(2, pq.size()); + // now min should be C(7) + Assertions.assertEquals("C(7)", pq.peek().toString()); + // removing an element not present -> false + Assertions.assertFalse(pq.remove(b)); + } + + @Test + void testContainsAndClear() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 2); + Node b = new Node("B", 3); + + pq.offer(a); + pq.offer(b); + + Assertions.assertTrue(pq.contains(a)); + Assertions.assertTrue(pq.contains(b)); + + pq.clear(); + Assertions.assertTrue(pq.isEmpty()); + Assertions.assertFalse(pq.contains(a)); + Assertions.assertNull(pq.peek()); + } + + // ------------------------ + // Key updates + // ------------------------ + + @Test + void testDecreaseKeyMovesUp() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 10); + Node b = new Node("B", 5); + Node c = new Node("C", 7); + + pq.offer(a); + pq.offer(b); + pq.offer(c); + + // current min is B(5) + Assertions.assertEquals("B(5)", pq.peek().toString()); + + // Make A more important: 10 -> 1 (smaller is better) + pq.decreaseKey(a, n -> n.prio = 1); + + // Now A should be at the top + Assertions.assertEquals("A(1)", pq.peek().toString()); + } + + @Test + void testIncreaseKeyMovesDown() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 1); + Node b = new Node("B", 2); + Node c = new Node("C", 3); + + pq.offer(a); + pq.offer(b); + pq.offer(c); + + // min is A(1) + Assertions.assertEquals("A(1)", pq.peek().toString()); + + // Make A worse: 1 -> 100 + pq.increaseKey(a, n -> n.prio = 100); + + // Now min should be B(2) + Assertions.assertEquals("B(2)", pq.peek().toString()); + } + + @Test + void testChangeKeyChoosesDirectionAutomatically() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 10); + Node b = new Node("B", 20); + Node c = new Node("C", 30); + + pq.offer(a); + pq.offer(b); + pq.offer(c); + + // Decrease B to 0 -> should move up + pq.changeKey(b, n -> n.prio = 0); + Assertions.assertEquals("B(0)", pq.peek().toString()); + + // Increase B to 100 -> should move down + pq.changeKey(b, n -> n.prio = 100); + Assertions.assertEquals("A(10)", pq.peek().toString()); + } + + @Test + void testDirectMutationWithoutChangeKeyDoesNotReheapByDesign() { + // Demonstrates the contract: do NOT mutate comparator fields directly. + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 5); + Node b = new Node("B", 10); + + pq.offer(a); + pq.offer(b); + + // Illegally mutate priority directly + a.prio = 100; // worse than b now, but heap wasn't notified + + // The heap structure is unchanged; peek still returns A(100) (was A(5) before) + // This test documents the behavior/contract rather than relying on it. + Assertions.assertEquals("A(100)", pq.peek().toString()); + + // Now fix properly via changeKey (no change in value, but triggers reheap) + pq.changeKey(a, n -> n.prio = n.prio); + Assertions.assertEquals("B(10)", pq.peek().toString()); + } + + // ------------------------ + // Identity semantics & duplicates + // ------------------------ + + @Test + void testDuplicateEqualsElementsAreSupportedIdentityMap() { + IndexedPriorityQueue pq = new IndexedPriorityQueue<>(Comparator.comparingInt(n -> n.prio)); + + NodeWithEquals x1 = new NodeWithEquals("X1", 7); + NodeWithEquals x2 = new NodeWithEquals("X2", 7); // equals to X1 by prio, but different instance + + // With IdentityHashMap internally, both can coexist + pq.offer(x1); + pq.offer(x2); + + Assertions.assertEquals(2, pq.size()); + // Poll twice; both 7s should be returned (order between x1/x2 is unspecified) + Assertions.assertEquals(7, pq.poll().prio); + Assertions.assertEquals(7, pq.poll().prio); + Assertions.assertTrue(pq.isEmpty()); + } + + // ------------------------ + // Capacity growth + // ------------------------ + + @Test + void testGrowByManyInserts() { + IndexedPriorityQueue pq = new IndexedPriorityQueue<>(); + int n = 100; // beyond default capacity (11) + + for (int i = n; i >= 1; i--) { + pq.offer(i); + } + + Assertions.assertEquals(n, pq.size()); + // Ensure min-to-max order when polling + for (int expected = 1; expected <= n; expected++) { + Integer v = pq.poll(); + Assertions.assertEquals(expected, v); + } + Assertions.assertTrue(pq.isEmpty()); + Assertions.assertNull(pq.poll()); + } + + // ------------------------ + // remove/contains edge cases + // ------------------------ + + @Test + void testRemoveHeadAndMiddleAndTail() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 1); + Node b = new Node("B", 2); + Node c = new Node("C", 3); + Node d = new Node("D", 4); + + pq.offer(a); + pq.offer(b); + pq.offer(c); + pq.offer(d); + + // remove head + Assertions.assertTrue(pq.remove(a)); + Assertions.assertFalse(pq.contains(a)); + Assertions.assertEquals("B(2)", pq.peek().toString()); + + // remove middle + Assertions.assertTrue(pq.remove(c)); + Assertions.assertFalse(pq.contains(c)); + Assertions.assertEquals("B(2)", pq.peek().toString()); + + // remove tail (last) + Assertions.assertTrue(pq.remove(d)); + Assertions.assertFalse(pq.contains(d)); + Assertions.assertEquals("B(2)", pq.peek().toString()); + + // remove last remaining + Assertions.assertTrue(pq.remove(b)); + Assertions.assertTrue(pq.isEmpty()); + Assertions.assertNull(pq.peek()); + } + + // ------------------------ + // Error / edge cases for coverage + // ------------------------ + + @Test + void testInvalidInitialCapacityThrows() { + Assertions.assertThrows(IllegalArgumentException.class, () -> new IndexedPriorityQueue(0, Comparator.naturalOrder())); + } + + @Test + void testChangeKeyOnMissingElementThrows() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 10); + + Assertions.assertThrows(IllegalArgumentException.class, () -> pq.changeKey(a, n -> n.prio = 5)); + } + + @Test + void testDecreaseKeyOnMissingElementThrows() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 10); + + Assertions.assertThrows(IllegalArgumentException.class, () -> pq.decreaseKey(a, n -> n.prio = 5)); + } + + @Test + void testIncreaseKeyOnMissingElementThrows() { + IndexedPriorityQueue pq = newNodePQ(); + Node a = new Node("A", 10); + + Assertions.assertThrows(IllegalArgumentException.class, () -> pq.increaseKey(a, n -> n.prio = 15)); + } +} From 9f2b675f3e47c49bf4d1623d9d04f95506e2f813 Mon Sep 17 00:00:00 2001 From: GOPISETTI NAVADEEP <2400030007@kluniversity.in> Date: Mon, 17 Nov 2025 14:24:20 +0530 Subject: [PATCH 010/122] feat: add Sudoku Solver using Backtracking (#7073) * feat: add Sudoku Solver using Backtracking (issue #6929) * refactor: remove old Sudoku class from puzzlesandgames package * Remove old Sudoku implementation and its test class --- .../backtracking/SudokuSolver.java | 157 ++++++++++++++++ .../thealgorithms/puzzlesandgames/Sudoku.java | 169 ------------------ .../backtracking/SudokuSolverTest.java | 53 ++++++ .../puzzlesandgames/SudokuTest.java | 38 ---- 4 files changed, 210 insertions(+), 207 deletions(-) create mode 100644 src/main/java/com/thealgorithms/backtracking/SudokuSolver.java delete mode 100644 src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java create mode 100644 src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java delete mode 100644 src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java diff --git a/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java b/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java new file mode 100644 index 000000000000..543fe2d02b50 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java @@ -0,0 +1,157 @@ +package com.thealgorithms.backtracking; + +/** + * Sudoku Solver using Backtracking Algorithm + * Solves a 9x9 Sudoku puzzle by filling empty cells with valid digits (1-9) + * + * @author Navadeep0007 + */ +public final class SudokuSolver { + + private static final int GRID_SIZE = 9; + private static final int SUBGRID_SIZE = 3; + private static final int EMPTY_CELL = 0; + + private SudokuSolver() { + // Utility class, prevent instantiation + } + + /** + * Solves the Sudoku puzzle using backtracking + * + * @param board 9x9 Sudoku board with 0 representing empty cells + * @return true if puzzle is solved, false otherwise + */ + public static boolean solveSudoku(int[][] board) { + if (board == null || board.length != GRID_SIZE) { + return false; + } + + for (int row = 0; row < GRID_SIZE; row++) { + if (board[row].length != GRID_SIZE) { + return false; + } + } + + return solve(board); + } + + /** + * Recursive helper method to solve the Sudoku puzzle + * + * @param board the Sudoku board + * @return true if solution is found, false otherwise + */ + private static boolean solve(int[][] board) { + for (int row = 0; row < GRID_SIZE; row++) { + for (int col = 0; col < GRID_SIZE; col++) { + if (board[row][col] == EMPTY_CELL) { + for (int number = 1; number <= GRID_SIZE; number++) { + if (isValidPlacement(board, row, col, number)) { + board[row][col] = number; + + if (solve(board)) { + return true; + } + + // Backtrack + board[row][col] = EMPTY_CELL; + } + } + return false; + } + } + } + return true; + } + + /** + * Checks if placing a number at given position is valid + * + * @param board the Sudoku board + * @param row row index + * @param col column index + * @param number number to place (1-9) + * @return true if placement is valid, false otherwise + */ + private static boolean isValidPlacement(int[][] board, int row, int col, int number) { + return !isNumberInRow(board, row, number) && !isNumberInColumn(board, col, number) && !isNumberInSubgrid(board, row, col, number); + } + + /** + * Checks if number exists in the given row + * + * @param board the Sudoku board + * @param row row index + * @param number number to check + * @return true if number exists in row, false otherwise + */ + private static boolean isNumberInRow(int[][] board, int row, int number) { + for (int col = 0; col < GRID_SIZE; col++) { + if (board[row][col] == number) { + return true; + } + } + return false; + } + + /** + * Checks if number exists in the given column + * + * @param board the Sudoku board + * @param col column index + * @param number number to check + * @return true if number exists in column, false otherwise + */ + private static boolean isNumberInColumn(int[][] board, int col, int number) { + for (int row = 0; row < GRID_SIZE; row++) { + if (board[row][col] == number) { + return true; + } + } + return false; + } + + /** + * Checks if number exists in the 3x3 subgrid + * + * @param board the Sudoku board + * @param row row index + * @param col column index + * @param number number to check + * @return true if number exists in subgrid, false otherwise + */ + private static boolean isNumberInSubgrid(int[][] board, int row, int col, int number) { + int subgridRowStart = row - row % SUBGRID_SIZE; + int subgridColStart = col - col % SUBGRID_SIZE; + + for (int i = subgridRowStart; i < subgridRowStart + SUBGRID_SIZE; i++) { + for (int j = subgridColStart; j < subgridColStart + SUBGRID_SIZE; j++) { + if (board[i][j] == number) { + return true; + } + } + } + return false; + } + + /** + * Prints the Sudoku board + * + * @param board the Sudoku board + */ + public static void printBoard(int[][] board) { + for (int row = 0; row < GRID_SIZE; row++) { + if (row % SUBGRID_SIZE == 0 && row != 0) { + System.out.println("-----------"); + } + for (int col = 0; col < GRID_SIZE; col++) { + if (col % SUBGRID_SIZE == 0 && col != 0) { + System.out.print("|"); + } + System.out.print(board[row][col]); + } + System.out.println(); + } + } +} diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java b/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java deleted file mode 100644 index fce665c4de00..000000000000 --- a/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.thealgorithms.puzzlesandgames; - -/** - * A class that provides methods to solve Sudoku puzzles of any n x n size - * using a backtracking approach, where n must be a perfect square. - * The algorithm checks for safe number placements in rows, columns, - * and subgrids (which are sqrt(n) x sqrt(n) in size) and recursively solves the puzzle. - * Though commonly used for 9x9 grids, it is adaptable to other valid Sudoku dimensions. - */ -final class Sudoku { - - private Sudoku() { - } - - /** - * Checks if placing a number in a specific position on the Sudoku board is safe. - * The number is considered safe if it does not violate any of the Sudoku rules: - * - It should not be present in the same row. - * - It should not be present in the same column. - * - It should not be present in the corresponding 3x3 subgrid. - * - It should not be present in the corresponding subgrid, which is sqrt(n) x sqrt(n) in size (e.g., for a 9x9 grid, the subgrid will be 3x3). - * - * @param board The current state of the Sudoku board. - * @param row The row index where the number is to be placed. - * @param col The column index where the number is to be placed. - * @param num The number to be placed on the board. - * @return True if the placement is safe, otherwise false. - */ - public static boolean isSafe(int[][] board, int row, int col, int num) { - // Check the row for duplicates - for (int d = 0; d < board.length; d++) { - if (board[row][d] == num) { - return false; - } - } - - // Check the column for duplicates - for (int r = 0; r < board.length; r++) { - if (board[r][col] == num) { - return false; - } - } - - // Check the corresponding 3x3 subgrid for duplicates - int sqrt = (int) Math.sqrt(board.length); - int boxRowStart = row - row % sqrt; - int boxColStart = col - col % sqrt; - - for (int r = boxRowStart; r < boxRowStart + sqrt; r++) { - for (int d = boxColStart; d < boxColStart + sqrt; d++) { - if (board[r][d] == num) { - return false; - } - } - } - - return true; - } - - /** - * Solves the Sudoku puzzle using backtracking. - * The algorithm finds an empty cell and tries placing numbers - * from 1 to n, where n is the size of the board - * (for example, from 1 to 9 in a standard 9x9 Sudoku). - * The algorithm finds an empty cell and tries placing numbers from 1 to 9. - * The standard version of Sudoku uses numbers from 1 to 9, so the algorithm can be - * easily modified for other variations of the game. - * If a number placement is valid (checked via `isSafe`), the number is - * placed and the function recursively attempts to solve the rest of the puzzle. - * If no solution is possible, the number is removed (backtracked), - * and the process is repeated. - * - * @param board The current state of the Sudoku board. - * @param n The size of the Sudoku board (typically 9 for a standard puzzle). - * @return True if the Sudoku puzzle is solvable, false otherwise. - */ - public static boolean solveSudoku(int[][] board, int n) { - int row = -1; - int col = -1; - boolean isEmpty = true; - - // Find the next empty cell - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - if (board[i][j] == 0) { - row = i; - col = j; - isEmpty = false; - break; - } - } - if (!isEmpty) { - break; - } - } - - // No empty space left - if (isEmpty) { - return true; - } - - // Try placing numbers 1 to n in the empty cell (n should be a perfect square) - // Eg: n=9 for a standard 9x9 Sudoku puzzle, n=16 for a 16x16 puzzle, etc. - for (int num = 1; num <= n; num++) { - if (isSafe(board, row, col, num)) { - board[row][col] = num; - if (solveSudoku(board, n)) { - return true; - } else { - // replace it - board[row][col] = 0; - } - } - } - return false; - } - - /** - * Prints the current state of the Sudoku board in a readable format. - * Each row is printed on a new line, with numbers separated by spaces. - * - * @param board The current state of the Sudoku board. - * @param n The size of the Sudoku board (typically 9 for a standard puzzle). - */ - public static void print(int[][] board, int n) { - // Print the board in a nxn grid format - // if n=9, print the board in a 9x9 grid format - // if n=16, print the board in a 16x16 grid format - for (int r = 0; r < n; r++) { - for (int d = 0; d < n; d++) { - System.out.print(board[r][d]); - System.out.print(" "); - } - System.out.print("\n"); - - if ((r + 1) % (int) Math.sqrt(n) == 0) { - System.out.print(""); - } - } - } - - /** - * The driver method to demonstrate solving a Sudoku puzzle. - * A sample 9x9 Sudoku puzzle is provided, and the program attempts to solve it - * using the `solveSudoku` method. If a solution is found, it is printed to the console. - * - * @param args Command-line arguments (not used in this program). - */ - public static void main(String[] args) { - int[][] board = new int[][] { - {3, 0, 6, 5, 0, 8, 4, 0, 0}, - {5, 2, 0, 0, 0, 0, 0, 0, 0}, - {0, 8, 7, 0, 0, 0, 0, 3, 1}, - {0, 0, 3, 0, 1, 0, 0, 8, 0}, - {9, 0, 0, 8, 6, 3, 0, 0, 5}, - {0, 5, 0, 0, 9, 0, 6, 0, 0}, - {1, 3, 0, 0, 0, 0, 2, 5, 0}, - {0, 0, 0, 0, 0, 0, 0, 7, 4}, - {0, 0, 5, 2, 0, 6, 3, 0, 0}, - }; - int n = board.length; - - if (solveSudoku(board, n)) { - print(board, n); - } else { - System.out.println("No solution"); - } - } -} diff --git a/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java b/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java new file mode 100644 index 000000000000..75d3eae08629 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class SudokuSolverTest { + + @Test + void testSolveSudokuEasyPuzzle() { + int[][] board = {{5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9, 5, 0, 0, 0}, {0, 9, 8, 0, 0, 0, 0, 6, 0}, {8, 0, 0, 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0, 3, 0, 0, 1}, {7, 0, 0, 0, 2, 0, 0, 0, 6}, {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1, 9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9}}; + + assertTrue(SudokuSolver.solveSudoku(board)); + + int[][] expected = {{5, 3, 4, 6, 7, 8, 9, 1, 2}, {6, 7, 2, 1, 9, 5, 3, 4, 8}, {1, 9, 8, 3, 4, 2, 5, 6, 7}, {8, 5, 9, 7, 6, 1, 4, 2, 3}, {4, 2, 6, 8, 5, 3, 7, 9, 1}, {7, 1, 3, 9, 2, 4, 8, 5, 6}, {9, 6, 1, 5, 3, 7, 2, 8, 4}, {2, 8, 7, 4, 1, 9, 6, 3, 5}, {3, 4, 5, 2, 8, 6, 1, 7, 9}}; + + assertArrayEquals(expected, board); + } + + @Test + void testSolveSudokuHardPuzzle() { + int[][] board = {{0, 0, 0, 0, 0, 0, 6, 8, 0}, {0, 0, 0, 0, 7, 3, 0, 0, 9}, {3, 0, 9, 0, 0, 0, 0, 4, 5}, {4, 9, 0, 0, 0, 0, 0, 0, 0}, {8, 0, 3, 0, 5, 0, 9, 0, 2}, {0, 0, 0, 0, 0, 0, 0, 3, 6}, {9, 6, 0, 0, 0, 0, 3, 0, 8}, {7, 0, 0, 6, 8, 0, 0, 0, 0}, {0, 2, 8, 0, 0, 0, 0, 0, 0}}; + + assertTrue(SudokuSolver.solveSudoku(board)); + } + + @Test + void testSolveSudokuAlreadySolved() { + int[][] board = {{5, 3, 4, 6, 7, 8, 9, 1, 2}, {6, 7, 2, 1, 9, 5, 3, 4, 8}, {1, 9, 8, 3, 4, 2, 5, 6, 7}, {8, 5, 9, 7, 6, 1, 4, 2, 3}, {4, 2, 6, 8, 5, 3, 7, 9, 1}, {7, 1, 3, 9, 2, 4, 8, 5, 6}, {9, 6, 1, 5, 3, 7, 2, 8, 4}, {2, 8, 7, 4, 1, 9, 6, 3, 5}, {3, 4, 5, 2, 8, 6, 1, 7, 9}}; + + assertTrue(SudokuSolver.solveSudoku(board)); + } + + @Test + void testSolveSudokuInvalidSize() { + int[][] board = {{1, 2, 3}, {4, 5, 6}}; + assertFalse(SudokuSolver.solveSudoku(board)); + } + + @Test + void testSolveSudokuNullBoard() { + assertFalse(SudokuSolver.solveSudoku(null)); + } + + @Test + void testSolveSudokuEmptyBoard() { + int[][] board = {{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}}; + + assertTrue(SudokuSolver.solveSudoku(board)); + } +} diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java deleted file mode 100644 index 7fb96dcf805f..000000000000 --- a/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.thealgorithms.puzzlesandgames; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -public class SudokuTest { - - @Test - void testIsSafe2() { - int[][] board = {{3, 0, 6, 5, 0, 8, 4, 0, 0}, {5, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 8, 7, 0, 0, 0, 0, 3, 1}, {0, 0, 3, 0, 1, 0, 0, 8, 0}, {9, 0, 0, 8, 6, 3, 0, 0, 5}, {0, 5, 0, 0, 9, 0, 6, 0, 0}, {1, 3, 0, 0, 0, 0, 2, 5, 0}, {0, 0, 0, 0, 0, 0, 0, 7, 4}, {0, 0, 5, 2, 0, 6, 3, 0, 0}}; - - assertFalse(Sudoku.isSafe(board, 0, 1, 3)); - assertTrue(Sudoku.isSafe(board, 1, 2, 1)); - assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.isSafe(board, 10, 10, 5); }); - assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.isSafe(board, -1, 0, 5); }); - } - - @Test - void testSolveSudoku() { - int[][] board = {{3, 0, 6, 5, 0, 8, 4, 0, 0}, {5, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 8, 7, 0, 0, 0, 0, 3, 1}, {0, 0, 3, 0, 1, 0, 0, 8, 0}, {9, 0, 0, 8, 6, 3, 0, 0, 5}, {0, 5, 0, 0, 9, 0, 6, 0, 0}, {1, 3, 0, 0, 0, 0, 2, 5, 0}, {0, 0, 0, 0, 0, 0, 0, 7, 4}, {0, 0, 5, 2, 0, 6, 3, 0, 0}}; - - assertTrue(Sudoku.solveSudoku(board, board.length)); - assertEquals(1, board[0][1]); - assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.solveSudoku(board, 10); }); - assertTrue(Sudoku.solveSudoku(board, -1)); - } - - @Test - void testUnsolvableSudoku() { - int[][] unsolvableBoard = {{5, 1, 6, 8, 4, 9, 7, 3, 2}, {3, 0, 7, 6, 0, 5, 0, 0, 0}, {8, 0, 9, 7, 0, 0, 0, 6, 5}, {1, 3, 5, 0, 6, 0, 9, 0, 7}, {4, 7, 2, 5, 9, 1, 0, 0, 6}, {9, 6, 8, 3, 7, 0, 0, 5, 0}, {2, 5, 3, 1, 8, 6, 0, 7, 4}, {6, 8, 4, 2, 5, 7, 3, 9, 0}, {7, 9, 1, 4, 3, 0, 5, 0, 0}}; - - assertFalse(Sudoku.solveSudoku(unsolvableBoard, unsolvableBoard.length)); - } -} From 1c97ad8015855780e7d86248dd2938ef38caf2d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 22:16:07 +0100 Subject: [PATCH 011/122] chore(deps): bump org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0 (#7076) Bumps org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6126bbe4d6c5..774db2ec4403 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ org.apache.commons commons-lang3 - 3.19.0 + 3.20.0 org.apache.commons From 3519e396af269683a42796ef653b37abdcd7d791 Mon Sep 17 00:00:00 2001 From: shreeya-g10 Date: Wed, 19 Nov 2025 21:50:46 +0530 Subject: [PATCH 012/122] backtracking: add unique permutation algorithm with test cases (#7078) * Add unique permutation algorithm and test cases * Fix: add braces for checkstyle * Apply clang-format --- .../backtracking/UniquePermutation.java | 62 +++++++++++++++++++ .../backtracking/UniquePermutationTest.java | 31 ++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/main/java/com/thealgorithms/backtracking/UniquePermutation.java create mode 100644 src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java diff --git a/src/main/java/com/thealgorithms/backtracking/UniquePermutation.java b/src/main/java/com/thealgorithms/backtracking/UniquePermutation.java new file mode 100644 index 000000000000..4804e247ab03 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/UniquePermutation.java @@ -0,0 +1,62 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Generates all UNIQUE permutations of a string, even when duplicate characters exist. + * + * Example: + * Input: "AAB" + * Output: ["AAB", "ABA", "BAA"] + * + * Time Complexity: O(n! * n) + */ +public final class UniquePermutation { + + private UniquePermutation() { + // Prevent instantiation + throw new UnsupportedOperationException("Utility class"); + } + + public static List generateUniquePermutations(String input) { + List result = new ArrayList<>(); + if (input == null) { + return result; + } + + char[] chars = input.toCharArray(); + Arrays.sort(chars); // important: sort to detect duplicates + + backtrack(chars, new boolean[chars.length], new StringBuilder(), result); + return result; + } + + private static void backtrack(char[] chars, boolean[] used, StringBuilder current, List result) { + + if (current.length() == chars.length) { + result.add(current.toString()); + return; + } + + for (int i = 0; i < chars.length; i++) { + + // skip duplicates + if (i > 0 && chars[i] == chars[i - 1] && !used[i - 1]) { + continue; + } + + if (!used[i]) { + used[i] = true; + current.append(chars[i]); + + backtrack(chars, used, current, result); + + // undo changes + used[i] = false; + current.deleteCharAt(current.length() - 1); + } + } + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java b/src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java new file mode 100644 index 000000000000..c8e7cd0af0dd --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class UniquePermutationTest { + + @Test + void testUniquePermutationsAab() { + List expected = Arrays.asList("AAB", "ABA", "BAA"); + List result = UniquePermutation.generateUniquePermutations("AAB"); + assertEquals(expected, result); + } + + @Test + void testUniquePermutationsAbc() { + List expected = Arrays.asList("ABC", "ACB", "BAC", "BCA", "CAB", "CBA"); + List result = UniquePermutation.generateUniquePermutations("ABC"); + assertEquals(expected, result); + } + + @Test + void testEmptyString() { + List expected = Arrays.asList(""); + List result = UniquePermutation.generateUniquePermutations(""); + assertEquals(expected, result); + } +} From e37a7ab6ce87c7cf13274366dca536d34cfc5668 Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Wed, 19 Nov 2025 17:25:09 +0100 Subject: [PATCH 013/122] Update DIRECTORY.md (#7070) Co-authored-by: alxkm --- DIRECTORY.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index f15d65c18c3f..042efa72addc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -24,6 +24,8 @@ - πŸ“„ [Permutation](src/main/java/com/thealgorithms/backtracking/Permutation.java) - πŸ“„ [PowerSum](src/main/java/com/thealgorithms/backtracking/PowerSum.java) - πŸ“„ [SubsequenceFinder](src/main/java/com/thealgorithms/backtracking/SubsequenceFinder.java) + - πŸ“„ [SudokuSolver](src/main/java/com/thealgorithms/backtracking/SudokuSolver.java) + - πŸ“„ [UniquePermutation](src/main/java/com/thealgorithms/backtracking/UniquePermutation.java) - πŸ“„ [WordPatternMatcher](src/main/java/com/thealgorithms/backtracking/WordPatternMatcher.java) - πŸ“„ [WordSearch](src/main/java/com/thealgorithms/backtracking/WordSearch.java) - πŸ“ **bitmanipulation** @@ -133,6 +135,7 @@ - πŸ“„ [PhoneticAlphabetConverter](src/main/java/com/thealgorithms/conversions/PhoneticAlphabetConverter.java) - πŸ“„ [RgbHsvConversion](src/main/java/com/thealgorithms/conversions/RgbHsvConversion.java) - πŸ“„ [RomanToInteger](src/main/java/com/thealgorithms/conversions/RomanToInteger.java) + - πŸ“„ [TemperatureConverter](src/main/java/com/thealgorithms/conversions/TemperatureConverter.java) - πŸ“„ [TimeConverter](src/main/java/com/thealgorithms/conversions/TimeConverter.java) - πŸ“„ [TurkishToLatinConversion](src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java) - πŸ“„ [UnitConversions](src/main/java/com/thealgorithms/conversions/UnitConversions.java) @@ -208,6 +211,7 @@ - πŸ“„ [GenericHeap](src/main/java/com/thealgorithms/datastructures/heaps/GenericHeap.java) - πŸ“„ [Heap](src/main/java/com/thealgorithms/datastructures/heaps/Heap.java) - πŸ“„ [HeapElement](src/main/java/com/thealgorithms/datastructures/heaps/HeapElement.java) + - πŸ“„ [IndexedPriorityQueue](src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java) - πŸ“„ [KthElementFinder](src/main/java/com/thealgorithms/datastructures/heaps/KthElementFinder.java) - πŸ“„ [LeftistHeap](src/main/java/com/thealgorithms/datastructures/heaps/LeftistHeap.java) - πŸ“„ [MaxHeap](src/main/java/com/thealgorithms/datastructures/heaps/MaxHeap.java) @@ -507,6 +511,7 @@ - πŸ“„ [PiNilakantha](src/main/java/com/thealgorithms/maths/PiNilakantha.java) - πŸ“„ [PollardRho](src/main/java/com/thealgorithms/maths/PollardRho.java) - πŸ“„ [Pow](src/main/java/com/thealgorithms/maths/Pow.java) + - πŸ“„ [PowerOfFour](src/main/java/com/thealgorithms/maths/PowerOfFour.java) - πŸ“„ [PowerOfTwoOrNot](src/main/java/com/thealgorithms/maths/PowerOfTwoOrNot.java) - πŸ“„ [PowerUsingRecursion](src/main/java/com/thealgorithms/maths/PowerUsingRecursion.java) - πŸ“ **Prime** @@ -525,6 +530,7 @@ - πŸ“„ [SieveOfAtkin](src/main/java/com/thealgorithms/maths/SieveOfAtkin.java) - πŸ“„ [SieveOfEratosthenes](src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java) - πŸ“„ [SimpsonIntegration](src/main/java/com/thealgorithms/maths/SimpsonIntegration.java) + - πŸ“„ [SmithNumber](src/main/java/com/thealgorithms/maths/SmithNumber.java) - πŸ“„ [SolovayStrassenPrimalityTest](src/main/java/com/thealgorithms/maths/SolovayStrassenPrimalityTest.java) - πŸ“„ [SquareRootWithBabylonianMethod](src/main/java/com/thealgorithms/maths/SquareRootWithBabylonianMethod.java) - πŸ“„ [SquareRootWithNewtonRaphsonMethod](src/main/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonMethod.java) @@ -622,7 +628,6 @@ - πŸ“„ [ProjectileMotion](src/main/java/com/thealgorithms/physics/ProjectileMotion.java) - πŸ“„ [SimplePendulumRK4](src/main/java/com/thealgorithms/physics/SimplePendulumRK4.java) - πŸ“ **puzzlesandgames** - - πŸ“„ [Sudoku](src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java) - πŸ“„ [TowerOfHanoi](src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java) - πŸ“„ [WordBoggle](src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java) - πŸ“ **randomized** @@ -842,6 +847,8 @@ - πŸ“„ [PermutationTest](src/test/java/com/thealgorithms/backtracking/PermutationTest.java) - πŸ“„ [PowerSumTest](src/test/java/com/thealgorithms/backtracking/PowerSumTest.java) - πŸ“„ [SubsequenceFinderTest](src/test/java/com/thealgorithms/backtracking/SubsequenceFinderTest.java) + - πŸ“„ [SudokuSolverTest](src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java) + - πŸ“„ [UniquePermutationTest](src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java) - πŸ“„ [WordPatternMatcherTest](src/test/java/com/thealgorithms/backtracking/WordPatternMatcherTest.java) - πŸ“„ [WordSearchTest](src/test/java/com/thealgorithms/backtracking/WordSearchTest.java) - πŸ“ **bitmanipulation** @@ -944,6 +951,7 @@ - πŸ“„ [OctalToHexadecimalTest](src/test/java/com/thealgorithms/conversions/OctalToHexadecimalTest.java) - πŸ“„ [PhoneticAlphabetConverterTest](src/test/java/com/thealgorithms/conversions/PhoneticAlphabetConverterTest.java) - πŸ“„ [RomanToIntegerTest](src/test/java/com/thealgorithms/conversions/RomanToIntegerTest.java) + - πŸ“„ [TemperatureConverterTest](src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java) - πŸ“„ [TimeConverterTest](src/test/java/com/thealgorithms/conversions/TimeConverterTest.java) - πŸ“„ [TurkishToLatinConversionTest](src/test/java/com/thealgorithms/conversions/TurkishToLatinConversionTest.java) - πŸ“„ [UnitConversionsTest](src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java) @@ -1009,6 +1017,7 @@ - πŸ“„ [FibonacciHeapTest](src/test/java/com/thealgorithms/datastructures/heaps/FibonacciHeapTest.java) - πŸ“„ [GenericHeapTest](src/test/java/com/thealgorithms/datastructures/heaps/GenericHeapTest.java) - πŸ“„ [HeapElementTest](src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java) + - πŸ“„ [IndexedPriorityQueueTest](src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java) - πŸ“„ [KthElementFinderTest](src/test/java/com/thealgorithms/datastructures/heaps/KthElementFinderTest.java) - πŸ“„ [LeftistHeapTest](src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java) - πŸ“„ [MaxHeapTest](src/test/java/com/thealgorithms/datastructures/heaps/MaxHeapTest.java) @@ -1279,6 +1288,7 @@ - πŸ“„ [PiApproximationTest](src/test/java/com/thealgorithms/maths/PiApproximationTest.java) - πŸ“„ [PollardRhoTest](src/test/java/com/thealgorithms/maths/PollardRhoTest.java) - πŸ“„ [PowTest](src/test/java/com/thealgorithms/maths/PowTest.java) + - πŸ“„ [PowerOfFourTest](src/test/java/com/thealgorithms/maths/PowerOfFourTest.java) - πŸ“„ [PowerOfTwoOrNotTest](src/test/java/com/thealgorithms/maths/PowerOfTwoOrNotTest.java) - πŸ“„ [PowerUsingRecursionTest](src/test/java/com/thealgorithms/maths/PowerUsingRecursionTest.java) - πŸ“„ [PronicNumberTest](src/test/java/com/thealgorithms/maths/PronicNumberTest.java) @@ -1288,6 +1298,7 @@ - πŸ“„ [SecondMinMaxTest](src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java) - πŸ“„ [SieveOfAtkinTest](src/test/java/com/thealgorithms/maths/SieveOfAtkinTest.java) - πŸ“„ [SieveOfEratosthenesTest](src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java) + - πŸ“„ [SmithNumberTest](src/test/java/com/thealgorithms/maths/SmithNumberTest.java) - πŸ“„ [SolovayStrassenPrimalityTestTest](src/test/java/com/thealgorithms/maths/SolovayStrassenPrimalityTestTest.java) - πŸ“„ [SquareFreeIntegerTest](src/test/java/com/thealgorithms/maths/SquareFreeIntegerTest.java) - πŸ“„ [SquareRootWithNewtonRaphsonTestMethod](src/test/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonTestMethod.java) @@ -1377,7 +1388,6 @@ - πŸ“„ [ProjectileMotionTest](src/test/java/com/thealgorithms/physics/ProjectileMotionTest.java) - πŸ“„ [SimplePendulumRK4Test](src/test/java/com/thealgorithms/physics/SimplePendulumRK4Test.java) - πŸ“ **puzzlesandgames** - - πŸ“„ [SudokuTest](src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java) - πŸ“„ [TowerOfHanoiTest](src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java) - πŸ“„ [WordBoggleTest](src/test/java/com/thealgorithms/puzzlesandgames/WordBoggleTest.java) - πŸ“ **randomized** From fba6292ebc849dc8311ab24d8bcbf6e3dacbc105 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:31:32 +0100 Subject: [PATCH 014/122] chore: pin infer to more recent hash (#7079) * chore: pin infer to more recent hash * chore: suppress new infer warnings --- .github/workflows/infer.yml | 2 +- .inferconfig | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index 3df7c4b1fc9e..329fb0cd720a 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -44,7 +44,7 @@ jobs: cd .. git clone https://github.com/facebook/infer.git cd infer - git checkout 01aaa268f9d38723ba69c139e10f9e2a04b40b1c + git checkout 02c2c43b71e4c5110c0be841e66153942fda06c9 ./build-infer.sh java cp -r infer ../Java diff --git a/.inferconfig b/.inferconfig index 6af4f9e2e818..239172177b38 100644 --- a/.inferconfig +++ b/.inferconfig @@ -1,6 +1,8 @@ { "report-block-list-path-regex": [ "src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java", + "src/main/java/com/thealgorithms/compression/ArithmeticCoding.java", + "src/main/java/com/thealgorithms/datastructures/caches/FIFOCache.java", "src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java", "src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java", "src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java", @@ -8,15 +10,18 @@ "src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java", "src/main/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorder.java", "src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java", + "src/main/java/com/thealgorithms/dynamicprogramming/DamerauLevenshteinDistance.java", "src/main/java/com/thealgorithms/dynamicprogramming/Fibonacci.java", "src/main/java/com/thealgorithms/maths/SimpsonIntegration.java", "src/main/java/com/thealgorithms/others/Dijkstra.java", "src/main/java/com/thealgorithms/sorts/TopologicalSort.java", "src/main/java/com/thealgorithms/strings/AhoCorasick.java", + "src/test/java/com/thealgorithms/compression/ShannonFanoTest.java", "src/test/java/com/thealgorithms/datastructures/caches/LRUCacheTest.java", "src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java", "src/test/java/com/thealgorithms/datastructures/trees/KDTreeTest.java", "src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java", + "src/test/java/com/thealgorithms/others/HuffmanTest.java", "src/test/java/com/thealgorithms/searches/QuickSelectTest.java", "src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java", "src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java" From e6c576c50aac098f84a1e6a02d81c714750100ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 19:44:44 +0100 Subject: [PATCH 015/122] chore(deps): bump actions/checkout from 5 to 6 in /.github/workflows (#7084) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/clang-format-lint.yml | 2 +- .github/workflows/codeql.yml | 4 ++-- .github/workflows/infer.yml | 2 +- .github/workflows/project_structure.yml | 2 +- .github/workflows/update-directorymd.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39bde758d68e..c5f200c12836 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK uses: actions/setup-java@v5 with: diff --git a/.github/workflows/clang-format-lint.yml b/.github/workflows/clang-format-lint.yml index ca014e115282..dc0c9754ed1b 100644 --- a/.github/workflows/clang-format-lint.yml +++ b/.github/workflows/clang-format-lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: DoozyX/clang-format-lint-action@v0.20 with: source: './src' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3a4cfd829b6b..152d0d766fd2 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up JDK uses: actions/setup-java@v5 @@ -52,7 +52,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index 329fb0cd720a..c85b1d225f72 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -15,7 +15,7 @@ jobs: run_infer: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK uses: actions/setup-java@v5 diff --git a/.github/workflows/project_structure.yml b/.github/workflows/project_structure.yml index f9fb82a2781c..5aadc6353791 100644 --- a/.github/workflows/project_structure.yml +++ b/.github/workflows/project_structure.yml @@ -15,7 +15,7 @@ jobs: check_structure: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: '3.13' diff --git a/.github/workflows/update-directorymd.yml b/.github/workflows/update-directorymd.yml index 6692a884a867..f6c91abe0e74 100644 --- a/.github/workflows/update-directorymd.yml +++ b/.github/workflows/update-directorymd.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Run Directory Tree Generator uses: DenizAltunkapan/directory-tree-generator@v2 From fa8ea8e6143ee788eb752178b2252faa41e546cf Mon Sep 17 00:00:00 2001 From: KANAKALA SAI KIRAN <2400030639@kluniversity.in> Date: Sat, 22 Nov 2025 15:14:34 +0530 Subject: [PATCH 016/122] Feature/centroid decomposition (#7086) * feat: Add Centroid Decomposition for trees (#7054) - Implement CentroidDecomposition with O(N log N) construction - Add CentroidTree class with parent tracking and query methods - Include buildFromEdges helper for easy tree construction - Add comprehensive test suite with 20+ test cases - Cover edge cases, validation, and various tree structures Closes #7054 * feat: Add Centroid Decomposition for trees (#7054) - Implement CentroidDecomposition with O(N log N) construction - Add CentroidTree class with parent tracking and query methods - Include buildFromEdges helper for easy tree construction - Add comprehensive test suite with 20+ test cases - Cover edge cases, validation, and various tree structures Closes #7054 * fix: Remove trailing whitespace from CentroidDecompositionTest * fix: Remove trailing whitespace and add newlines at end of files * fix: Format code to comply with clang-format and checkstyle requirements * style: Fix lambda formatting in test assertions - Change single-line lambdas to multi-line format - Align with repository code style guidelines * style: Apply clang-format to match repository style guide - Format code according to .clang-format configuration - Use single-line lambdas as allowed by AllowShortLambdasOnASingleLine: All - Apply 4-space indentation - Ensure proper line endings --- .../trees/CentroidDecomposition.java | 217 ++++++++++++++++ .../trees/CentroidDecompositionTest.java | 236 ++++++++++++++++++ 2 files changed, 453 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java create mode 100644 src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java b/src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java new file mode 100644 index 000000000000..0b29dd6f5f5e --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java @@ -0,0 +1,217 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Centroid Decomposition is a divide-and-conquer technique for trees. + * It recursively partitions a tree by finding centroids - nodes whose removal + * creates balanced subtrees (each with at most N/2 nodes). + * + *

+ * Time Complexity: O(N log N) for construction + * Space Complexity: O(N) + * + *

+ * Applications: + * - Distance queries on trees + * - Path counting problems + * - Nearest neighbor searches + * + * @see Centroid Decomposition + * @see Centroid Decomposition Tutorial + * @author lens161 + */ +public final class CentroidDecomposition { + + private CentroidDecomposition() { + } + + /** + * Represents the centroid tree structure. + */ + public static final class CentroidTree { + private final int n; + private final List> adj; + private final int[] parent; + private final int[] subtreeSize; + private final boolean[] removed; + private int root; + + /** + * Constructs a centroid tree from an adjacency list. + * + * @param adj adjacency list representation of the tree (0-indexed) + * @throws IllegalArgumentException if tree is empty or null + */ + public CentroidTree(List> adj) { + if (adj == null || adj.isEmpty()) { + throw new IllegalArgumentException("Tree cannot be empty or null"); + } + + this.n = adj.size(); + this.adj = adj; + this.parent = new int[n]; + this.subtreeSize = new int[n]; + this.removed = new boolean[n]; + Arrays.fill(parent, -1); + + // Build centroid tree starting from node 0 + this.root = decompose(0, -1); + } + + /** + * Recursively builds the centroid tree. + * + * @param u current node + * @param p parent in centroid tree + * @return centroid of current component + */ + private int decompose(int u, int p) { + int size = getSubtreeSize(u, -1); + int centroid = findCentroid(u, -1, size); + + removed[centroid] = true; + parent[centroid] = p; + + // Recursively decompose each subtree + for (int v : adj.get(centroid)) { + if (!removed[v]) { + decompose(v, centroid); + } + } + + return centroid; + } + + /** + * Calculates subtree size from node u. + * + * @param u current node + * @param p parent node (-1 for root) + * @return size of subtree rooted at u + */ + private int getSubtreeSize(int u, int p) { + subtreeSize[u] = 1; + for (int v : adj.get(u)) { + if (v != p && !removed[v]) { + subtreeSize[u] += getSubtreeSize(v, u); + } + } + return subtreeSize[u]; + } + + /** + * Finds the centroid of a subtree. + * A centroid is a node whose removal creates components with size <= totalSize/2. + * + * @param u current node + * @param p parent node + * @param totalSize total size of current component + * @return centroid node + */ + private int findCentroid(int u, int p, int totalSize) { + for (int v : adj.get(u)) { + if (v != p && !removed[v] && subtreeSize[v] > totalSize / 2) { + return findCentroid(v, u, totalSize); + } + } + return u; + } + + /** + * Gets the parent of a node in the centroid tree. + * + * @param node the node + * @return parent node in centroid tree, or -1 if root + */ + public int getParent(int node) { + if (node < 0 || node >= n) { + throw new IllegalArgumentException("Invalid node: " + node); + } + return parent[node]; + } + + /** + * Gets the root of the centroid tree. + * + * @return root node + */ + public int getRoot() { + return root; + } + + /** + * Gets the number of nodes in the tree. + * + * @return number of nodes + */ + public int size() { + return n; + } + + /** + * Returns the centroid tree structure as a string. + * Format: node -> parent (or ROOT for root node) + * + * @return string representation + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Centroid Tree:\n"); + for (int i = 0; i < n; i++) { + sb.append("Node ").append(i).append(" -> "); + if (parent[i] == -1) { + sb.append("ROOT"); + } else { + sb.append("Parent ").append(parent[i]); + } + sb.append("\n"); + } + return sb.toString(); + } + } + + /** + * Creates a centroid tree from an edge list. + * + * @param n number of nodes (0-indexed: 0 to n-1) + * @param edges list of edges where each edge is [u, v] + * @return CentroidTree object + * @throws IllegalArgumentException if n <= 0 or edges is invalid + */ + public static CentroidTree buildFromEdges(int n, int[][] edges) { + if (n <= 0) { + throw new IllegalArgumentException("Number of nodes must be positive"); + } + if (edges == null) { + throw new IllegalArgumentException("Edges cannot be null"); + } + if (edges.length != n - 1) { + throw new IllegalArgumentException("Tree must have exactly n-1 edges"); + } + + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + if (edge.length != 2) { + throw new IllegalArgumentException("Each edge must have exactly 2 nodes"); + } + int u = edge[0]; + int v = edge[1]; + + if (u < 0 || u >= n || v < 0 || v >= n) { + throw new IllegalArgumentException("Invalid node in edge: [" + u + ", " + v + "]"); + } + + adj.get(u).add(v); + adj.get(v).add(u); + } + + return new CentroidTree(adj); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java new file mode 100644 index 000000000000..43d732e54f34 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java @@ -0,0 +1,236 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Test cases for CentroidDecomposition + * + * @author lens161 + */ +class CentroidDecompositionTest { + + @Test + void testSingleNode() { + // Tree with just one node + int[][] edges = {}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(1, edges); + + assertEquals(1, tree.size()); + assertEquals(0, tree.getRoot()); + assertEquals(-1, tree.getParent(0)); + } + + @Test + void testTwoNodes() { + // Simple tree: 0 - 1 + int[][] edges = {{0, 1}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(2, edges); + + assertEquals(2, tree.size()); + int root = tree.getRoot(); + assertTrue(root == 0 || root == 1, "Root should be either node 0 or 1"); + + // One node should be root, other should have the root as parent + int nonRoot = (root == 0) ? 1 : 0; + assertEquals(-1, tree.getParent(root)); + assertEquals(root, tree.getParent(nonRoot)); + } + + @Test + void testLinearTree() { + // Linear tree: 0 - 1 - 2 - 3 - 4 + int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 4}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(5, edges); + + assertEquals(5, tree.size()); + // For a linear tree of 5 nodes, the centroid should be the middle node (node 2) + assertEquals(2, tree.getRoot()); + assertEquals(-1, tree.getParent(2)); + } + + @Test + void testBalancedBinaryTree() { + // Balanced binary tree: + // 0 + // / \ + // 1 2 + // / \ + // 3 4 + int[][] edges = {{0, 1}, {0, 2}, {1, 3}, {1, 4}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(5, edges); + + assertEquals(5, tree.size()); + // Root should be 0 or 1 (both are valid centroids) + int root = tree.getRoot(); + assertTrue(root == 0 || root == 1); + assertEquals(-1, tree.getParent(root)); + + // All nodes should have a parent in centroid tree except root + for (int i = 0; i < 5; i++) { + if (i != root) { + assertTrue(tree.getParent(i) >= 0 && tree.getParent(i) < 5); + } + } + } + + @Test + void testStarTree() { + // Star tree: center node 0 connected to 1, 2, 3, 4 + int[][] edges = {{0, 1}, {0, 2}, {0, 3}, {0, 4}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(5, edges); + + assertEquals(5, tree.size()); + // Center node (0) should be the root + assertEquals(0, tree.getRoot()); + + // All other nodes should have 0 as parent + for (int i = 1; i < 5; i++) { + assertEquals(0, tree.getParent(i)); + } + } + + @Test + void testCompleteTree() { + // Complete binary tree of 7 nodes: + // 0 + // / \ + // 1 2 + // / \ / \ + // 3 4 5 6 + int[][] edges = {{0, 1}, {0, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(7, edges); + + assertEquals(7, tree.size()); + assertEquals(0, tree.getRoot()); // Root should be the center + + // Verify all nodes are reachable in centroid tree + boolean[] visited = new boolean[7]; + visited[0] = true; + for (int i = 1; i < 7; i++) { + int parent = tree.getParent(i); + assertTrue(parent >= 0 && parent < 7); + assertTrue(visited[parent], "Parent should be processed before child"); + visited[i] = true; + } + } + + @Test + void testLargerTree() { + // Tree with 10 nodes + int[][] edges = {{0, 1}, {0, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}, {3, 7}, {4, 8}, {5, 9}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(10, edges); + + assertEquals(10, tree.size()); + int root = tree.getRoot(); + assertTrue(root >= 0 && root < 10); + assertEquals(-1, tree.getParent(root)); + + // Verify centroid tree structure is valid + for (int i = 0; i < 10; i++) { + if (i != root) { + assertTrue(tree.getParent(i) >= -1 && tree.getParent(i) < 10); + } + } + } + + @Test + void testPathGraph() { + // Path graph with 8 nodes: 0-1-2-3-4-5-6-7 + int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(8, edges); + + assertEquals(8, tree.size()); + // For path of 8 nodes, centroid should be around middle + int root = tree.getRoot(); + assertTrue(root >= 2 && root <= 5, "Root should be near the middle of path"); + } + + @Test + void testInvalidEmptyTree() { + assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(0, new int[][] {}); }); + } + + @Test + void testInvalidNegativeNodes() { + assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(-1, new int[][] {}); }); + } + + @Test + void testInvalidNullEdges() { + assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(5, null); }); + } + + @Test + void testInvalidEdgeCount() { + // Tree with n nodes must have n-1 edges + int[][] edges = {{0, 1}, {1, 2}}; // 2 edges for 5 nodes (should be 4) + assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(5, edges); }); + } + + @Test + void testInvalidEdgeFormat() { + int[][] edges = {{0, 1, 2}}; // Edge with 3 elements instead of 2 + assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(3, edges); }); + } + + @Test + void testInvalidNodeInEdge() { + int[][] edges = {{0, 5}}; // Node 5 doesn't exist in tree of size 3 + assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(3, edges); }); + } + + @Test + void testInvalidNodeQuery() { + int[][] edges = {{0, 1}, {1, 2}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(3, edges); + + assertThrows(IllegalArgumentException.class, () -> { tree.getParent(-1); }); + + assertThrows(IllegalArgumentException.class, () -> { tree.getParent(5); }); + } + + @Test + void testToString() { + int[][] edges = {{0, 1}, {1, 2}}; + CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(3, edges); + + String result = tree.toString(); + assertNotNull(result); + assertTrue(result.contains("Centroid Tree")); + assertTrue(result.contains("Node")); + assertTrue(result.contains("ROOT")); + } + + @Test + void testAdjacencyListConstructor() { + List> adj = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + adj.add(new ArrayList<>()); + } + adj.get(0).add(1); + adj.get(1).add(0); + adj.get(1).add(2); + adj.get(2).add(1); + + CentroidDecomposition.CentroidTree tree = new CentroidDecomposition.CentroidTree(adj); + assertEquals(3, tree.size()); + assertEquals(1, tree.getRoot()); + } + + @Test + void testNullAdjacencyList() { + assertThrows(IllegalArgumentException.class, () -> { new CentroidDecomposition.CentroidTree(null); }); + } + + @Test + void testEmptyAdjacencyList() { + assertThrows(IllegalArgumentException.class, () -> { new CentroidDecomposition.CentroidTree(new ArrayList<>()); }); + } +} From f693c44b539508cd262374a6d8e9d7329301d6cc Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Sat, 22 Nov 2025 18:26:16 +0100 Subject: [PATCH 017/122] fix: prevent duplicate auth header in GitHub Actions workflow (#7091) --- .github/workflows/update-directorymd.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-directorymd.yml b/.github/workflows/update-directorymd.yml index f6c91abe0e74..101d82427e38 100644 --- a/.github/workflows/update-directorymd.yml +++ b/.github/workflows/update-directorymd.yml @@ -1,4 +1,4 @@ -name: Generate Directory Markdown +ο»Ώname: Generate Directory Markdown on: push: @@ -15,6 +15,8 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@v6 + with: + persist-credentials: false - name: Run Directory Tree Generator uses: DenizAltunkapan/directory-tree-generator@v2 From 1c6026ecc640b26d21bf5386872ef99de3d42c60 Mon Sep 17 00:00:00 2001 From: Eswar Venkata Ram Charan Seeram Date: Tue, 25 Nov 2025 01:46:16 +0530 Subject: [PATCH 018/122] fix: correct error message in surfaceAreaCylinder method (#7106) --- src/main/java/com/thealgorithms/maths/Area.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/maths/Area.java b/src/main/java/com/thealgorithms/maths/Area.java index a34ad6b01ab5..1eba6666dde3 100644 --- a/src/main/java/com/thealgorithms/maths/Area.java +++ b/src/main/java/com/thealgorithms/maths/Area.java @@ -96,7 +96,7 @@ public static double surfaceAreaCylinder(final double radius, final double heigh throw new IllegalArgumentException(POSITIVE_RADIUS); } if (height <= 0) { - throw new IllegalArgumentException(POSITIVE_RADIUS); + throw new IllegalArgumentException(POSITIVE_HEIGHT); } return 2 * (Math.PI * radius * radius + Math.PI * radius * height); } From 2c4bf3c7c88b2f3b7bfa6baeebb7b569e0530a5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:36:37 +0100 Subject: [PATCH 019/122] chore(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.9.8.1 to 4.9.8.2 (#7109) chore(deps): bump com.github.spotbugs:spotbugs-maven-plugin Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.8.1 to 4.9.8.2. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.8.1...spotbugs-maven-plugin-4.9.8.2) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 774db2ec4403..eb2aafc0fcc0 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.8.1 + 4.9.8.2 spotbugs-exclude.xml true From a14e1e37669f0d8841e4a5bae07726da21e64736 Mon Sep 17 00:00:00 2001 From: prasanth-30011 Date: Wed, 26 Nov 2025 16:51:07 +0530 Subject: [PATCH 020/122] docs: improve javadoc for Combination class (#7126) Updated class documentation to reflect that the class finds combinations instead of permutations. --- src/main/java/com/thealgorithms/backtracking/Combination.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/backtracking/Combination.java b/src/main/java/com/thealgorithms/backtracking/Combination.java index ecaf7428f986..377d2c862d54 100644 --- a/src/main/java/com/thealgorithms/backtracking/Combination.java +++ b/src/main/java/com/thealgorithms/backtracking/Combination.java @@ -7,8 +7,7 @@ import java.util.TreeSet; /** - * Finds all permutations of given array - * @author Alan Piao (git-Alan Piao) + * Finds all combinations of a given array using backtracking algorithm * @author Alan Piao (git-Alan Piao) */ public final class Combination { private Combination() { From e841d73837c17d462e8bdfbb4d601e6744082981 Mon Sep 17 00:00:00 2001 From: Jayana Anjani Pavan Vardhan Naidu Date: Thu, 27 Nov 2025 16:55:52 +0530 Subject: [PATCH 021/122] Update Javadoc for perimeterIrregularPolygon method (#7141) Fixed incorrect @return description in perimeterIrregularPolygon method javadoc. Changed "trapezoid" to "irregular polygon" to accurately reflect what the method calculates. --- src/main/java/com/thealgorithms/maths/Perimeter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/maths/Perimeter.java b/src/main/java/com/thealgorithms/maths/Perimeter.java index f8aa1876d388..670851eb346b 100644 --- a/src/main/java/com/thealgorithms/maths/Perimeter.java +++ b/src/main/java/com/thealgorithms/maths/Perimeter.java @@ -27,7 +27,7 @@ public static float perimeterRegularPolygon(int n, float side) { * @param side2 for length of side 2 * @param side3 for length of side 3 * @param sides for length of remaining sides - * @return Perimeter of given trapezoid. + * @return Perimeter of given irregular polygon. */ public static float perimeterIrregularPolygon(float side1, float side2, float side3, float... sides) { float perimeter = side1 + side2 + side3; From d1ea3069203b5d2a132f2cf553424fbf4c2e3353 Mon Sep 17 00:00:00 2001 From: Gokul45-45 <2400032465@kluniversity.in> Date: Thu, 27 Nov 2025 16:59:48 +0530 Subject: [PATCH 022/122] feat: add Valid Parentheses algorithm using Stack (#7117) * feat: add Valid Parentheses algorithm using Stack * fix: add missing ValidParentheses.java implementation * fix: remove trailing spaces and add newline at EOF * fix: remove misplaced ValidParentheses.java from root --- .../stacks/ValidParentheses.java | 74 +++++++++++++++++++ .../stacks/ValidParenthesesTest.java | 32 ++++++++ 2 files changed, 106 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..2cc616a38826 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java @@ -0,0 +1,74 @@ +package com.thealgorithms.stacks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * Valid Parentheses Problem + * + * Given a string containing just the characters '(', ')', '{', '}', '[' and ']', + * determine if the input string is valid. + * + * An input string is valid if: + * 1. Open brackets must be closed by the same type of brackets. + * 2. Open brackets must be closed in the correct order. + * 3. Every close bracket has a corresponding open bracket of the same type. + * + * Examples: + * Input: "()" + * Output: true + * + * Input: "()[]{}" + * Output: true + * + * Input: "(]" + * Output: false + * + * Input: "([)]" + * Output: false + * + * @author Gokul45-45 + */ +public final class ValidParentheses { + private ValidParentheses() { + } + + /** + * Checks if the given string has valid parentheses + * + * @param s the input string containing parentheses + * @return true if valid, false otherwise + */ + public static boolean isValid(String s) { + if (s == null || s.length() % 2 != 0) { + return false; + } + + Map parenthesesMap = new HashMap<>(); + parenthesesMap.put('(', ')'); + parenthesesMap.put('{', '}'); + parenthesesMap.put('[', ']'); + + Stack stack = new Stack<>(); + + for (char c : s.toCharArray()) { + if (parenthesesMap.containsKey(c)) { + // Opening bracket - push to stack + stack.push(c); + } else { + // Closing bracket - check if it matches + if (stack.isEmpty()) { + return false; + } + char openBracket = stack.pop(); + if (parenthesesMap.get(openBracket) != c) { + return false; + } + } + } + + // Stack should be empty if all brackets are matched + return stack.isEmpty(); + } +} 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..39014780caa9 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ValidParenthesesTest { + + @Test + void testValidParentheses() { + assertTrue(ValidParentheses.isValid("()")); + assertTrue(ValidParentheses.isValid("()[]{}")); + assertTrue(ValidParentheses.isValid("{[]}")); + assertTrue(ValidParentheses.isValid("")); + } + + @Test + void testInvalidParentheses() { + assertFalse(ValidParentheses.isValid("(]")); + assertFalse(ValidParentheses.isValid("([)]")); + assertFalse(ValidParentheses.isValid("{{{")); + assertFalse(ValidParentheses.isValid("}")); + assertFalse(ValidParentheses.isValid("(")); + } + + @Test + void testNullAndOddLength() { + assertFalse(ValidParentheses.isValid(null)); + assertFalse(ValidParentheses.isValid("(()")); + } +} From f352f8111ba3fdcc87674263c0907f9452f5b0b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 09:26:07 +0100 Subject: [PATCH 023/122] chore(deps): bump com.mebigfatguy.fb-contrib:fb-contrib from 7.7.0 to 7.7.1 (#7144) chore(deps): bump com.mebigfatguy.fb-contrib:fb-contrib Bumps [com.mebigfatguy.fb-contrib:fb-contrib](https://github.com/mebigfatguy/fb-contrib) from 7.7.0 to 7.7.1. - [Commits](https://github.com/mebigfatguy/fb-contrib/commits) --- updated-dependencies: - dependency-name: com.mebigfatguy.fb-contrib:fb-contrib dependency-version: 7.7.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb2aafc0fcc0..6b918cdad5bb 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.mebigfatguy.fb-contrib fb-contrib - 7.7.0 + 7.7.1 com.h3xstream.findsecbugs From 4c5525a2e0a9b18b879d774398d1a0f5ca29c509 Mon Sep 17 00:00:00 2001 From: Sameer Prajapati Date: Fri, 28 Nov 2025 14:17:53 +0530 Subject: [PATCH 024/122] =?UTF-8?q?feat:=20Added=20Gomory=E2=80=93Hu=20Tre?= =?UTF-8?q?e=20(all-pairs=20min-cuts=20via=20n1=20max-flows)=20(#6818)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Gomory–Hu Tree (all-pairs min-cuts via n1 max-flows) * Stabilize Monte Carlo integration with antithetic variates to reduce variance * Fix Checkstyle in GomoryHuTreeTest: remove inner assignments, add braces, split declarations * SpotBugs: use RandomGenerator interface in test helper --------- Co-authored-by: Deniz Altunkapan --- DIRECTORY.md | 1 + .../com/thealgorithms/graph/GomoryHuTree.java | 144 ++++++++++++++++++ .../randomized/MonteCarloIntegration.java | 16 +- .../thealgorithms/graph/GomoryHuTreeTest.java | 132 ++++++++++++++++ 4 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/thealgorithms/graph/GomoryHuTree.java create mode 100644 src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java diff --git a/DIRECTORY.md b/DIRECTORY.md index 042efa72addc..b8db96ffa993 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -385,6 +385,7 @@ - πŸ“„ [Dinic](src/main/java/com/thealgorithms/graph/Dinic.java) - πŸ“„ [Edmonds](src/main/java/com/thealgorithms/graph/Edmonds.java) - πŸ“„ [EdmondsKarp](src/main/java/com/thealgorithms/graph/EdmondsKarp.java) + - πŸ“„ [GomoryHuTree](src/main/java/com/thealgorithms/graph/GomoryHuTree.java) - πŸ“„ [HierholzerAlgorithm](src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java) - πŸ“„ [HierholzerEulerianPath](src/main/java/com/thealgorithms/graph/HierholzerEulerianPath.java) - πŸ“„ [HopcroftKarp](src/main/java/com/thealgorithms/graph/HopcroftKarp.java) diff --git a/src/main/java/com/thealgorithms/graph/GomoryHuTree.java b/src/main/java/com/thealgorithms/graph/GomoryHuTree.java new file mode 100644 index 000000000000..f8c110f25571 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/GomoryHuTree.java @@ -0,0 +1,144 @@ +package com.thealgorithms.graph; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Queue; + +/** + * Gomory–Hu tree construction for undirected graphs via nβˆ’1 max-flow computations. + * + *

API: {@code buildTree(int[][])} returns {@code {parent, weight}} arrays for the tree. + * + * @see Wikipedia: Gomory–Hu tree + */ + +public final class GomoryHuTree { + private GomoryHuTree() { + } + + public static int[][] buildTree(int[][] cap) { + validateCapacityMatrix(cap); + final int n = cap.length; + if (n == 1) { + return new int[][] {new int[] {-1}, new int[] {0}}; + } + + int[] parent = new int[n]; + int[] weight = new int[n]; + Arrays.fill(parent, 0); + parent[0] = -1; + weight[0] = 0; + + for (int s = 1; s < n; s++) { + int t = parent[s]; + MaxFlowResult res = edmondsKarpWithMinCut(cap, s, t); + int f = res.flow; + weight[s] = f; + + for (int v = 0; v < n; v++) { + if (v != s && parent[v] == t && res.reachable[v]) { + parent[v] = s; + } + } + + if (t != 0 && res.reachable[parent[t]]) { + parent[s] = parent[t]; + parent[t] = s; + weight[s] = weight[t]; + weight[t] = f; + } + } + return new int[][] {parent, weight}; + } + + private static void validateCapacityMatrix(int[][] cap) { + if (cap == null || cap.length == 0) { + throw new IllegalArgumentException("Capacity matrix must not be null or empty"); + } + final int n = cap.length; + for (int i = 0; i < n; i++) { + if (cap[i] == null || cap[i].length != n) { + throw new IllegalArgumentException("Capacity matrix must be square"); + } + for (int j = 0; j < n; j++) { + if (cap[i][j] < 0) { + throw new IllegalArgumentException("Capacities must be non-negative"); + } + } + } + } + + private static final class MaxFlowResult { + final int flow; + final boolean[] reachable; + MaxFlowResult(int flow, boolean[] reachable) { + this.flow = flow; + this.reachable = reachable; + } + } + + private static MaxFlowResult edmondsKarpWithMinCut(int[][] capacity, int source, int sink) { + final int n = capacity.length; + int[][] residual = new int[n][n]; + for (int i = 0; i < n; i++) { + residual[i] = Arrays.copyOf(capacity[i], n); + } + + int[] parent = new int[n]; + int maxFlow = 0; + + while (bfs(residual, source, sink, parent)) { + int pathFlow = Integer.MAX_VALUE; + for (int v = sink; v != source; v = parent[v]) { + int u = parent[v]; + pathFlow = Math.min(pathFlow, residual[u][v]); + } + for (int v = sink; v != source; v = parent[v]) { + int u = parent[v]; + residual[u][v] -= pathFlow; + residual[v][u] += pathFlow; + } + maxFlow += pathFlow; + } + + boolean[] reachable = new boolean[n]; + markReachable(residual, source, reachable); + return new MaxFlowResult(maxFlow, reachable); + } + + private static boolean bfs(int[][] residual, int source, int sink, int[] parent) { + Arrays.fill(parent, -1); + parent[source] = source; + Queue q = new ArrayDeque<>(); + q.add(source); + while (!q.isEmpty()) { + int u = q.poll(); + for (int v = 0; v < residual.length; v++) { + if (residual[u][v] > 0 && parent[v] == -1) { + parent[v] = u; + if (v == sink) { + return true; + } + q.add(v); + } + } + } + return false; + } + + private static void markReachable(int[][] residual, int source, boolean[] vis) { + Arrays.fill(vis, false); + Queue q = new ArrayDeque<>(); + vis[source] = true; + q.add(source); + while (!q.isEmpty()) { + int u = q.poll(); + for (int v = 0; v < residual.length; v++) { + if (!vis[v] && residual[u][v] > 0) { + vis[v] = true; + q.add(v); + } + } + } + } +} diff --git a/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java b/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java index 05d7abbbcd6c..06101295e880 100644 --- a/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java +++ b/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java @@ -64,13 +64,21 @@ private static double doApproximate(Function fx, double a, doubl if (!validate(fx, a, b, n)) { throw new IllegalArgumentException("Invalid input parameters"); } - double totalArea = 0.0; + double total = 0.0; double interval = b - a; - for (int i = 0; i < n; i++) { + int pairs = n / 2; + for (int i = 0; i < pairs; i++) { + double u = generator.nextDouble(); + double x1 = a + u * interval; + double x2 = a + (1.0 - u) * interval; + total += fx.apply(x1); + total += fx.apply(x2); + } + if ((n & 1) == 1) { double x = a + generator.nextDouble() * interval; - totalArea += fx.apply(x); + total += fx.apply(x); } - return interval * totalArea / n; + return interval * total / n; } private static boolean validate(Function fx, double a, double b, int n) { diff --git a/src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java b/src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java new file mode 100644 index 000000000000..241f23c0fa1d --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java @@ -0,0 +1,132 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Queue; +import java.util.Random; +import java.util.random.RandomGenerator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class GomoryHuTreeTest { + + @Test + @DisplayName("Single node graph") + void singleNode() { + int[][] cap = {{0}}; + int[][] res = GomoryHuTree.buildTree(cap); + int[] parent = res[0]; + int[] weight = res[1]; + assertEquals(-1, parent[0]); + assertEquals(0, weight[0]); + } + + @Test + @DisplayName("Triangle undirected graph with known min-cuts") + void triangleGraph() { + // 0-1:3, 1-2:2, 0-2:4 + int[][] cap = new int[3][3]; + cap[0][1] = 3; + cap[1][0] = 3; + cap[1][2] = 2; + cap[2][1] = 2; + cap[0][2] = 4; + cap[2][0] = 4; + + int[][] tree = GomoryHuTree.buildTree(cap); + // validate all pairs via path-min-edge equals maxflow + validateAllPairs(cap, tree); + } + + @Test + @DisplayName("Random small undirected graphs compare to EdmondsKarp") + void randomSmallGraphs() { + Random rng = new Random(42); + for (int n = 2; n <= 6; n++) { + for (int iter = 0; iter < 10; iter++) { + int[][] cap = randSymmetricMatrix(n, 0, 5, rng); + int[][] tree = GomoryHuTree.buildTree(cap); + validateAllPairs(cap, tree); + } + } + } + + private static int[][] randSymmetricMatrix(int n, int lo, int hi, RandomGenerator rng) { + int[][] a = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int w = rng.nextInt(hi - lo + 1) + lo; + a[i][j] = w; + a[j][i] = w; + } + } + // zero diagonal + for (int i = 0; i < n; i++) { + a[i][i] = 0; + } + return a; + } + + private static void validateAllPairs(int[][] cap, int[][] tree) { + int n = cap.length; + int[] parent = tree[0]; + int[] weight = tree[1]; + + // build adjacency list of tree without generic array creation + List> g = new ArrayList<>(); + for (int i = 0; i < n; i++) { + g.add(new ArrayList<>()); + } + for (int v = 1; v < n; v++) { + int u = parent[v]; + int w = weight[v]; + g.get(u).add(new int[] {v, w}); + g.get(v).add(new int[] {u, w}); + } + + for (int s = 0; s < n; s++) { + for (int t = s + 1; t < n; t++) { + int treeVal = minEdgeOnPath(g, s, t); + int flowVal = EdmondsKarp.maxFlow(cap, s, t); + assertEquals(flowVal, treeVal, "pair (" + s + "," + t + ")"); + } + } + } + + private static int minEdgeOnPath(List> g, int s, int t) { + // BFS to record parent and edge weight along the path, since it's a tree, unique path exists + int n = g.size(); + int[] parent = new int[n]; + int[] edgeW = new int[n]; + Arrays.fill(parent, -1); + Queue q = new ArrayDeque<>(); + q.add(s); + parent[s] = s; + while (!q.isEmpty()) { + int u = q.poll(); + if (u == t) { + break; + } + for (int[] e : g.get(u)) { + int v = e[0]; + int w = e[1]; + if (parent[v] == -1) { + parent[v] = u; + edgeW[v] = w; + q.add(v); + } + } + } + int cur = t; + int ans = Integer.MAX_VALUE; + while (cur != s) { + ans = Math.min(ans, edgeW[cur]); + cur = parent[cur]; + } + return ans == Integer.MAX_VALUE ? 0 : ans; + } +} From a9ba87c4b6efc77f8444fdc94de26af3c6f67df2 Mon Sep 17 00:00:00 2001 From: Taranjeet Singh Kalsi Date: Fri, 28 Nov 2025 14:21:28 +0530 Subject: [PATCH 025/122] Added program to check Abundant number (#6961) added program for abundant number --- .../thealgorithms/maths/AbundantNumber.java | 58 +++++++++++++++++++ .../maths/AbundantNumberTest.java | 31 ++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/AbundantNumber.java create mode 100644 src/test/java/com/thealgorithms/maths/AbundantNumberTest.java diff --git a/src/main/java/com/thealgorithms/maths/AbundantNumber.java b/src/main/java/com/thealgorithms/maths/AbundantNumber.java new file mode 100644 index 000000000000..804ac4d71477 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AbundantNumber.java @@ -0,0 +1,58 @@ +package com.thealgorithms.maths; + +/** + * In number theory, an abundant number or excessive number is a positive integer for which + * the sum of its proper divisors is greater than the number. + * Equivalently, it is a number for which the sum of proper divisors (or aliquot sum) is greater than n. + * + * The integer 12 is the first abundant number. Its proper divisors are 1, 2, 3, 4 and 6 for a total of 16. + * + * Wiki: https://en.wikipedia.org/wiki/Abundant_number + */ +public final class AbundantNumber { + + private AbundantNumber() { + } + + // Function to calculate sum of all divisors including n + private static int sumOfDivisors(int n) { + int sum = 1 + n; // 1 and n are always divisors + for (int i = 2; i <= n / 2; i++) { + if (n % i == 0) { + sum += i; // adding divisor to sum + } + } + return sum; + } + + // Common validation method + private static void validatePositiveNumber(int number) { + if (number <= 0) { + throw new IllegalArgumentException("Number must be positive."); + } + } + + /** + * Check if {@code number} is an Abundant number or not by checking sum of divisors > 2n + * + * @param number the number + * @return {@code true} if {@code number} is an Abundant number, otherwise false + */ + public static boolean isAbundant(int number) { + validatePositiveNumber(number); + + return sumOfDivisors(number) > 2 * number; + } + + /** + * Check if {@code number} is an Abundant number or not by checking Aliquot Sum > n + * + * @param number the number + * @return {@code true} if {@code number} is a Abundant number, otherwise false + */ + public static boolean isAbundantNumber(int number) { + validatePositiveNumber(number); + + return AliquotSum.getAliquotSum(number) > number; + } +} diff --git a/src/test/java/com/thealgorithms/maths/AbundantNumberTest.java b/src/test/java/com/thealgorithms/maths/AbundantNumberTest.java new file mode 100644 index 000000000000..5b35345afd02 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AbundantNumberTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class AbundantNumberTest { + @ParameterizedTest + @CsvSource({"12", "66", "222", "444", "888", "2424"}) + void abundantNumbersTest(int n) { + assertTrue(AbundantNumber.isAbundant(n)); + assertTrue(AbundantNumber.isAbundantNumber(n)); + } + + @ParameterizedTest + @CsvSource({"1", "2", "6", "111", "333", "2222"}) + void nonAbundantNumbersTest(int n) { + assertFalse(AbundantNumber.isAbundant(n)); + assertFalse(AbundantNumber.isAbundantNumber(n)); + } + + @ParameterizedTest + @CsvSource({"0", "-1"}) + void throwsNegativeNumbersNotAllowed(int n) { + assertThrows(IllegalArgumentException.class, () -> AbundantNumber.isAbundant(n)); + assertThrows(IllegalArgumentException.class, () -> AbundantNumber.isAbundantNumber(n)); + } +} From ac6fef19dc1a689f8e16a8a7361d1868c2c69e83 Mon Sep 17 00:00:00 2001 From: Taranjeet Singh Kalsi Date: Mon, 1 Dec 2025 04:07:41 +0530 Subject: [PATCH 026/122] Added program to check either number is Evil or Odious (#6958) added program for evil number --- .../com/thealgorithms/maths/EvilNumber.java | 39 +++++++++++++++++++ .../thealgorithms/maths/EvilNumberTest.java | 28 +++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/EvilNumber.java create mode 100644 src/test/java/com/thealgorithms/maths/EvilNumberTest.java diff --git a/src/main/java/com/thealgorithms/maths/EvilNumber.java b/src/main/java/com/thealgorithms/maths/EvilNumber.java new file mode 100644 index 000000000000..419133702fd4 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/EvilNumber.java @@ -0,0 +1,39 @@ +package com.thealgorithms.maths; + +/** + * In number theory, an evil number is a non-negative integer that has an even number of 1s in its binary expansion. + * Non-negative integers that are not evil are called odious numbers. + * + * Evil Number Wiki: https://en.wikipedia.org/wiki/Evil_number + * Odious Number Wiki: https://en.wikipedia.org/wiki/Odious_number + */ +public final class EvilNumber { + + private EvilNumber() { + } + + // Function to count number of one bits in a number using bitwise operators + private static int countOneBits(int number) { + int oneBitCounter = 0; + while (number > 0) { + oneBitCounter += number & 1; // increment count if last bit is 1 + number >>= 1; // right shift to next bit + } + return oneBitCounter; + } + + /** + * Check either {@code number} is an Evil number or Odious number + * + * @param number the number + * @return {@code true} if {@code number} is an Evil number, otherwise false (in case of of Odious number) + */ + public static boolean isEvilNumber(int number) { + if (number < 0) { + throw new IllegalArgumentException("Negative numbers are not allowed."); + } + + int noOfOneBits = countOneBits(number); + return noOfOneBits % 2 == 0; + } +} diff --git a/src/test/java/com/thealgorithms/maths/EvilNumberTest.java b/src/test/java/com/thealgorithms/maths/EvilNumberTest.java new file mode 100644 index 000000000000..e59171fad25f --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/EvilNumberTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class EvilNumberTest { + @ParameterizedTest + @CsvSource({"0", "3", "10", "129", "222", "500", "777", "1198"}) + void evilNumbersTest(int n) { + assertTrue(EvilNumber.isEvilNumber(n)); + } + + @ParameterizedTest + @CsvSource({"1", "7", "100", "333", "555", "1199"}) + void odiousNumbersTest(int n) { + assertFalse(EvilNumber.isEvilNumber(n)); + } + + @ParameterizedTest + @CsvSource({"-1"}) + void throwsNegativeNumbersNotAllowed(int n) { + assertThrows(IllegalArgumentException.class, () -> EvilNumber.isEvilNumber(n)); + } +} From 14c0b0844e747e3c4a9b84e8a63b6db3f6f0294d Mon Sep 17 00:00:00 2001 From: Shewale Gopal Sanjay Date: Tue, 2 Dec 2025 01:43:14 +0530 Subject: [PATCH 027/122] Added threaded binary tree (#6995) * Add One-Time Pad Cipher implementation in Java * Add One-Time Pad Cipher implementation (fixed) * Add ThreadedBinaryTree with in-order traversal and tests * feat: add threaded binary tree implementation and tests * fix: remove redundant null check for SpotBugs compliance --- .../trees/ThreadedBinaryTree.java | 145 ++++++++++++++++++ .../trees/ThreadedBinaryTreeTest.java | 50 ++++++ 2 files changed, 195 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java create mode 100644 src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java b/src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java new file mode 100644 index 000000000000..fd8876cecb70 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java @@ -0,0 +1,145 @@ +/* + * TheAlgorithms (https://github.com/TheAlgorithms/Java) + * Author: Shewale41 + * This file is licensed under the MIT License. + */ + +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.List; + +/** + * Threaded binary tree implementation that supports insertion and + * in-order traversal without recursion or stack by using threads. + * + *

In this implementation, a node's null left/right pointers are used + * to point to the in-order predecessor/successor respectively. Two flags + * indicate whether left/right pointers are real children or threads. + * + * @see Wikipedia: + * Threaded binary tree + */ +public final class ThreadedBinaryTree { + + private Node root; + + private static final class Node { + int value; + Node left; + Node right; + boolean leftIsThread; + boolean rightIsThread; + + Node(int value) { + this.value = value; + this.left = null; + this.right = null; + this.leftIsThread = false; + this.rightIsThread = false; + } + } + + public ThreadedBinaryTree() { + this.root = null; + } + + /** + * Inserts a value into the threaded binary tree. Duplicate values are inserted + * to the right subtree (consistent deterministic rule). + * + * @param value the integer value to insert + */ + public void insert(int value) { + Node newNode = new Node(value); + if (root == null) { + root = newNode; + return; + } + + Node current = root; + Node parent = null; + + while (true) { + parent = current; + if (value < current.value) { + if (!current.leftIsThread && current.left != null) { + current = current.left; + } else { + break; + } + } else { // value >= current.value + if (!current.rightIsThread && current.right != null) { + current = current.right; + } else { + break; + } + } + } + + if (value < parent.value) { + // attach newNode as left child + newNode.left = parent.left; + newNode.leftIsThread = parent.leftIsThread; + newNode.right = parent; + newNode.rightIsThread = true; + + parent.left = newNode; + parent.leftIsThread = false; + } else { + // attach newNode as right child + newNode.right = parent.right; + newNode.rightIsThread = parent.rightIsThread; + newNode.left = parent; + newNode.leftIsThread = true; + + parent.right = newNode; + parent.rightIsThread = false; + } + } + + /** + * Returns the in-order traversal of the tree as a list of integers. + * Traversal is done without recursion or an explicit stack by following threads. + * + * @return list containing the in-order sequence of node values + */ + public List inorderTraversal() { + List result = new ArrayList<>(); + Node current = root; + if (current == null) { + return result; + } + + // Move to the leftmost node + while (current.left != null && !current.leftIsThread) { + current = current.left; + } + + while (current != null) { + result.add(current.value); + + // If right pointer is a thread, follow it + if (current.rightIsThread) { + current = current.right; + } else { + // Move to leftmost node in right subtree + current = current.right; + while (current != null && !current.leftIsThread && current.left != null) { + current = current.left; + } + } + } + + return result; + } + + /** + * Helper: checks whether the tree is empty. + * + * @return true if tree has no nodes + */ + public boolean isEmpty() { + return root == null; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java new file mode 100644 index 000000000000..c5973168438e --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java @@ -0,0 +1,50 @@ +/* + * TheAlgorithms (https://github.com/TheAlgorithms/Java) + * Author: Shewale41 + * This file is licensed under the MIT License. + */ + +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Basic tests for ThreadedBinaryTree inorder traversal. + */ +public class ThreadedBinaryTreeTest { + + @Test + public void testInorderTraversalSimple() { + ThreadedBinaryTree tree = new ThreadedBinaryTree(); + tree.insert(50); + tree.insert(30); + tree.insert(70); + tree.insert(20); + tree.insert(40); + tree.insert(60); + tree.insert(80); + + List expected = List.of(20, 30, 40, 50, 60, 70, 80); + List actual = tree.inorderTraversal(); + + assertEquals(expected, actual); + } + + @Test + public void testInorderWithDuplicates() { + ThreadedBinaryTree tree = new ThreadedBinaryTree(); + tree.insert(5); + tree.insert(3); + tree.insert(7); + tree.insert(7); // duplicate + tree.insert(2); + + List expected = List.of(2, 3, 5, 7, 7); + List actual = tree.inorderTraversal(); + + assertEquals(expected, actual); + } +} From 8d2cdb27db3b0979d28bbb2a723fa2e235a736b2 Mon Sep 17 00:00:00 2001 From: duvvuvenkataramana Date: Tue, 2 Dec 2025 01:47:32 +0530 Subject: [PATCH 028/122] feat: implement One-Time Pad cipher (#6941) (#7096) * feat: implement One-Time Pad cipher (#6941) * style: format OneTimePadCipher with clang-format --- .../ciphers/OneTimePadCipher.java | 89 +++++++++++++++++++ .../ciphers/OneTimePadCipherTest.java | 49 ++++++++++ 2 files changed, 138 insertions(+) create mode 100644 src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java create mode 100644 src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java diff --git a/src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java b/src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java new file mode 100644 index 000000000000..7733f5cb46f2 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java @@ -0,0 +1,89 @@ +package com.thealgorithms.ciphers; + +import java.security.SecureRandom; +import java.util.Objects; + +/** + * One-Time Pad (OTP) cipher implementation. + * + *

The One-Time Pad is information-theoretically secure if: + *

    + *
  • The key is truly random.
  • + *
  • The key length is at least as long as the plaintext.
  • + *
  • The key is used only once and kept secret.
  • + *
+ * + *

This implementation is for educational purposes only and should not be + * used in production systems. + */ +public final class OneTimePadCipher { + + private static final SecureRandom RANDOM = new SecureRandom(); + + private OneTimePadCipher() { + // utility class + } + + /** + * Generates a random key of the given length in bytes. + * + * @param length the length of the key in bytes, must be non-negative + * @return a new random key + * @throws IllegalArgumentException if length is negative + */ + public static byte[] generateKey(int length) { + if (length < 0) { + throw new IllegalArgumentException("length must be non-negative"); + } + byte[] key = new byte[length]; + RANDOM.nextBytes(key); + return key; + } + + /** + * Encrypts the given plaintext bytes using the provided key. + *

The key length must be exactly the same as the plaintext length. + * + * @param plaintext the plaintext bytes, must not be {@code null} + * @param key the one-time pad key bytes, must not be {@code null} + * @return the ciphertext bytes + * @throws IllegalArgumentException if the key length does not match plaintext length + * @throws NullPointerException if plaintext or key is {@code null} + */ + public static byte[] encrypt(byte[] plaintext, byte[] key) { + validateInputs(plaintext, key); + return xor(plaintext, key); + } + + /** + * Decrypts the given ciphertext bytes using the provided key. + *

For a One-Time Pad, decryption is identical to encryption: + * {@code plaintext = ciphertext XOR key}. + * + * @param ciphertext the ciphertext bytes, must not be {@code null} + * @param key the one-time pad key bytes, must not be {@code null} + * @return the decrypted plaintext bytes + * @throws IllegalArgumentException if the key length does not match ciphertext length + * @throws NullPointerException if ciphertext or key is {@code null} + */ + public static byte[] decrypt(byte[] ciphertext, byte[] key) { + validateInputs(ciphertext, key); + return xor(ciphertext, key); + } + + private static void validateInputs(byte[] input, byte[] key) { + Objects.requireNonNull(input, "input must not be null"); + Objects.requireNonNull(key, "key must not be null"); + if (input.length != key.length) { + throw new IllegalArgumentException("Key length must match input length"); + } + } + + private static byte[] xor(byte[] data, byte[] key) { + byte[] result = new byte[data.length]; + for (int i = 0; i < data.length; i++) { + result[i] = (byte) (data[i] ^ key[i]); + } + return result; + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java b/src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java new file mode 100644 index 000000000000..837c56c603d4 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Test; + +class OneTimePadCipherTest { + + @Test + void encryptAndDecryptWithRandomKeyRestoresPlaintext() { + String plaintext = "The quick brown fox jumps over the lazy dog."; + byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8); + + byte[] key = OneTimePadCipher.generateKey(plaintextBytes.length); + + byte[] ciphertext = OneTimePadCipher.encrypt(plaintextBytes, key); + byte[] decrypted = OneTimePadCipher.decrypt(ciphertext, key); + + assertArrayEquals(plaintextBytes, decrypted); + assertEquals(plaintext, new String(decrypted, StandardCharsets.UTF_8)); + } + + @Test + void generateKeyWithNegativeLengthThrowsException() { + assertThrows(IllegalArgumentException.class, () -> OneTimePadCipher.generateKey(-1)); + } + + @Test + void encryptWithMismatchedKeyLengthThrowsException() { + byte[] data = "hello".getBytes(StandardCharsets.UTF_8); + byte[] shortKey = OneTimePadCipher.generateKey(2); + + assertThrows(IllegalArgumentException.class, () -> OneTimePadCipher.encrypt(data, shortKey)); + } + + @Test + void decryptWithMismatchedKeyLengthThrowsException() { + byte[] data = "hello".getBytes(StandardCharsets.UTF_8); + byte[] key = OneTimePadCipher.generateKey(data.length); + byte[] ciphertext = OneTimePadCipher.encrypt(data, key); + + byte[] wrongSizedKey = OneTimePadCipher.generateKey(data.length + 1); + + assertThrows(IllegalArgumentException.class, () -> OneTimePadCipher.decrypt(ciphertext, wrongSizedKey)); + } +} From 927fe1f87cdfe2ea57051812427e78c9cf6f4775 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:54:59 +0100 Subject: [PATCH 029/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 12.1.2 to 12.2.0 (#7147) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 12.1.2 to 12.2.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-12.1.2...checkstyle-12.2.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 12.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b918cdad5bb..dc7d211b2742 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 12.1.2 + 12.2.0 From bd7f269aa992e87359d82b3d3d289b4fefde5eea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 08:58:42 +0000 Subject: [PATCH 030/122] chore(deps): bump gitpod/workspace-java-21 from 2025-10-06-13-14-25 to 2025-11-14-10-05-32 (#7148) chore(deps): bump gitpod/workspace-java-21 Bumps gitpod/workspace-java-21 from 2025-10-06-13-14-25 to 2025-11-14-10-05-32. --- updated-dependencies: - dependency-name: gitpod/workspace-java-21 dependency-version: 2025-11-14-10-05-32 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .gitpod.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile index 4195f928d1bc..a8951da7de26 100644 --- a/.gitpod.dockerfile +++ b/.gitpod.dockerfile @@ -1,4 +1,4 @@ -FROM gitpod/workspace-java-21:2025-10-06-13-14-25 +FROM gitpod/workspace-java-21:2025-11-14-10-05-32 ENV LLVM_SCRIPT="tmp_llvm.sh" From 246162e5bdc6b9f4eb4cf8287e31eb44c36301da Mon Sep 17 00:00:00 2001 From: espinosadev <163946778+espinosa-dev@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:45:25 +0100 Subject: [PATCH 031/122] Fixed redundant boolean logic (#7151) --- .../java/com/thealgorithms/strings/Upper.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/Upper.java b/src/main/java/com/thealgorithms/strings/Upper.java index 5e248cb6ee39..85db7d41e1aa 100644 --- a/src/main/java/com/thealgorithms/strings/Upper.java +++ b/src/main/java/com/thealgorithms/strings/Upper.java @@ -15,23 +15,27 @@ public static void main(String[] args) { } /** - * Converts all the characters in this {@code String} to upper case + * Converts all the characters in this {@code String} to upper case. * * @param s the string to convert * @return the {@code String}, converted to uppercase. */ public static String toUpperCase(String s) { if (s == null) { - throw new IllegalArgumentException("Input string connot be null"); + throw new IllegalArgumentException("Input string cannot be null"); } if (s.isEmpty()) { return s; } - StringBuilder result = new StringBuilder(s); - for (int i = 0; i < result.length(); ++i) { - char currentChar = result.charAt(i); - if (Character.isLetter(currentChar) && Character.isLowerCase(currentChar)) { - result.setCharAt(i, Character.toUpperCase(currentChar)); + + StringBuilder result = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); ++i) { + char currentChar = s.charAt(i); + if (Character.isLowerCase(currentChar)) { + result.append(Character.toUpperCase(currentChar)); + } else { + result.append(currentChar); } } return result.toString(); From 316def4ea65f68ca8982148b97896c8bfef27e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A1nchez=20N=C3=BA=C3=B1ez?= Date: Sun, 7 Dec 2025 12:18:55 +0100 Subject: [PATCH 032/122] Add Snell's Law refraction algorithm (#7150) * Add Snell's Law implementation for refraction angle * Add tests for Snell's Law calculations * Update documentation with reference link to Snell's Law Added a reference link to Snell's Law in the documentation. * Prevent instantiation of SnellLaw class Make SnellLaw class non-instantiable by adding a private constructor. * Rename SnellsLawTest to SnellLawTest * Refactor SnellLawTest for clarity and accuracy * Rename SnellsLaw.java to SnellLaw.java * Refactor SnellLawTest with additional assertions * Refactor SnellLaw class constructor and error handling Refactor SnellLaw constructor and error message formatting. * Fix missing newline at end of SnellLawTest.java Ensure that the SnellLawTest class has a newline at the end of the file. * Simplify assertions in SnellLawTest * Simplify exception throwing for total internal reflection --- .../com/thealgorithms/physics/SnellLaw.java | 33 +++++++++++++++ .../thealgorithms/physics/SnellLawTest.java | 41 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/main/java/com/thealgorithms/physics/SnellLaw.java create mode 100644 src/test/java/com/thealgorithms/physics/SnellLawTest.java diff --git a/src/main/java/com/thealgorithms/physics/SnellLaw.java b/src/main/java/com/thealgorithms/physics/SnellLaw.java new file mode 100644 index 000000000000..2736984814fd --- /dev/null +++ b/src/main/java/com/thealgorithms/physics/SnellLaw.java @@ -0,0 +1,33 @@ +package com.thealgorithms.physics; + +/** + * Calculates refraction angle using Snell's Law: + * n1 * sin(theta1) = n2 * sin(theta2) + * @see Snell's Law + */ +public final class SnellLaw { + + private SnellLaw() { + throw new AssertionError("No instances."); + } + + /** + * Computes the refracted angle (theta2) in radians. + * + * @param n1 index of refraction of medium 1 + * @param n2 index of refraction of medium 2 + * @param theta1 incident angle in radians + * @return refracted angle (theta2) in radians + * @throws IllegalArgumentException if total internal reflection occurs + */ + public static double refractedAngle(double n1, double n2, double theta1) { + double ratio = n1 / n2; + double sinTheta2 = ratio * Math.sin(theta1); + + if (Math.abs(sinTheta2) > 1.0) { + throw new IllegalArgumentException("Total internal reflection: no refraction possible."); + } + + return Math.asin(sinTheta2); + } +} diff --git a/src/test/java/com/thealgorithms/physics/SnellLawTest.java b/src/test/java/com/thealgorithms/physics/SnellLawTest.java new file mode 100644 index 000000000000..ddd5fb1d5af7 --- /dev/null +++ b/src/test/java/com/thealgorithms/physics/SnellLawTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.physics; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class SnellLawTest { + + @Test + public void testRefractedAngle() { + double n1 = 1.0; // air + double n2 = 1.5; // glass + double theta1 = Math.toRadians(30); + + double theta2 = SnellLaw.refractedAngle(n1, n2, theta1); + + double expected = Math.asin(n1 / n2 * Math.sin(theta1)); + + assertEquals(expected, theta2, 1e-12); + } + + @Test + public void testTotalInternalReflection() { + double n1 = 1.5; + double n2 = 1.0; + double theta1 = Math.toRadians(60); // large angle + + assertThrows(IllegalArgumentException.class, () -> SnellLaw.refractedAngle(n1, n2, theta1)); + } + + @Test + public void testNoTotalInternalReflectionAtLowAngles() { + double n1 = 1.5; + double n2 = 1.0; + double theta1 = Math.toRadians(10); + + assertDoesNotThrow(() -> SnellLaw.refractedAngle(n1, n2, theta1)); + } +} From e7c3e1f7739a494d96ded480b167b28ca657f1cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 09:43:17 +0100 Subject: [PATCH 033/122] chore(deps): bump com.mebigfatguy.fb-contrib:fb-contrib from 7.7.1 to 7.7.2 (#7153) chore(deps): bump com.mebigfatguy.fb-contrib:fb-contrib Bumps [com.mebigfatguy.fb-contrib:fb-contrib](https://github.com/mebigfatguy/fb-contrib) from 7.7.1 to 7.7.2. - [Commits](https://github.com/mebigfatguy/fb-contrib/commits/v7.7.2) --- updated-dependencies: - dependency-name: com.mebigfatguy.fb-contrib:fb-contrib dependency-version: 7.7.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc7d211b2742..4baf7a5f99b0 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.mebigfatguy.fb-contrib fb-contrib - 7.7.1 + 7.7.2 com.h3xstream.findsecbugs From 520151ab8e51b502e96cd959ebdfdfff7468aa22 Mon Sep 17 00:00:00 2001 From: codeahl <142953446+codeahl@users.noreply.github.com> Date: Wed, 10 Dec 2025 03:00:28 -0600 Subject: [PATCH 034/122] Add Big-O time and space complexity comments for sorting algorithms (#7155) Add time and space complexity comments for specific sorting algorithms Co-authored-by: Deniz Altunkapan --- .../com/thealgorithms/sorts/BubbleSort.java | 7 +++++++ .../java/com/thealgorithms/sorts/HeapSort.java | 13 ++++++++++++- .../com/thealgorithms/sorts/InsertionSort.java | 18 ++++++++++++++++++ .../com/thealgorithms/sorts/MergeSort.java | 13 +++++++++---- .../com/thealgorithms/sorts/QuickSort.java | 11 +++++++++-- .../com/thealgorithms/sorts/SelectionSort.java | 13 +++++++++---- .../thealgorithms/sorts/TopologicalSort.java | 11 +++++++++-- 7 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/BubbleSort.java b/src/main/java/com/thealgorithms/sorts/BubbleSort.java index 6823c68d0a74..d2eca3506c2d 100644 --- a/src/main/java/com/thealgorithms/sorts/BubbleSort.java +++ b/src/main/java/com/thealgorithms/sorts/BubbleSort.java @@ -10,6 +10,13 @@ class BubbleSort implements SortAlgorithm { /** * Implements generic bubble sort algorithm. * + * Time Complexity: + * - Best case: O(n) – array is already sorted. + * - Average case: O(n^2) + * - Worst case: O(n^2) + * + * Space Complexity: O(1) – in-place sorting. + * * @param array the array to be sorted. * @param the type of elements in the array. * @return the sorted array. diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java index e798fb91b925..5e3b20f43e10 100644 --- a/src/main/java/com/thealgorithms/sorts/HeapSort.java +++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java @@ -1,9 +1,20 @@ package com.thealgorithms.sorts; /** - * Heap Sort Algorithm Implementation + * Heap Sort algorithm implementation. + * + * Heap sort converts the array into a max-heap and repeatedly extracts the maximum + * element to sort the array in increasing order. + * + * Time Complexity: + * - Best case: O(n log n) + * - Average case: O(n log n) + * - Worst case: O(n log n) + * + * Space Complexity: O(1) – in-place sorting * * @see Heap Sort Algorithm + * @see SortAlgorithm */ public class HeapSort implements SortAlgorithm { diff --git a/src/main/java/com/thealgorithms/sorts/InsertionSort.java b/src/main/java/com/thealgorithms/sorts/InsertionSort.java index 21ebf3827b5f..fdbfd9cd1cfa 100644 --- a/src/main/java/com/thealgorithms/sorts/InsertionSort.java +++ b/src/main/java/com/thealgorithms/sorts/InsertionSort.java @@ -1,5 +1,23 @@ package com.thealgorithms.sorts; +/** + * Generic Insertion Sort algorithm. + * + * Standard insertion sort iterates through the array and inserts each element into its + * correct position in the sorted portion of the array. + * + * Sentinel sort is a variation that first places the minimum element at index 0 to + * avoid redundant comparisons in subsequent passes. + * + * Time Complexity: + * - Best case: O(n) – array is already sorted (sentinel sort can improve slightly) + * - Average case: O(n^2) + * - Worst case: O(n^2) – array is reverse sorted + * + * Space Complexity: O(1) – in-place sorting + * + * @see SortAlgorithm + */ class InsertionSort implements SortAlgorithm { /** diff --git a/src/main/java/com/thealgorithms/sorts/MergeSort.java b/src/main/java/com/thealgorithms/sorts/MergeSort.java index 86a184f67b26..f7a7c8da004d 100644 --- a/src/main/java/com/thealgorithms/sorts/MergeSort.java +++ b/src/main/java/com/thealgorithms/sorts/MergeSort.java @@ -13,11 +13,16 @@ class MergeSort implements SortAlgorithm { private Comparable[] aux; /** - * Generic merge sort algorithm implements. + * Generic merge sort algorithm. * - * @param unsorted the array which should be sorted. - * @param Comparable class. - * @return sorted array. + * Time Complexity: + * - Best case: O(n log n) + * - Average case: O(n log n) + * - Worst case: O(n log n) + * + * Space Complexity: O(n) – requires auxiliary array for merging. + * + * @see SortAlgorithm */ @Override public > T[] sort(T[] unsorted) { diff --git a/src/main/java/com/thealgorithms/sorts/QuickSort.java b/src/main/java/com/thealgorithms/sorts/QuickSort.java index 3abb1aae2306..a025e6909259 100644 --- a/src/main/java/com/thealgorithms/sorts/QuickSort.java +++ b/src/main/java/com/thealgorithms/sorts/QuickSort.java @@ -8,9 +8,16 @@ class QuickSort implements SortAlgorithm { /** - * This method implements the Generic Quick Sort + * Generic Quick Sort algorithm. * - * @param array The array to be sorted Sorts the array in increasing order + * Time Complexity: + * - Best case: O(n log n) – pivot splits array roughly in half each time. + * - Average case: O(n log n) + * - Worst case: O(n^2) – occurs when pivot consistently produces unbalanced splits. + * + * Space Complexity: O(log n) – recursion stack, in-place sorting. + * + * @see SortAlgorithm */ @Override public > T[] sort(T[] array) { diff --git a/src/main/java/com/thealgorithms/sorts/SelectionSort.java b/src/main/java/com/thealgorithms/sorts/SelectionSort.java index db7732d7e218..2d1814441701 100644 --- a/src/main/java/com/thealgorithms/sorts/SelectionSort.java +++ b/src/main/java/com/thealgorithms/sorts/SelectionSort.java @@ -2,11 +2,16 @@ public class SelectionSort implements SortAlgorithm { /** - * Sorts an array of comparable elements in increasing order using the selection sort algorithm. + * Generic Selection Sort algorithm. * - * @param array the array to be sorted - * @param the class of array elements - * @return the sorted array + * Time Complexity: + * - Best case: O(n^2) + * - Average case: O(n^2) + * - Worst case: O(n^2) + * + * Space Complexity: O(1) – in-place sorting. + * + * @see SortAlgorithm */ @Override public > T[] sort(T[] array) { diff --git a/src/main/java/com/thealgorithms/sorts/TopologicalSort.java b/src/main/java/com/thealgorithms/sorts/TopologicalSort.java index e4ed240a9947..382ddde9a6f2 100644 --- a/src/main/java/com/thealgorithms/sorts/TopologicalSort.java +++ b/src/main/java/com/thealgorithms/sorts/TopologicalSort.java @@ -11,9 +11,16 @@ * a linked list. A Directed Graph is proven to be acyclic when a DFS or Depth First Search is * performed, yielding no back-edges. * - * https://en.wikipedia.org/wiki/Topological_sorting + * Time Complexity: O(V + E) + * - V: number of vertices + * - E: number of edges * - * @author Jonathan Taylor (https://github.com/Jtmonument) + * Space Complexity: O(V + E) + * - adjacency list and recursion stack in DFS + * + * Reference: https://en.wikipedia.org/wiki/Topological_sorting + * + * Author: Jonathan Taylor (https://github.com/Jtmonument) * Based on Introduction to Algorithms 3rd Edition */ public final class TopologicalSort { From 4f40206684b026f8c0315d8fa419f4951e0500ba Mon Sep 17 00:00:00 2001 From: Lucas Horn <146038502+LucasAHorn@users.noreply.github.com> Date: Wed, 10 Dec 2025 03:04:32 -0600 Subject: [PATCH 035/122] Add unit tests for graph algorithms (#7133) (#7156) * Add unit tests for graph algorithms (BFS, DFS, Dijkstra, Bellman-Ford) #7133 - Created BellmanFordTest.java with 10 test methods covering various graph scenarios - Created ConnectedComponentTest.java with 15 test methods for DFS-based component counting - Enhanced MatrixGraphsTest.java with additional BFS/DFS test cases - Enhanced DijkstraAlgorithmTest.java with comprehensive shortest path tests Fixes #7133 * fix: format files using clang * fix: removing my changes on pull_request_template --------- Co-authored-by: prashantdubeypng --- .../graphs/BellmanFordTest.java | 158 +++++++++++++ .../graphs/ConnectedComponentTest.java | 204 +++++++++++++++++ .../graphs/DijkstraAlgorithmTest.java | 117 ++++++++++ .../graphs/MatrixGraphsTest.java | 211 ++++++++++++++++++ 4 files changed, 690 insertions(+) create mode 100644 src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java create mode 100644 src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java new file mode 100644 index 000000000000..c824241c680d --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java @@ -0,0 +1,158 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the BellmanFord algorithm implementation. + * Tests cover various graph scenarios including: + * - Simple weighted graphs + * - Graphs with negative weights + * - Single vertex graphs + * - Disconnected graphs + * - Linear path graphs + */ +class BellmanFordTest { + + @Test + void testSimpleGraph() { + // Create a simple graph with 5 vertices and 8 edges + // Graph visualization: + // 1 + // /|\ + // 6 | 7 + // / | \ + // 0 5 2 + // \ | / + // 8 | -2 + // \|/ + // 4---3 + // 9 + BellmanFord bellmanFord = new BellmanFord(5, 8); + bellmanFord.addEdge(0, 1, 6); + bellmanFord.addEdge(0, 4, 8); + bellmanFord.addEdge(1, 2, 7); + bellmanFord.addEdge(1, 4, 5); + bellmanFord.addEdge(2, 3, -2); + bellmanFord.addEdge(2, 4, -3); + bellmanFord.addEdge(3, 4, 9); + bellmanFord.addEdge(4, 3, 7); + + // Verify edge array creation + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(8, bellmanFord.getEdgeArray().length); + } + + @Test + void testGraphWithNegativeWeights() { + // Graph with negative edge weights (but no negative cycle) + BellmanFord bellmanFord = new BellmanFord(4, 5); + bellmanFord.addEdge(0, 1, 4); + bellmanFord.addEdge(0, 2, 5); + bellmanFord.addEdge(1, 2, -3); + bellmanFord.addEdge(2, 3, 4); + bellmanFord.addEdge(1, 3, 6); + + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(5, bellmanFord.getEdgeArray().length); + } + + @Test + void testSingleVertexGraph() { + // Graph with single vertex and no edges + BellmanFord bellmanFord = new BellmanFord(1, 0); + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(0, bellmanFord.getEdgeArray().length); + } + + @Test + void testLinearGraph() { + // Linear graph: 0 -> 1 -> 2 -> 3 + BellmanFord bellmanFord = new BellmanFord(4, 3); + bellmanFord.addEdge(0, 1, 2); + bellmanFord.addEdge(1, 2, 3); + bellmanFord.addEdge(2, 3, 4); + + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(3, bellmanFord.getEdgeArray().length); + } + + @Test + void testEdgeAddition() { + BellmanFord bellmanFord = new BellmanFord(3, 3); + + bellmanFord.addEdge(0, 1, 5); + bellmanFord.addEdge(1, 2, 3); + bellmanFord.addEdge(0, 2, 10); + + // Verify all edges were added + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(3, bellmanFord.getEdgeArray().length); + } + + @Test + void testGraphWithZeroWeightEdges() { + // Graph with zero weight edges + BellmanFord bellmanFord = new BellmanFord(3, 3); + bellmanFord.addEdge(0, 1, 0); + bellmanFord.addEdge(1, 2, 0); + bellmanFord.addEdge(0, 2, 1); + + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(3, bellmanFord.getEdgeArray().length); + } + + @Test + void testLargerGraph() { + // Larger graph with 6 vertices + BellmanFord bellmanFord = new BellmanFord(6, 9); + bellmanFord.addEdge(0, 1, 5); + bellmanFord.addEdge(0, 2, 3); + bellmanFord.addEdge(1, 3, 6); + bellmanFord.addEdge(1, 2, 2); + bellmanFord.addEdge(2, 4, 4); + bellmanFord.addEdge(2, 5, 2); + bellmanFord.addEdge(2, 3, 7); + bellmanFord.addEdge(3, 4, -1); + bellmanFord.addEdge(4, 5, -2); + + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(9, bellmanFord.getEdgeArray().length); + } + + @Test + void testVertexAndEdgeCount() { + BellmanFord bellmanFord = new BellmanFord(10, 15); + assertEquals(10, bellmanFord.vertex); + assertEquals(15, bellmanFord.edge); + } + + @Test + void testMultipleEdgesBetweenSameVertices() { + // Graph allowing multiple edges between same vertices + BellmanFord bellmanFord = new BellmanFord(2, 3); + bellmanFord.addEdge(0, 1, 5); + bellmanFord.addEdge(0, 1, 3); + bellmanFord.addEdge(1, 0, 2); + + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(3, bellmanFord.getEdgeArray().length); + } + + @Test + void testCompleteGraph() { + // Complete graph with 4 vertices (6 edges for undirected equivalent) + BellmanFord bellmanFord = new BellmanFord(4, 6); + bellmanFord.addEdge(0, 1, 1); + bellmanFord.addEdge(0, 2, 2); + bellmanFord.addEdge(0, 3, 3); + bellmanFord.addEdge(1, 2, 4); + bellmanFord.addEdge(1, 3, 5); + bellmanFord.addEdge(2, 3, 6); + + assertNotNull(bellmanFord.getEdgeArray()); + assertEquals(6, bellmanFord.getEdgeArray().length); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java new file mode 100644 index 000000000000..b5cfdd9de04f --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java @@ -0,0 +1,204 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the Graph class in ConnectedComponent.java. + * Tests the depth-first search implementation and connected component counting. + * Covers various graph topologies including: + * - Single connected components + * - Multiple disconnected components + * - Self-loops + * - Linear chains + * - Cyclic graphs + */ +class ConnectedComponentTest { + + @Test + void testSingleConnectedComponent() { + Graph graph = new Graph<>(); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 4); + graph.addEdge(4, 1); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testTwoDisconnectedComponents() { + Graph graph = new Graph<>(); + // Component 1: 1-2-3 + graph.addEdge(1, 2); + graph.addEdge(2, 3); + // Component 2: 4-5 + graph.addEdge(4, 5); + + assertEquals(2, graph.countGraphs()); + } + + @Test + void testThreeDisconnectedComponents() { + Graph graph = new Graph<>(); + // Component 1: a-b-c-d-e + graph.addEdge('a', 'b'); + graph.addEdge('a', 'e'); + graph.addEdge('b', 'e'); + graph.addEdge('b', 'c'); + graph.addEdge('c', 'd'); + graph.addEdge('d', 'a'); + // Component 2: x-y-z + graph.addEdge('x', 'y'); + graph.addEdge('x', 'z'); + // Component 3: w (self-loop) + graph.addEdge('w', 'w'); + + assertEquals(3, graph.countGraphs()); + } + + @Test + void testSingleNodeSelfLoop() { + Graph graph = new Graph<>(); + graph.addEdge(1, 1); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testLinearChain() { + Graph graph = new Graph<>(); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 4); + graph.addEdge(4, 5); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testStarTopology() { + // Star graph with center node 0 connected to nodes 1, 2, 3, 4 + Graph graph = new Graph<>(); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(0, 3); + graph.addEdge(0, 4); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testCompleteGraph() { + // Complete graph K4: every node connected to every other node + Graph graph = new Graph<>(); + graph.addEdge(1, 2); + graph.addEdge(1, 3); + graph.addEdge(1, 4); + graph.addEdge(2, 3); + graph.addEdge(2, 4); + graph.addEdge(3, 4); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testStringVertices() { + Graph graph = new Graph<>(); + // Component 1 + graph.addEdge("New York", "Los Angeles"); + graph.addEdge("Los Angeles", "Chicago"); + // Component 2 + graph.addEdge("London", "Paris"); + // Component 3 + graph.addEdge("Tokyo", "Tokyo"); + + assertEquals(3, graph.countGraphs()); + } + + @Test + void testEmptyGraph() { + Graph graph = new Graph<>(); + assertEquals(0, graph.countGraphs()); + } + + @Test + void testDepthFirstSearchBasic() { + Graph graph = new Graph<>(); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + + // Get the first node and perform DFS + assertNotNull(graph.nodeList); + assertEquals(3, graph.nodeList.size()); + } + + @Test + void testManyIsolatedComponents() { + Graph graph = new Graph<>(); + // Create 5 isolated components (each is a self-loop) + graph.addEdge(1, 1); + graph.addEdge(2, 2); + graph.addEdge(3, 3); + graph.addEdge(4, 4); + graph.addEdge(5, 5); + + assertEquals(5, graph.countGraphs()); + } + + @Test + void testBidirectionalEdges() { + Graph graph = new Graph<>(); + // Note: This is a directed graph representation + // Adding edge 1->2 does not automatically add 2->1 + graph.addEdge(1, 2); + graph.addEdge(2, 1); + graph.addEdge(2, 3); + graph.addEdge(3, 2); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testCyclicGraph() { + Graph graph = new Graph<>(); + // Create a cycle: 1 -> 2 -> 3 -> 4 -> 1 + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 4); + graph.addEdge(4, 1); + + assertEquals(1, graph.countGraphs()); + } + + @Test + void testMultipleCycles() { + Graph graph = new Graph<>(); + // Cycle 1: 1 -> 2 -> 3 -> 1 + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 1); + // Cycle 2: 4 -> 5 -> 4 + graph.addEdge(4, 5); + graph.addEdge(5, 4); + + assertEquals(2, graph.countGraphs()); + } + + @Test + void testIntegerGraphFromMainExample() { + // Recreate the example from main method + Graph graph = new Graph<>(); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(2, 4); + graph.addEdge(3, 5); + graph.addEdge(7, 8); + graph.addEdge(8, 10); + graph.addEdge(10, 8); + + assertEquals(2, graph.countGraphs()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java index c5df9acdf33b..a189091c17d3 100644 --- a/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java +++ b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.datastructures.graphs; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.BeforeEach; @@ -61,4 +62,120 @@ void testInvalidSourceVertex() { assertThrows(IllegalArgumentException.class, () -> dijkstraAlgorithm.run(graph, -1)); assertThrows(IllegalArgumentException.class, () -> dijkstraAlgorithm.run(graph, graph.length)); } + + @Test + void testLinearGraph() { + // Linear graph: 0 - 1 - 2 - 3 + // with weights: 2 3 4 + int[][] linearGraph = {{0, 2, 0, 0}, {2, 0, 3, 0}, {0, 3, 0, 4}, {0, 0, 4, 0}}; + + DijkstraAlgorithm dijkstraLinear = new DijkstraAlgorithm(4); + int[] distances = dijkstraLinear.run(linearGraph, 0); + + assertArrayEquals(new int[] {0, 2, 5, 9}, distances); + } + + @Test + void testStarTopology() { + // Star graph: center node 0 connected to all others + // 1(2) + // | + // 3(4)-0-2(3) + // | + // 4(5) + int[][] starGraph = {{0, 2, 3, 4, 5}, {2, 0, 0, 0, 0}, {3, 0, 0, 0, 0}, {4, 0, 0, 0, 0}, {5, 0, 0, 0, 0}}; + + DijkstraAlgorithm dijkstraStar = new DijkstraAlgorithm(5); + int[] distances = dijkstraStar.run(starGraph, 0); + + assertArrayEquals(new int[] {0, 2, 3, 4, 5}, distances); + } + + @Test + void testCompleteGraphK4() { + // Complete graph K4 with varying weights + int[][] completeGraph = {{0, 1, 2, 3}, {1, 0, 4, 5}, {2, 4, 0, 6}, {3, 5, 6, 0}}; + + DijkstraAlgorithm dijkstraComplete = new DijkstraAlgorithm(4); + int[] distances = dijkstraComplete.run(completeGraph, 0); + + // Direct paths from 0 are shortest + assertArrayEquals(new int[] {0, 1, 2, 3}, distances); + } + + @Test + void testDifferentSourceVertex() { + // Test running from different source vertices + int[][] simpleGraph = {{0, 5, 0, 0}, {5, 0, 3, 0}, {0, 3, 0, 2}, {0, 0, 2, 0}}; + + DijkstraAlgorithm dijkstra = new DijkstraAlgorithm(4); + + // From vertex 0 + int[] distFrom0 = dijkstra.run(simpleGraph, 0); + assertArrayEquals(new int[] {0, 5, 8, 10}, distFrom0); + + // From vertex 2 + int[] distFrom2 = dijkstra.run(simpleGraph, 2); + assertArrayEquals(new int[] {8, 3, 0, 2}, distFrom2); + + // From vertex 3 + int[] distFrom3 = dijkstra.run(simpleGraph, 3); + assertArrayEquals(new int[] {10, 5, 2, 0}, distFrom3); + } + + @Test + void testUnitWeightGraph() { + // Graph with all unit weights (like BFS distance) + int[][] unitGraph = {{0, 1, 1, 0}, {1, 0, 1, 1}, {1, 1, 0, 1}, {0, 1, 1, 0}}; + + DijkstraAlgorithm dijkstraUnit = new DijkstraAlgorithm(4); + int[] distances = dijkstraUnit.run(unitGraph, 0); + + assertArrayEquals(new int[] {0, 1, 1, 2}, distances); + } + + @Test + void testTwoVertexGraph() { + int[][] twoVertexGraph = {{0, 7}, {7, 0}}; + + DijkstraAlgorithm dijkstraTwo = new DijkstraAlgorithm(2); + int[] distances = dijkstraTwo.run(twoVertexGraph, 0); + + assertArrayEquals(new int[] {0, 7}, distances); + } + + @Test + void testShortcutPath() { + // Graph where direct path is longer than indirect path + // 0 --(10)--> 2 + // 0 --(1)--> 1 --(2)--> 2 + int[][] shortcutGraph = {{0, 1, 10}, {1, 0, 2}, {10, 2, 0}}; + + DijkstraAlgorithm dijkstraShortcut = new DijkstraAlgorithm(3); + int[] distances = dijkstraShortcut.run(shortcutGraph, 0); + + // The shortest path to vertex 2 should be 3 (via vertex 1), not 10 (direct) + assertArrayEquals(new int[] {0, 1, 3}, distances); + } + + @Test + void testSourceToSourceDistanceIsZero() { + // Verify distance from source to itself is always 0 + int[] distances = dijkstraAlgorithm.run(graph, 0); + assertEquals(0, distances[0]); + + distances = dijkstraAlgorithm.run(graph, 5); + assertEquals(0, distances[5]); + } + + @Test + void testLargeWeights() { + // Graph with large weights + int[][] largeWeightGraph = {{0, 1000, 0}, {1000, 0, 2000}, {0, 2000, 0}}; + + DijkstraAlgorithm dijkstraLarge = new DijkstraAlgorithm(3); + int[] distances = dijkstraLarge.run(largeWeightGraph, 0); + + assertArrayEquals(new int[] {0, 1000, 3000}, distances); + } } diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java index cc8a2df872ce..eaff0222bd36 100644 --- a/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java +++ b/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java @@ -137,4 +137,215 @@ void testDisconnectedGraph() { assertTrue(dfs.containsAll(Arrays.asList(0, 1))); assertTrue(bfs.containsAll(Arrays.asList(0, 1))); } + + @Test + void testSingleVertexGraphDfs() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(1); + + List dfs = graph.depthFirstOrder(0); + assertEquals(1, dfs.size()); + assertEquals(0, dfs.getFirst()); + } + + @Test + void testSingleVertexGraphBfs() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(1); + + List bfs = graph.breadthFirstOrder(0); + assertEquals(1, bfs.size()); + assertEquals(0, bfs.getFirst()); + } + + @Test + void testBfsLevelOrder() { + // Create a graph where BFS should visit level by level + // 0 + // /|\ + // 1 2 3 + // | + // 4 + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(0, 3); + graph.addEdge(1, 4); + + List bfs = graph.breadthFirstOrder(0); + assertEquals(5, bfs.size()); + assertEquals(0, bfs.get(0)); + // Level 1 vertices (1, 2, 3) should appear before level 2 vertex (4) + int indexOf4 = bfs.indexOf(4); + assertTrue(bfs.indexOf(1) < indexOf4); + assertTrue(bfs.indexOf(2) < indexOf4); + assertTrue(bfs.indexOf(3) < indexOf4); + } + + @Test + void testDfsStartFromDifferentVertices() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + + // DFS from vertex 0 + List dfs0 = graph.depthFirstOrder(0); + assertEquals(4, dfs0.size()); + assertEquals(0, dfs0.get(0)); + + // DFS from vertex 2 + List dfs2 = graph.depthFirstOrder(2); + assertEquals(4, dfs2.size()); + assertEquals(2, dfs2.get(0)); + + // DFS from vertex 3 + List dfs3 = graph.depthFirstOrder(3); + assertEquals(4, dfs3.size()); + assertEquals(3, dfs3.get(0)); + } + + @Test + void testBfsStartFromDifferentVertices() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + + // BFS from vertex 0 + List bfs0 = graph.breadthFirstOrder(0); + assertEquals(4, bfs0.size()); + assertEquals(0, bfs0.get(0)); + + // BFS from vertex 2 + List bfs2 = graph.breadthFirstOrder(2); + assertEquals(4, bfs2.size()); + assertEquals(2, bfs2.get(0)); + } + + @Test + void testStarTopologyBfs() { + // Star graph: 0 is center connected to 1, 2, 3, 4 + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(0, 3); + graph.addEdge(0, 4); + + List bfs = graph.breadthFirstOrder(0); + assertEquals(5, bfs.size()); + assertEquals(0, bfs.get(0)); + // All neighbors should be at distance 1 + assertTrue(bfs.containsAll(Arrays.asList(1, 2, 3, 4))); + } + + @Test + void testStarTopologyDfs() { + // Star graph: 0 is center connected to 1, 2, 3, 4 + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(0, 3); + graph.addEdge(0, 4); + + List dfs = graph.depthFirstOrder(0); + assertEquals(5, dfs.size()); + assertEquals(0, dfs.get(0)); + assertTrue(dfs.containsAll(Arrays.asList(1, 2, 3, 4))); + } + + @Test + void testNegativeStartVertexDfs() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + + List dfs = graph.depthFirstOrder(-1); + assertTrue(dfs.isEmpty()); + } + + @Test + void testNegativeStartVertexBfs() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + + List bfs = graph.breadthFirstOrder(-1); + assertTrue(bfs.isEmpty()); + } + + @Test + void testCompleteGraphKFour() { + // Complete graph K4: every vertex connected to every other vertex + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(0, 3); + graph.addEdge(1, 2); + graph.addEdge(1, 3); + graph.addEdge(2, 3); + + assertEquals(6, graph.numberOfEdges()); + + List dfs = graph.depthFirstOrder(0); + List bfs = graph.breadthFirstOrder(0); + + assertEquals(4, dfs.size()); + assertEquals(4, bfs.size()); + assertTrue(dfs.containsAll(Arrays.asList(0, 1, 2, 3))); + assertTrue(bfs.containsAll(Arrays.asList(0, 1, 2, 3))); + } + + @Test + void testLargerGraphTraversal() { + // Create a larger graph with 10 vertices + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(10); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(1, 3); + graph.addEdge(1, 4); + graph.addEdge(2, 5); + graph.addEdge(2, 6); + graph.addEdge(3, 7); + graph.addEdge(4, 8); + graph.addEdge(5, 9); + + List dfs = graph.depthFirstOrder(0); + List bfs = graph.breadthFirstOrder(0); + + assertEquals(10, dfs.size()); + assertEquals(10, bfs.size()); + assertEquals(0, dfs.get(0)); + assertEquals(0, bfs.get(0)); + } + + @Test + void testSelfLoop() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(3); + graph.addEdge(0, 0); // Self loop + graph.addEdge(0, 1); + graph.addEdge(1, 2); + + List dfs = graph.depthFirstOrder(0); + List bfs = graph.breadthFirstOrder(0); + + assertEquals(3, dfs.size()); + assertEquals(3, bfs.size()); + } + + @Test + void testLinearGraphTraversal() { + // Linear graph: 0 - 1 - 2 - 3 - 4 + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 4); + + List dfs = graph.depthFirstOrder(0); + List bfs = graph.breadthFirstOrder(0); + + assertEquals(5, dfs.size()); + assertEquals(5, bfs.size()); + + // In a linear graph, BFS and DFS starting from 0 should be the same + assertEquals(Arrays.asList(0, 1, 2, 3, 4), dfs); + assertEquals(Arrays.asList(0, 1, 2, 3, 4), bfs); + } } From a8bc9cc5258f68ced843b827cbabb1b60e80f35d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:33:35 +0000 Subject: [PATCH 036/122] chore(deps-dev): bump org.mockito:mockito-core from 5.20.0 to 5.21.0 (#7154) Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.20.0 to 5.21.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.20.0...v5.21.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-version: 5.21.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Deniz Altunkapan --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4baf7a5f99b0..185249a0ea0f 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ org.mockito mockito-core - 5.20.0 + 5.21.0 test From 746457ce152909a76613cdd7ba69b0e7acd72e5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:29:17 +0100 Subject: [PATCH 037/122] chore(deps): bump actions/cache from 4 to 5 in /.github/workflows (#7162) Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/infer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index c85b1d225f72..9d4dcf63000b 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -33,7 +33,7 @@ jobs: - name: Cache infer build id: cache-infer - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: infer key: ${{ runner.os }}-infer-${{ env.year_week }} From bccaf970baf8f7caa7ae08c0088e224625d8d5f0 Mon Sep 17 00:00:00 2001 From: utkarsh patel <153971829+Utkarsh-patel26@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:38:18 +0530 Subject: [PATCH 038/122] Add Z-Algorithm for Linear-Time String Pattern Matching (#7124) * Add Z-Algorithm (string pattern matching) with tests * Add Z-Algorithm (string pattern matching) with tests * Add Z-Algorithm (string pattern matching) with tests * Fix checkstyle errors for ZAlgorithm * Fix: clang-format and checkstyle compliance for ZAlgorithm --- DIRECTORY.md | 2 + .../com/thealgorithms/strings/ZAlgorithm.java | 48 +++++++++++++++++++ .../thealgorithms/strings/ZAlgorithmTest.java | 25 ++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/ZAlgorithm.java create mode 100644 src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java diff --git a/DIRECTORY.md b/DIRECTORY.md index b8db96ffa993..6ea83fc6a60a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -822,6 +822,7 @@ - πŸ“„ [Upper](src/main/java/com/thealgorithms/strings/Upper.java) - πŸ“„ [ValidParentheses](src/main/java/com/thealgorithms/strings/ValidParentheses.java) - πŸ“„ [WordLadder](src/main/java/com/thealgorithms/strings/WordLadder.java) + - πŸ“„ [ZAlgorithm](src/main/java/com/thealgorithms/strings/ZAlgorithm.java) - πŸ“ **zigZagPattern** - πŸ“„ [ZigZagPattern](src/main/java/com/thealgorithms/strings/zigZagPattern/ZigZagPattern.java) - πŸ“ **tree** @@ -1579,6 +1580,7 @@ - πŸ“„ [UpperTest](src/test/java/com/thealgorithms/strings/UpperTest.java) - πŸ“„ [ValidParenthesesTest](src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java) - πŸ“„ [WordLadderTest](src/test/java/com/thealgorithms/strings/WordLadderTest.java) + - πŸ“„ [ZAlgorithmTest](src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java) - πŸ“ **zigZagPattern** - πŸ“„ [ZigZagPatternTest](src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java) - πŸ“ **tree** diff --git a/src/main/java/com/thealgorithms/strings/ZAlgorithm.java b/src/main/java/com/thealgorithms/strings/ZAlgorithm.java new file mode 100644 index 000000000000..dc029b751f45 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/ZAlgorithm.java @@ -0,0 +1,48 @@ +/* + * https://en.wikipedia.org/wiki/Z-algorithm + */ +package com.thealgorithms.strings; + +public final class ZAlgorithm { + + private ZAlgorithm() { + throw new UnsupportedOperationException("Utility class"); + } + + public static int[] zFunction(String s) { + int n = s.length(); + int[] z = new int[n]; + int l = 0; + int r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + + while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) { + z[i]++; + } + + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + return z; + } + + public static int search(String text, String pattern) { + String s = pattern + "$" + text; + int[] z = zFunction(s); + int p = pattern.length(); + + for (int i = 0; i < z.length; i++) { + if (z[i] == p) { + return i - p - 1; + } + } + return -1; + } +} diff --git a/src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java b/src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java new file mode 100644 index 000000000000..df749ed9a8b5 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java @@ -0,0 +1,25 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ZAlgorithmTest { + + @Test + void testZFunction() { + int[] z = ZAlgorithm.zFunction("aaaaa"); + assertArrayEquals(new int[] {0, 4, 3, 2, 1}, z); + } + + @Test + void testSearchFound() { + assertEquals(2, ZAlgorithm.search("abcabca", "cab")); + } + + @Test + void testSearchNotFound() { + assertEquals(-1, ZAlgorithm.search("abcdef", "gh")); + } +} From aa914512030269fd30173cd3892804bac203aded Mon Sep 17 00:00:00 2001 From: Muhammad Junaid Khalid Date: Sun, 14 Dec 2025 15:14:00 +0500 Subject: [PATCH 039/122] feat: Binary Tree to String algorithm added (Leetcode 606) (#6727) * feat: BinaryTreeToString.java Added with tests * fix: javadoc added to BinaryTreeToString * fix: link to leetcode added --- .../trees/BinaryTreeToString.java | 100 ++++++++++++++++++ .../trees/BinaryTreeToStringTest.java | 57 ++++++++++ 2 files changed, 157 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java create mode 100644 src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java b/src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java new file mode 100644 index 000000000000..2f9b3b489d56 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java @@ -0,0 +1,100 @@ +package com.thealgorithms.datastructures.trees; + +/** + * Leetcode 606: Construct String from Binary Tree: + * https://leetcode.com/problems/construct-string-from-binary-tree/ + * + * Utility class to convert a {@link BinaryTree} into its string representation. + *

+ * The conversion follows a preorder traversal pattern (root β†’ left β†’ right) + * and uses parentheses to denote the tree structure. + * Empty parentheses "()" are used to explicitly represent missing left children + * when a right child exists, ensuring the structure is unambiguous. + *

+ * + *

Rules:

+ *
    + *
  • Each node is represented as {@code (value)}.
  • + *
  • If a node has only a right child, include {@code ()} before the right + * child + * to indicate the missing left child.
  • + *
  • If a node has no children, it appears as just {@code (value)}.
  • + *
  • The outermost parentheses are removed from the final string.
  • + *
+ * + *

Example:

+ * + *
+ *     Input tree:
+ *           1
+ *          / \
+ *         2   3
+ *          \
+ *           4
+ *
+ *     Output string:
+ *     "1(2()(4))(3)"
+ * 
+ * + *

+ * This implementation matches the logic from LeetCode problem 606: + * Construct String from Binary Tree. + *

+ * + * @author Muhammad Junaid + * @see BinaryTree + */ +public class BinaryTreeToString { + + /** String builder used to accumulate the string representation. */ + private StringBuilder sb; + + /** + * Converts a binary tree (given its root node) to its string representation. + * + * @param root the root node of the binary tree + * @return the string representation of the binary tree, or an empty string if + * the tree is null + */ + public String tree2str(BinaryTree.Node root) { + if (root == null) { + return ""; + } + + sb = new StringBuilder(); + dfs(root); + + // Remove the leading and trailing parentheses added by the root call + return sb.substring(1, sb.length() - 1); + } + + /** + * Performs a recursive depth-first traversal to build the string. + * Each recursive call appends the node value and its children (if any) + * enclosed in parentheses. + * + * @param node the current node being processed + */ + private void dfs(BinaryTree.Node node) { + if (node == null) { + return; + } + + sb.append("(").append(node.data); + + // Recursively build left and right subtrees + if (node.left != null) { + dfs(node.left); + } + + // Handle the special case: right child exists but left child is null + if (node.right != null && node.left == null) { + sb.append("()"); + dfs(node.right); + } else if (node.right != null) { + dfs(node.right); + } + + sb.append(")"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java new file mode 100644 index 000000000000..2461fd74143d --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.datastructures.trees; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Tests for the BinaryTreeToString class. + */ +public class BinaryTreeToStringTest { + + @Test + public void testTreeToStringBasic() { + BinaryTree tree = new BinaryTree(); + tree.put(1); + tree.put(2); + tree.put(3); + tree.put(4); + + BinaryTreeToString converter = new BinaryTreeToString(); + String result = converter.tree2str(tree.getRoot()); + + // Output will depend on insertion logic of BinaryTree.put() + // which is BST-style, so result = "1()(2()(3()(4)))" + Assertions.assertEquals("1()(2()(3()(4)))", result); + } + + @Test + public void testSingleNodeTree() { + BinaryTree tree = new BinaryTree(); + tree.put(10); + + BinaryTreeToString converter = new BinaryTreeToString(); + String result = converter.tree2str(tree.getRoot()); + + Assertions.assertEquals("10", result); + } + + @Test + public void testComplexTreeStructure() { + BinaryTree.Node root = new BinaryTree.Node(10); + root.left = new BinaryTree.Node(5); + root.right = new BinaryTree.Node(20); + root.right.left = new BinaryTree.Node(15); + root.right.right = new BinaryTree.Node(25); + + BinaryTreeToString converter = new BinaryTreeToString(); + String result = converter.tree2str(root); + + Assertions.assertEquals("10(5)(20(15)(25))", result); + } + + @Test + public void testNullTree() { + BinaryTreeToString converter = new BinaryTreeToString(); + Assertions.assertEquals("", converter.tree2str(null)); + } +} From d9f2ac8340946f4c677a3ea1a5b9128a533086e0 Mon Sep 17 00:00:00 2001 From: Taranjeet Singh Kalsi Date: Sun, 14 Dec 2025 15:59:14 +0530 Subject: [PATCH 040/122] Added program to check Lucky number (#6962) added program for lucky number --- .../com/thealgorithms/maths/LuckyNumber.java | 78 +++++++++++++++++++ .../thealgorithms/maths/LuckyNumberTest.java | 32 ++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/LuckyNumber.java create mode 100644 src/test/java/com/thealgorithms/maths/LuckyNumberTest.java diff --git a/src/main/java/com/thealgorithms/maths/LuckyNumber.java b/src/main/java/com/thealgorithms/maths/LuckyNumber.java new file mode 100644 index 000000000000..70308e1e0edd --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/LuckyNumber.java @@ -0,0 +1,78 @@ +package com.thealgorithms.maths; + +/** + * In number theory, a lucky number is a natural number in a set which is generated by a certain "sieve". + * This sieve is similar to the sieve of Eratosthenes that generates the primes, + * but it eliminates numbers based on their position in the remaining set, + * instead of their value (or position in the initial set of natural numbers). + * + * Wiki: https://en.wikipedia.org/wiki/Lucky_number + */ +public final class LuckyNumber { + + private LuckyNumber() { + } + + // Common validation method + private static void validatePositiveNumber(int number) { + if (number <= 0) { + throw new IllegalArgumentException("Number must be positive."); + } + } + + // Function to check recursively for Lucky Number + private static boolean isLuckyRecursiveApproach(int n, int counter) { + // Base case: If counter exceeds n, number is lucky + if (counter > n) { + return true; + } + + // If number is eliminated in this step, it's not lucky + if (n % counter == 0) { + return false; + } + + // Calculate new position after removing every counter-th number + int newNumber = n - (n / counter); + + // Recursive call for next round + return isLuckyRecursiveApproach(newNumber, counter + 1); + } + + /** + * Check if {@code number} is a Lucky number or not using recursive approach + * + * @param number the number + * @return {@code true} if {@code number} is a Lucky number, otherwise false + */ + public static boolean isLuckyNumber(int number) { + validatePositiveNumber(number); + int counterStarting = 2; + return isLuckyRecursiveApproach(number, counterStarting); + } + + /** + * Check if {@code number} is a Lucky number or not using iterative approach + * + * @param number the number + * @return {@code true} if {@code number} is a Lucky number, otherwise false + */ + public static boolean isLucky(int number) { + validatePositiveNumber(number); + + int counter = 2; // Position starts from 2 (since first elimination happens at 2) + int position = number; // The position of the number in the sequence + + while (counter <= position) { + if (position % counter == 0) { + return false; + } // Number is eliminated + + // Update the position of n after removing every counter-th number + position = position - (position / counter); + counter++; + } + + return true; // Survives all eliminations β†’ Lucky Number + } +} diff --git a/src/test/java/com/thealgorithms/maths/LuckyNumberTest.java b/src/test/java/com/thealgorithms/maths/LuckyNumberTest.java new file mode 100644 index 000000000000..91904316b25c --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/LuckyNumberTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class LuckyNumberTest { + + @ParameterizedTest + @CsvSource({"1", "3", "13", "49", "109", "459", "949"}) + void luckyNumbersTest(int n) { + assertTrue(LuckyNumber.isLucky(n)); + assertTrue(LuckyNumber.isLuckyNumber(n)); + } + + @ParameterizedTest + @CsvSource({"2", "17", "100", "300", "700"}) + void nonLuckyNumbersTest(int n) { + assertFalse(LuckyNumber.isLucky(n)); + assertFalse(LuckyNumber.isLuckyNumber(n)); + } + + @ParameterizedTest + @CsvSource({"0", "-1"}) + void throwsNegativeNumbersNotAllowed(int n) { + assertThrows(IllegalArgumentException.class, () -> LuckyNumber.isLucky(n)); + assertThrows(IllegalArgumentException.class, () -> LuckyNumber.isLuckyNumber(n)); + } +} From b76962d5ad9b66e13c757b65bf0a8d9843c52505 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:53:29 +0100 Subject: [PATCH 041/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 12.2.0 to 12.3.0 (#7167) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 12.2.0 to 12.3.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-12.2.0...checkstyle-12.3.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 12.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 185249a0ea0f..d60f8b9668e6 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 12.2.0 + 12.3.0 From 4f293b33e553af095305de23d72cf29c416a0c9f Mon Sep 17 00:00:00 2001 From: NYAMATHABAD KOUSHIK SAI <120590127+koushik-sai@users.noreply.github.com> Date: Wed, 17 Dec 2025 21:18:00 +0530 Subject: [PATCH 042/122] [FEAT] Add Thin Lens formula for ray optics (#7170) * [FEAT] Add Thin Lens formula for ray optics * [CI] Re-run clang-format checks * [CI] Re-run checks after rebase * [STYLE] Apply clang-format to ThinLensTest --------- Co-authored-by: Koushik Sai --- .../com/thealgorithms/physics/ThinLens.java | 74 +++++++++++++++++++ .../thealgorithms/physics/ThinLensTest.java | 19 +++++ 2 files changed, 93 insertions(+) create mode 100644 src/main/java/com/thealgorithms/physics/ThinLens.java create mode 100644 src/test/java/com/thealgorithms/physics/ThinLensTest.java diff --git a/src/main/java/com/thealgorithms/physics/ThinLens.java b/src/main/java/com/thealgorithms/physics/ThinLens.java new file mode 100644 index 000000000000..5fb29d8c41e4 --- /dev/null +++ b/src/main/java/com/thealgorithms/physics/ThinLens.java @@ -0,0 +1,74 @@ +package com.thealgorithms.physics; + +/** + * Implements the Thin Lens Formula used in ray optics: + * + *
+ *     1/f = 1/v + 1/u
+ * 
+ * + * where: + *
    + *
  • f = focal length
  • + *
  • u = object distance
  • + *
  • v = image distance
  • + *
+ * + * Uses the Cartesian sign convention. + * + * @see Thin Lens + */ +public final class ThinLens { + + private ThinLens() { + throw new AssertionError("No instances."); + } + + /** + * Computes the image distance using the thin lens formula. + * + * @param focalLength focal length of the lens (f) + * @param objectDistance object distance (u) + * @return image distance (v) + * @throws IllegalArgumentException if focal length or object distance is zero + */ + public static double imageDistance(double focalLength, double objectDistance) { + + if (focalLength == 0 || objectDistance == 0) { + throw new IllegalArgumentException("Focal length and object distance must be non-zero."); + } + + return 1.0 / ((1.0 / focalLength) - (1.0 / objectDistance)); + } + + /** + * Computes magnification of the image. + * + *
+     *     m = v / u
+     * 
+ * + * @param imageDistance image distance (v) + * @param objectDistance object distance (u) + * @return magnification + * @throws IllegalArgumentException if object distance is zero + */ + public static double magnification(double imageDistance, double objectDistance) { + + if (objectDistance == 0) { + throw new IllegalArgumentException("Object distance must be non-zero."); + } + + return imageDistance / objectDistance; + } + + /** + * Determines whether the image formed is real or virtual. + * + * @param imageDistance image distance (v) + * @return {@code true} if image is real, {@code false} if virtual + */ + public static boolean isRealImage(double imageDistance) { + return imageDistance > 0; + } +} diff --git a/src/test/java/com/thealgorithms/physics/ThinLensTest.java b/src/test/java/com/thealgorithms/physics/ThinLensTest.java new file mode 100644 index 000000000000..cf7e94676819 --- /dev/null +++ b/src/test/java/com/thealgorithms/physics/ThinLensTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.physics; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ThinLensTest { + + @Test + void testConvexLensRealImage() { + double v = ThinLens.imageDistance(10, 20); + assertEquals(20, v, 1e-6); + } + + @Test + void testMagnification() { + assertEquals(2.0, ThinLens.magnification(20, 10), 1e-6); + } +} From 2911a7b52c1032591d2aa23feef5669fa43c7961 Mon Sep 17 00:00:00 2001 From: Rajuri Likhitha Date: Thu, 18 Dec 2025 02:44:12 +0530 Subject: [PATCH 043/122] ExtendedEuclideanAlgorithm (#7172) * commit1 * Fix clang-format spacing in array declarations --- .../maths/ExtendedEuclideanAlgorithm.java | 48 +++++++++++++++++++ .../maths/ExtendedEuclideanAlgorithmTest.java | 47 ++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithm.java create mode 100644 src/test/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithmTest.java diff --git a/src/main/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithm.java b/src/main/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithm.java new file mode 100644 index 000000000000..4934d4493bf2 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithm.java @@ -0,0 +1,48 @@ +package com.thealgorithms.maths; + +/** + * In mathematics, the extended Euclidean algorithm is an extension to the + * Euclidean algorithm, and computes, in addition to the greatest common divisor + * (gcd) of integers a and b, also the coefficients of BΓ©zout's identity, which + * are integers x and y such that ax + by = gcd(a, b). + * + *

+ * For more details, see + * https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm + */ +public final class ExtendedEuclideanAlgorithm { + + private ExtendedEuclideanAlgorithm() { + } + + /** + * This method implements the extended Euclidean algorithm. + * + * @param a The first number. + * @param b The second number. + * @return An array of three integers: + *

    + *
  • Index 0: The greatest common divisor (gcd) of a and b.
  • + *
  • Index 1: The value of x in the equation ax + by = gcd(a, b).
  • + *
  • Index 2: The value of y in the equation ax + by = gcd(a, b).
  • + *
+ */ + public static long[] extendedGCD(long a, long b) { + if (b == 0) { + // Base case: gcd(a, 0) = a. The equation is a*1 + 0*0 = a. + return new long[] {a, 1, 0}; + } + + // Recursive call + long[] result = extendedGCD(b, a % b); + long gcd = result[0]; + long x1 = result[1]; + long y1 = result[2]; + + // Update coefficients using the results from the recursive call + long x = y1; + long y = x1 - a / b * y1; + + return new long[] {gcd, x, y}; + } +} diff --git a/src/test/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithmTest.java b/src/test/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithmTest.java new file mode 100644 index 000000000000..56c005fd51ae --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithmTest.java @@ -0,0 +1,47 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ExtendedEuclideanAlgorithmTest { + + /** + * Verifies that the returned values satisfy BΓ©zout's identity: a*x + b*y = + * gcd(a, b) + */ + private void verifyBezoutIdentity(long a, long b, long[] result) { + long gcd = result[0]; + long x = result[1]; + long y = result[2]; + assertEquals(a * x + b * y, gcd, "BΓ©zout's identity failed for gcd(" + a + ", " + b + ")"); + } + + @Test + public void testExtendedGCD() { + // Test case 1: General case gcd(30, 50) = 10 + long[] result1 = ExtendedEuclideanAlgorithm.extendedGCD(30, 50); + assertEquals(10, result1[0], "Test Case 1 Failed: gcd(30, 50) should be 10"); + verifyBezoutIdentity(30, 50, result1); + + // Test case 2: Another general case gcd(240, 46) = 2 + long[] result2 = ExtendedEuclideanAlgorithm.extendedGCD(240, 46); + assertEquals(2, result2[0], "Test Case 2 Failed: gcd(240, 46) should be 2"); + verifyBezoutIdentity(240, 46, result2); + + // Test case 3: Base case where b is 0, gcd(10, 0) = 10 + long[] result3 = ExtendedEuclideanAlgorithm.extendedGCD(10, 0); + assertEquals(10, result3[0], "Test Case 3 Failed: gcd(10, 0) should be 10"); + verifyBezoutIdentity(10, 0, result3); + + // Test case 4: Numbers are co-prime gcd(17, 13) = 1 + long[] result4 = ExtendedEuclideanAlgorithm.extendedGCD(17, 13); + assertEquals(1, result4[0], "Test Case 4 Failed: gcd(17, 13) should be 1"); + verifyBezoutIdentity(17, 13, result4); + + // Test case 5: One number is a multiple of the other gcd(100, 20) = 20 + long[] result5 = ExtendedEuclideanAlgorithm.extendedGCD(100, 20); + assertEquals(20, result5[0], "Test Case 5 Failed: gcd(100, 20) should be 20"); + verifyBezoutIdentity(100, 20, result5); + } +} From 7d51c7f32097b488bab330f858391387ce4ce72c Mon Sep 17 00:00:00 2001 From: Saniya Mane <185913688+Saniya2701@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:25:03 +0530 Subject: [PATCH 044/122] Add additional edge cases to GCD unit tests (#7174) * Add additional edge cases to GCD unit tests * Rename GCD test methods and add descriptive names * Fix formatting in GCD tests --------- Co-authored-by: Deniz Altunkapan --- .../java/com/thealgorithms/maths/GCDTest.java | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/test/java/com/thealgorithms/maths/GCDTest.java b/src/test/java/com/thealgorithms/maths/GCDTest.java index bac3f8f7596c..6bc870e94df9 100644 --- a/src/test/java/com/thealgorithms/maths/GCDTest.java +++ b/src/test/java/com/thealgorithms/maths/GCDTest.java @@ -6,57 +6,77 @@ public class GCDTest { @Test - void test1() { + void testNegativeAndZeroThrowsException() { Assertions.assertThrows(ArithmeticException.class, () -> GCD.gcd(-1, 0)); } @Test - void test2() { + void testPositiveAndNegativeThrowsException() { Assertions.assertThrows(ArithmeticException.class, () -> GCD.gcd(10, -2)); } @Test - void test3() { + void testBothNegativeThrowsException() { Assertions.assertThrows(ArithmeticException.class, () -> GCD.gcd(-5, -3)); } @Test - void test4() { - Assertions.assertEquals(GCD.gcd(0, 2), 2); + void testZeroAndPositiveReturnsPositive() { + Assertions.assertEquals(2, GCD.gcd(0, 2)); } @Test - void test5() { - Assertions.assertEquals(GCD.gcd(10, 0), 10); + void testPositiveAndZeroReturnsPositive() { + Assertions.assertEquals(10, GCD.gcd(10, 0)); } @Test - void test6() { - Assertions.assertEquals(GCD.gcd(1, 0), 1); + void testOneAndZeroReturnsOne() { + Assertions.assertEquals(1, GCD.gcd(1, 0)); } @Test - void test7() { - Assertions.assertEquals(GCD.gcd(9, 6), 3); + void testTwoPositiveNumbers() { + Assertions.assertEquals(3, GCD.gcd(9, 6)); } @Test - void test8() { - Assertions.assertEquals(GCD.gcd(48, 18, 30, 12), 6); + void testMultipleArgumentsGcd() { + Assertions.assertEquals(6, GCD.gcd(48, 18, 30, 12)); } @Test - void testArrayGcd1() { - Assertions.assertEquals(GCD.gcd(new int[] {9, 6}), 3); + void testArrayInputGcd() { + Assertions.assertEquals(3, GCD.gcd(new int[] {9, 6})); } @Test - void testArrayGcd2() { - Assertions.assertEquals(GCD.gcd(new int[] {2 * 3 * 5 * 7, 2 * 5 * 5 * 5, 2 * 5 * 11, 5 * 5 * 5 * 13}), 5); + void testArrayWithCommonFactor() { + Assertions.assertEquals(5, GCD.gcd(new int[] {2 * 3 * 5 * 7, 2 * 5 * 5 * 5, 2 * 5 * 11, 5 * 5 * 5 * 13})); } @Test - void testArrayGcdForEmptyInput() { - Assertions.assertEquals(GCD.gcd(new int[] {}), 0); + void testEmptyArrayReturnsZero() { + Assertions.assertEquals(0, GCD.gcd(new int[] {})); + } + + @Test + void testSameNumbers() { + Assertions.assertEquals(7, GCD.gcd(7, 7)); + } + + @Test + void testPrimeNumbersHaveGcdOne() { + Assertions.assertEquals(1, GCD.gcd(13, 17)); + } + + @Test + void testSingleElementArrayReturnsElement() { + Assertions.assertEquals(42, GCD.gcd(new int[] {42})); + } + + @Test + void testLargeNumbers() { + Assertions.assertEquals(12, GCD.gcd(123456, 789012)); } } From 48e02b3bacc80600297812733c3ed483d265531c Mon Sep 17 00:00:00 2001 From: Adityaraj Solanki Date: Thu, 18 Dec 2025 21:39:55 +0530 Subject: [PATCH 045/122] Add input validation for negative values in FibonacciSeries (#7177) --- .../java/com/thealgorithms/recursion/FibonacciSeries.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java index e5f474085367..9bc6da2f7443 100644 --- a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java +++ b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java @@ -12,10 +12,12 @@ private FibonacciSeries() { throw new UnsupportedOperationException("Utility class"); } public static int fibonacci(int n) { + if (n < 0) { + throw new IllegalArgumentException("n must be a non-negative integer"); + } if (n <= 1) { return n; - } else { - return fibonacci(n - 1) + fibonacci(n - 2); } + return fibonacci(n - 1) + fibonacci(n - 2); } } From bb334881e938c8728d0add1f08d6b9d9f4bd8970 Mon Sep 17 00:00:00 2001 From: Gopal Gupta <143406016+gopaljilab@users.noreply.github.com> Date: Sat, 20 Dec 2025 19:12:34 +0530 Subject: [PATCH 046/122] Implement LengthOfLastWord algorithm and add JUnit tests (#7057) * Implement LengthOfLastWord algorithm in strings package * Add JUnit tests for LengthOfLastWord algorithm * style: fix import order and spacing for clang-format --- .../strings/LengthOfLastWord.java | 51 +++++++++++++++++++ .../strings/LengthOfLastWordTest.java | 18 +++++++ 2 files changed, 69 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/LengthOfLastWord.java create mode 100644 src/test/java/com/thealgorithms/strings/LengthOfLastWordTest.java diff --git a/src/main/java/com/thealgorithms/strings/LengthOfLastWord.java b/src/main/java/com/thealgorithms/strings/LengthOfLastWord.java new file mode 100644 index 000000000000..7eed59a5ef99 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LengthOfLastWord.java @@ -0,0 +1,51 @@ +package com.thealgorithms.strings; + +/** + * The {@code LengthOfLastWord} class provides a utility method to determine + * the length of the last word in a given string. + * + *

A "word" is defined as a maximal substring consisting of non-space + * characters only. Trailing spaces at the end of the string are ignored. + * + *

Example: + *

{@code
+ * LengthOfLastWord obj = new LengthOfLastWord();
+ * System.out.println(obj.lengthOfLastWord("Hello World"));  // Output: 5
+ * System.out.println(obj.lengthOfLastWord("  fly me   to   the moon  "));  // Output: 4
+ * System.out.println(obj.lengthOfLastWord("luffy is still joyboy"));  // Output: 6
+ * }
+ * + *

This implementation runs in O(n) time complexity, where n is the length + * of the input string, and uses O(1) additional space. + */ +public class LengthOfLastWord { + + /** + * Returns the length of the last word in the specified string. + * + *

The method iterates from the end of the string, skipping trailing + * spaces first, and then counts the number of consecutive non-space characters + * characters until another space (or the beginning of the string) is reached. + * + * @param s the input string to analyze + * @return the length of the last word in {@code s}; returns 0 if there is no word + * @throws NullPointerException if {@code s} is {@code null} + */ + public int lengthOfLastWord(String s) { + int sizeOfString = s.length() - 1; + int lastWordLength = 0; + + // Skip trailing spaces from the end of the string + while (sizeOfString >= 0 && s.charAt(sizeOfString) == ' ') { + sizeOfString--; + } + + // Count the characters of the last word + while (sizeOfString >= 0 && s.charAt(sizeOfString) != ' ') { + lastWordLength++; + sizeOfString--; + } + + return lastWordLength; + } +} diff --git a/src/test/java/com/thealgorithms/strings/LengthOfLastWordTest.java b/src/test/java/com/thealgorithms/strings/LengthOfLastWordTest.java new file mode 100644 index 000000000000..46a0a6eb0008 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LengthOfLastWordTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LengthOfLastWordTest { + @Test + public void testLengthOfLastWord() { + assertEquals(5, new LengthOfLastWord().lengthOfLastWord("Hello World")); + assertEquals(4, new LengthOfLastWord().lengthOfLastWord(" fly me to the moon ")); + assertEquals(6, new LengthOfLastWord().lengthOfLastWord("luffy is still joyboy")); + assertEquals(5, new LengthOfLastWord().lengthOfLastWord("Hello")); + assertEquals(0, new LengthOfLastWord().lengthOfLastWord(" ")); + assertEquals(0, new LengthOfLastWord().lengthOfLastWord("")); + assertEquals(3, new LengthOfLastWord().lengthOfLastWord("JUST LIE ")); + } +} From 1eb0d61730e033bbc37bab67f795f9fa3a8fc2b1 Mon Sep 17 00:00:00 2001 From: HxCodes Date: Sun, 21 Dec 2025 21:03:25 +0530 Subject: [PATCH 047/122] Improve documentation and inline comments for QuickSort algorithm (#7182) * Improve QuickSort documentation with detailed comments and explanations * Format QuickSort documentation and fix JavaDoc layout --- .../com/thealgorithms/sorts/QuickSort.java | 67 +++++++++++++------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/QuickSort.java b/src/main/java/com/thealgorithms/sorts/QuickSort.java index a025e6909259..b0ca8b9f159d 100644 --- a/src/main/java/com/thealgorithms/sorts/QuickSort.java +++ b/src/main/java/com/thealgorithms/sorts/QuickSort.java @@ -1,24 +1,36 @@ package com.thealgorithms.sorts; /** - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) + * QuickSort is a divide-and-conquer sorting algorithm. + * + *

The algorithm selects a pivot element and partitions the array into two + * subarrays such that: + *

    + *
  • Elements smaller than the pivot are placed on the left
  • + *
  • Elements greater than the pivot are placed on the right
  • + *
+ * + *

The subarrays are then recursively sorted until the entire array is ordered. + * + *

This implementation uses randomization to reduce the probability of + * encountering worst-case performance on already sorted inputs. + * + *

Time Complexity: + *

    + *
  • Best Case: O(n log n)
  • + *
  • Average Case: O(n log n)
  • + *
  • Worst Case: O(n^2)
  • + *
+ * + *

Space Complexity: O(log n) due to recursion stack (in-place sorting). + * + * @author Varun Upadhyay + * @author Podshivalov Nikita * @see SortAlgorithm */ + class QuickSort implements SortAlgorithm { - /** - * Generic Quick Sort algorithm. - * - * Time Complexity: - * - Best case: O(n log n) – pivot splits array roughly in half each time. - * - Average case: O(n log n) - * - Worst case: O(n^2) – occurs when pivot consistently produces unbalanced splits. - * - * Space Complexity: O(log n) – recursion stack, in-place sorting. - * - * @see SortAlgorithm - */ @Override public > T[] sort(T[] array) { doSort(array, 0, array.length - 1); @@ -28,27 +40,33 @@ public > T[] sort(T[] array) { /** * The sorting process * - * @param left The first index of an array - * @param right The last index of an array * @param array The array to be sorted + * @param left The first index of an array + * @param right The last index of an array */ private static > void doSort(T[] array, final int left, final int right) { + // Continue sorting only if the subarray has more than one element if (left < right) { + // Randomly choose a pivot and partition the array final int pivot = randomPartition(array, left, right); + // Recursively sort elements before the pivot doSort(array, left, pivot - 1); + // Recursively sort elements after the pivot doSort(array, pivot, right); } } /** - * Randomize the array to avoid the basically ordered sequences + * Randomizes the array to avoid already ordered or nearly ordered sequences * * @param array The array to be sorted - * @param left The first index of an array + * @param left The first index of an array * @param right The last index of an array * @return the partition index of the array */ private static > int randomPartition(T[] array, final int left, final int right) { + // Randomizing the pivot helps avoid worst-case performance + // for already sorted or nearly sorted arrays final int randomIndex = left + (int) (Math.random() * (right - left + 1)); SortUtils.swap(array, randomIndex, right); return partition(array, left, right); @@ -58,21 +76,26 @@ private static > int randomPartition(T[] array, final in * This method finds the partition index for an array * * @param array The array to be sorted - * @param left The first index of an array - * @param right The last index of an array Finds the partition index of an - * array + * @param left The first index of an array + * @param right The last index of an array */ private static > int partition(T[] array, int left, int right) { final int mid = (left + right) >>> 1; + // Choose the middle element as the pivot final T pivot = array[mid]; - + // Move the left and right pointers towards each other while (left <= right) { + // Move left pointer until an element >= pivot is found while (SortUtils.less(array[left], pivot)) { ++left; } + + // Move right pointer until an element <= pivot is found while (SortUtils.less(pivot, array[right])) { --right; } + + // Swap elements that are on the wrong side of the pivot if (left <= right) { SortUtils.swap(array, left, right); ++left; From 2707da25e35f6973ceacdf4aa7dde9ecf7d4d34c Mon Sep 17 00:00:00 2001 From: NYAMATHABAD KOUSHIK SAI <120590127+koushik-sai@users.noreply.github.com> Date: Tue, 23 Dec 2025 01:45:50 +0530 Subject: [PATCH 048/122] Add recursive factorial implementation with tests (#7178) * Add recursive factorial implementation with tests * [STYLE] Apply clang-format * Move recursive factorial to recursion package and remove duplicate * Move recursive factorial to recursion package and remove duplicate * Apply clang-format to factorial recursion tests * Apply clang-format to factorial recursion tests --------- Co-authored-by: Koushik Sai Co-authored-by: Deniz Altunkapan --- .../thealgorithms/{maths => recursion}/FactorialRecursion.java | 2 +- .../{maths => recursion}/FactorialRecursionTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/java/com/thealgorithms/{maths => recursion}/FactorialRecursion.java (92%) rename src/test/java/com/thealgorithms/{maths => recursion}/FactorialRecursionTest.java (96%) diff --git a/src/main/java/com/thealgorithms/maths/FactorialRecursion.java b/src/main/java/com/thealgorithms/recursion/FactorialRecursion.java similarity index 92% rename from src/main/java/com/thealgorithms/maths/FactorialRecursion.java rename to src/main/java/com/thealgorithms/recursion/FactorialRecursion.java index d9bafd1e39e9..673f216bdc9a 100644 --- a/src/main/java/com/thealgorithms/maths/FactorialRecursion.java +++ b/src/main/java/com/thealgorithms/recursion/FactorialRecursion.java @@ -1,4 +1,4 @@ -package com.thealgorithms.maths; +package com.thealgorithms.recursion; public final class FactorialRecursion { private FactorialRecursion() { diff --git a/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java b/src/test/java/com/thealgorithms/recursion/FactorialRecursionTest.java similarity index 96% rename from src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java rename to src/test/java/com/thealgorithms/recursion/FactorialRecursionTest.java index db18b46356b4..198fcd558f63 100644 --- a/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java +++ b/src/test/java/com/thealgorithms/recursion/FactorialRecursionTest.java @@ -1,4 +1,4 @@ -package com.thealgorithms.maths; +package com.thealgorithms.recursion; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; From 51335cc18adc988848bfec71fb291b39928e05dc Mon Sep 17 00:00:00 2001 From: NYAMATHABAD KOUSHIK SAI <120590127+koushik-sai@users.noreply.github.com> Date: Sat, 27 Dec 2025 22:25:23 +0530 Subject: [PATCH 049/122] Add Immutable HashMap implementation (#7183) * Add immutable HashMap implementation * Add immutable HashMap implementation * Applied clang-format --------- Co-authored-by: Koushik Sai --- .../hashmap/hashing/ImmutableHashMap.java | 115 ++++++++++++++++++ .../hashmap/hashing/ImmutableHashMapTest.java | 55 +++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMap.java create mode 100644 src/test/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMapTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMap.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMap.java new file mode 100644 index 000000000000..f6e09ec623b6 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMap.java @@ -0,0 +1,115 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +/** + * Immutable HashMap implementation using separate chaining. + * + *

This HashMap does not allow modification of existing instances. + * Any update operation returns a new ImmutableHashMap. + * + * @param key type + * @param value type + */ +public final class ImmutableHashMap { + + private static final int DEFAULT_CAPACITY = 16; + + private final Node[] table; + private final int size; + + /** + * Private constructor to enforce immutability. + */ + private ImmutableHashMap(Node[] table, int size) { + this.table = table; + this.size = size; + } + + /** + * Creates an empty ImmutableHashMap. + * + * @param key type + * @param value type + * @return empty ImmutableHashMap + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static ImmutableHashMap empty() { + Node[] table = (Node[]) new Node[DEFAULT_CAPACITY]; + return new ImmutableHashMap<>(table, 0); + } + + /** + * Returns a new ImmutableHashMap with the given key-value pair added. + * + * @param key key to add + * @param value value to associate + * @return new ImmutableHashMap instance + */ + public ImmutableHashMap put(K key, V value) { + Node[] newTable = table.clone(); + int index = hash(key); + + newTable[index] = new Node<>(key, value, newTable[index]); + return new ImmutableHashMap<>(newTable, size + 1); + } + + /** + * Retrieves the value associated with the given key. + * + * @param key key to search + * @return value if found, otherwise null + */ + public V get(K key) { + int index = hash(key); + Node current = table[index]; + + while (current != null) { + if ((key == null && current.key == null) || (key != null && key.equals(current.key))) { + return current.value; + } + current = current.next; + } + return null; + } + + /** + * Checks whether the given key exists in the map. + * + * @param key key to check + * @return true if key exists, false otherwise + */ + public boolean containsKey(K key) { + return get(key) != null; + } + + /** + * Returns the number of key-value pairs. + * + * @return size of the map + */ + public int size() { + return size; + } + + /** + * Computes hash index for a given key. + */ + private int hash(K key) { + return key == null ? 0 : (key.hashCode() & Integer.MAX_VALUE) % table.length; + } + + /** + * Node class for separate chaining. + */ + private static final class Node { + + private final K key; + private final V value; + private final Node next; + + private Node(K key, V value, Node next) { + this.key = key; + this.value = value; + this.next = next; + } + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMapTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMapTest.java new file mode 100644 index 000000000000..3d3fe63d775a --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMapTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ImmutableHashMapTest { + + @Test + void testEmptyMap() { + ImmutableHashMap map = ImmutableHashMap.empty(); + + assertEquals(0, map.size()); + assertNull(map.get("A")); + } + + @Test + void testPutDoesNotModifyOriginalMap() { + ImmutableHashMap map1 = ImmutableHashMap.empty(); + + ImmutableHashMap map2 = map1.put("A", 1); + + assertEquals(0, map1.size()); + assertEquals(1, map2.size()); + assertNull(map1.get("A")); + assertEquals(1, map2.get("A")); + } + + @Test + void testMultiplePuts() { + ImmutableHashMap map = ImmutableHashMap.empty().put("A", 1).put("B", 2); + + assertEquals(2, map.size()); + assertEquals(1, map.get("A")); + assertEquals(2, map.get("B")); + } + + @Test + void testContainsKey() { + ImmutableHashMap map = ImmutableHashMap.empty().put("X", 100); + + assertTrue(map.containsKey("X")); + assertFalse(map.containsKey("Y")); + } + + @Test + void testNullKey() { + ImmutableHashMap map = ImmutableHashMap.empty().put(null, 50); + + assertEquals(50, map.get(null)); + } +} From d382e656f20eaddc2d91c723519a2d8775e7dc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A1nchez=20N=C3=BA=C3=B1ez?= Date: Sun, 28 Dec 2025 10:53:12 +0100 Subject: [PATCH 050/122] Adding method to check wether a matrix is stochastic (#7184) * Add StochasticMatrix utility class This class provides methods to check if a matrix is row-stochastic or column-stochastic, ensuring all elements are non-negative and the sums of rows or columns equal 1. * Change package from maths to matrix Updated package declaration for StochasticMatrix class. * Change package declaration in StochasticMatrixTest * Delete src/test/java/com/thealgorithms/StochasticMatrixTest.java * Refactor matrix initialization for test cases * Fix formatting of matrix initialization in tests * Remove unnecessary newline in StochasticMatrix * Add import statement for JUnit in StochasticMatrixTest * Add additional assertions to StochasticMatrixTest * Clean up import statements in StochasticMatrixTest Removed unused import statements for cleaner code. * Reorder import statements in StochasticMatrixTest * Remove unused import assertThrows from StochasticMatrixTest Removed unused import for assertThrows. --- .../matrix/StochasticMatrix.java | 74 +++++++++++++++++++ .../matrix/StochasticMatrixTest.java | 28 +++++++ 2 files changed, 102 insertions(+) create mode 100644 src/main/java/com/thealgorithms/matrix/StochasticMatrix.java create mode 100644 src/test/java/com/thealgorithms/matrix/StochasticMatrixTest.java diff --git a/src/main/java/com/thealgorithms/matrix/StochasticMatrix.java b/src/main/java/com/thealgorithms/matrix/StochasticMatrix.java new file mode 100644 index 000000000000..8b071113f9cc --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/StochasticMatrix.java @@ -0,0 +1,74 @@ +package com.thealgorithms.matrix; + +/** + * Utility class to check whether a matrix is stochastic. + * A matrix is stochastic if all its elements are non-negative + * and the sum of each row or column is equal to 1. + *Reference: https://en.wikipedia.org/wiki/Stochastic_matrix + */ +public final class StochasticMatrix { + + private static final double TOLERANCE = 1e-9; + + private StochasticMatrix() { + // Utility class + } + /** + * Checks if a matrix is row-stochastic. + * + * @param matrix the matrix to check + * @return true if the matrix is row-stochastic + * @throws IllegalArgumentException if matrix is null or empty + */ + public static boolean isRowStochastic(double[][] matrix) { + validateMatrix(matrix); + + for (double[] row : matrix) { + double sum = 0.0; + for (double value : row) { + if (value < 0) { + return false; + } + sum += value; + } + if (Math.abs(sum - 1.0) > TOLERANCE) { + return false; + } + } + return true; + } + + /** + * Checks if a matrix is column-stochastic. + * + * @param matrix the matrix to check + * @return true if the matrix is column-stochastic + * @throws IllegalArgumentException if matrix is null or empty + */ + public static boolean isColumnStochastic(double[][] matrix) { + validateMatrix(matrix); + + int rows = matrix.length; + int cols = matrix[0].length; + + for (int j = 0; j < cols; j++) { + double sum = 0.0; + for (int i = 0; i < rows; i++) { + if (matrix[i][j] < 0) { + return false; + } + sum += matrix[i][j]; + } + if (Math.abs(sum - 1.0) > TOLERANCE) { + return false; + } + } + return true; + } + + private static void validateMatrix(double[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + throw new IllegalArgumentException("Matrix must not be null or empty"); + } + } +} diff --git a/src/test/java/com/thealgorithms/matrix/StochasticMatrixTest.java b/src/test/java/com/thealgorithms/matrix/StochasticMatrixTest.java new file mode 100644 index 000000000000..1bba918dadac --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/StochasticMatrixTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class StochasticMatrixTest { + + @Test + void testRowStochasticMatrix() { + double[][] matrix = {{0.2, 0.5, 0.3}, {0.1, 0.6, 0.3}}; + assertTrue(StochasticMatrix.isRowStochastic(matrix)); + assertFalse(StochasticMatrix.isColumnStochastic(matrix)); + } + + @Test + void testColumnStochasticMatrix() { + double[][] matrix = {{0.4, 0.2}, {0.6, 0.8}}; + assertTrue(StochasticMatrix.isColumnStochastic(matrix)); + } + + @Test + void testInvalidMatrix() { + double[][] matrix = {{0.5, -0.5}, {0.5, 1.5}}; + assertFalse(StochasticMatrix.isRowStochastic(matrix)); + } +} From 35556fe1f14d24f901d37bb7dcab6ad3faf75a91 Mon Sep 17 00:00:00 2001 From: Patil Arpita Date: Mon, 29 Dec 2025 18:57:31 +0530 Subject: [PATCH 051/122] Added 0/1 Knapsack Algorithm using Dynamic Programming in Java (#6789) * Added 0/1 Knapsack Algorithm using Dynamic Programming in Java * Fix Checkstyle whitespace issues in Knapsack.java * Formatted Knapsack.java to pass linter and added 0/1 Knapsack algorithm * Formatted Knapsack.java to pass linter and added 0/1 Knapsack algorithm * Formatted Knapsack.java to pass linter and added 0/1 Knapsack algorithm * Formatted Knapsack.java to pass linter and added 0/1 Knapsack algorithm --------- Co-authored-by: Deniz Altunkapan --- .../dynamicprogramming/Knapsack.java | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java b/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java index 134561766830..0d4c8d501f9f 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java @@ -3,53 +3,76 @@ import java.util.Arrays; /** - * A Dynamic Programming based solution for the 0-1 Knapsack problem. - * This class provides a method, `knapSack`, that calculates the maximum value that can be - * obtained from a given set of items with weights and values, while not exceeding a - * given weight capacity. + * 0/1 Knapsack Problem - Dynamic Programming solution. * - * @see 0-1 Knapsack Problem + * This algorithm solves the classic optimization problem where we have n items, + * each with a weight and a value. The goal is to maximize the total value + * without exceeding the knapsack's weight capacity. + * + * Time Complexity: O(n * W) + * Space Complexity: O(W) + * + * Example: + * values = {60, 100, 120} + * weights = {10, 20, 30} + * W = 50 + * Output: 220 + * + * @author Arpita + * @see Knapsack Problem */ public final class Knapsack { private Knapsack() { } + /** + * Validates the input to ensure correct constraints. + */ private static void throwIfInvalidInput(final int weightCapacity, final int[] weights, final int[] values) { if (weightCapacity < 0) { throw new IllegalArgumentException("Weight capacity should not be negative."); } if (weights == null || values == null || weights.length != values.length) { - throw new IllegalArgumentException("Input arrays must not be null and must have the same length."); + throw new IllegalArgumentException("Weights and values must be non-null and of the same length."); } if (Arrays.stream(weights).anyMatch(w -> w <= 0)) { - throw new IllegalArgumentException("Input array should not contain non-positive weight(s)."); + throw new IllegalArgumentException("Weights must be positive."); } } /** - * Solves the 0-1 Knapsack problem using Dynamic Programming. + * Solves the 0/1 Knapsack problem using Dynamic Programming (bottom-up approach). * * @param weightCapacity The maximum weight capacity of the knapsack. - * @param weights An array of item weights. - * @param values An array of item values. - * @return The maximum value that can be obtained without exceeding the weight capacity. - * @throws IllegalArgumentException If the input arrays are null or have different lengths. + * @param weights The array of item weights. + * @param values The array of item values. + * @return The maximum total value achievable without exceeding capacity. */ - public static int knapSack(final int weightCapacity, final int[] weights, final int[] values) throws IllegalArgumentException { + public static int knapSack(final int weightCapacity, final int[] weights, final int[] values) { throwIfInvalidInput(weightCapacity, weights, values); - // DP table to store the state of the maximum possible return for a given weight capacity. int[] dp = new int[weightCapacity + 1]; + // Fill dp[] array iteratively for (int i = 0; i < values.length; i++) { - for (int w = weightCapacity; w > 0; w--) { - if (weights[i] <= w) { - dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]); - } + for (int w = weightCapacity; w >= weights[i]; w--) { + dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]); } } return dp[weightCapacity]; } + + /* + // Example main method for local testing only. + public static void main(String[] args) { + int[] values = {60, 100, 120}; + int[] weights = {10, 20, 30}; + int weightCapacity = 50; + + int maxValue = knapSack(weightCapacity, weights, values); + System.out.println("Maximum value = " + maxValue); // Output: 220 + } + */ } From 6149ec7fed62c68d0ac6e0e91f9e9125d43bf385 Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Mon, 29 Dec 2025 16:06:23 +0100 Subject: [PATCH 052/122] Update DIRECTORY.md (#7093) Co-authored-by: DenizAltunkapan --- DIRECTORY.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 6ea83fc6a60a..deaf59636fa4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -80,6 +80,7 @@ - πŸ“„ [ECC](src/main/java/com/thealgorithms/ciphers/ECC.java) - πŸ“„ [HillCipher](src/main/java/com/thealgorithms/ciphers/HillCipher.java) - πŸ“„ [MonoAlphabetic](src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java) + - πŸ“„ [OneTimePadCipher](src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java) - πŸ“„ [PermutationCipher](src/main/java/com/thealgorithms/ciphers/PermutationCipher.java) - πŸ“„ [PlayfairCipher](src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java) - πŸ“„ [Polybius](src/main/java/com/thealgorithms/ciphers/Polybius.java) @@ -200,6 +201,7 @@ - πŸ“„ [GenericHashMapUsingArrayList](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayList.java) - πŸ“„ [HashMap](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java) - πŸ“„ [HashMapCuckooHashing](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java) + - πŸ“„ [ImmutableHashMap](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMap.java) - πŸ“„ [Intersection](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Intersection.java) - πŸ“„ [LinearProbingHashMap](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMap.java) - πŸ“„ [MainCuckooHashing](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MainCuckooHashing.java) @@ -266,8 +268,10 @@ - πŸ“„ [BSTRecursiveGeneric](src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java) - πŸ“„ [BTree](src/main/java/com/thealgorithms/datastructures/trees/BTree.java) - πŸ“„ [BinaryTree](src/main/java/com/thealgorithms/datastructures/trees/BinaryTree.java) + - πŸ“„ [BinaryTreeToString](src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java) - πŸ“„ [BoundaryTraversal](src/main/java/com/thealgorithms/datastructures/trees/BoundaryTraversal.java) - πŸ“„ [CeilInBinarySearchTree](src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java) + - πŸ“„ [CentroidDecomposition](src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java) - πŸ“„ [CheckBinaryTreeIsValidBST](src/main/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBST.java) - πŸ“„ [CheckIfBinaryTreeBalanced](src/main/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalanced.java) - πŸ“„ [CheckTreeIsSymmetric](src/main/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetric.java) @@ -287,6 +291,7 @@ - πŸ“„ [SameTreesCheck](src/main/java/com/thealgorithms/datastructures/trees/SameTreesCheck.java) - πŸ“„ [SegmentTree](src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java) - πŸ“„ [SplayTree](src/main/java/com/thealgorithms/datastructures/trees/SplayTree.java) + - πŸ“„ [ThreadedBinaryTree](src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java) - πŸ“„ [Treap](src/main/java/com/thealgorithms/datastructures/trees/Treap.java) - πŸ“„ [TreeRandomNode](src/main/java/com/thealgorithms/datastructures/trees/TreeRandomNode.java) - πŸ“„ [Trie](src/main/java/com/thealgorithms/datastructures/trees/Trie.java) @@ -426,6 +431,7 @@ - πŸ“„ [AbsoluteMax](src/main/java/com/thealgorithms/maths/AbsoluteMax.java) - πŸ“„ [AbsoluteMin](src/main/java/com/thealgorithms/maths/AbsoluteMin.java) - πŸ“„ [AbsoluteValue](src/main/java/com/thealgorithms/maths/AbsoluteValue.java) + - πŸ“„ [AbundantNumber](src/main/java/com/thealgorithms/maths/AbundantNumber.java) - πŸ“„ [AliquotSum](src/main/java/com/thealgorithms/maths/AliquotSum.java) - πŸ“„ [AmicableNumber](src/main/java/com/thealgorithms/maths/AmicableNumber.java) - πŸ“„ [Area](src/main/java/com/thealgorithms/maths/Area.java) @@ -452,10 +458,11 @@ - πŸ“„ [EulerMethod](src/main/java/com/thealgorithms/maths/EulerMethod.java) - πŸ“„ [EulerPseudoprime](src/main/java/com/thealgorithms/maths/EulerPseudoprime.java) - πŸ“„ [EulersFunction](src/main/java/com/thealgorithms/maths/EulersFunction.java) + - πŸ“„ [EvilNumber](src/main/java/com/thealgorithms/maths/EvilNumber.java) + - πŸ“„ [ExtendedEuclideanAlgorithm](src/main/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithm.java) - πŸ“„ [FFT](src/main/java/com/thealgorithms/maths/FFT.java) - πŸ“„ [FFTBluestein](src/main/java/com/thealgorithms/maths/FFTBluestein.java) - πŸ“„ [Factorial](src/main/java/com/thealgorithms/maths/Factorial.java) - - πŸ“„ [FactorialRecursion](src/main/java/com/thealgorithms/maths/FactorialRecursion.java) - πŸ“„ [FastExponentiation](src/main/java/com/thealgorithms/maths/FastExponentiation.java) - πŸ“„ [FastInverseSqrt](src/main/java/com/thealgorithms/maths/FastInverseSqrt.java) - πŸ“„ [FibonacciJavaStreams](src/main/java/com/thealgorithms/maths/FibonacciJavaStreams.java) @@ -489,6 +496,7 @@ - πŸ“„ [LinearDiophantineEquationsSolver](src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java) - πŸ“„ [LongDivision](src/main/java/com/thealgorithms/maths/LongDivision.java) - πŸ“„ [LucasSeries](src/main/java/com/thealgorithms/maths/LucasSeries.java) + - πŸ“„ [LuckyNumber](src/main/java/com/thealgorithms/maths/LuckyNumber.java) - πŸ“„ [MagicSquare](src/main/java/com/thealgorithms/maths/MagicSquare.java) - πŸ“„ [MathBuilder](src/main/java/com/thealgorithms/maths/MathBuilder.java) - πŸ“„ [MaxValue](src/main/java/com/thealgorithms/maths/MaxValue.java) @@ -561,6 +569,7 @@ - πŸ“„ [PrintAMatrixInSpiralOrder](src/main/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrder.java) - πŸ“„ [RotateMatrixBy90Degrees](src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java) - πŸ“„ [SolveSystem](src/main/java/com/thealgorithms/matrix/SolveSystem.java) + - πŸ“„ [StochasticMatrix](src/main/java/com/thealgorithms/matrix/StochasticMatrix.java) - πŸ“ **matrixexponentiation** - πŸ“„ [Fibonacci](src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java) - πŸ“ **utils** @@ -628,6 +637,8 @@ - πŸ“„ [Kinematics](src/main/java/com/thealgorithms/physics/Kinematics.java) - πŸ“„ [ProjectileMotion](src/main/java/com/thealgorithms/physics/ProjectileMotion.java) - πŸ“„ [SimplePendulumRK4](src/main/java/com/thealgorithms/physics/SimplePendulumRK4.java) + - πŸ“„ [SnellLaw](src/main/java/com/thealgorithms/physics/SnellLaw.java) + - πŸ“„ [ThinLens](src/main/java/com/thealgorithms/physics/ThinLens.java) - πŸ“ **puzzlesandgames** - πŸ“„ [TowerOfHanoi](src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java) - πŸ“„ [WordBoggle](src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java) @@ -640,6 +651,7 @@ - πŸ“„ [ReservoirSampling](src/main/java/com/thealgorithms/randomized/ReservoirSampling.java) - πŸ“ **recursion** - πŸ“„ [DiceThrower](src/main/java/com/thealgorithms/recursion/DiceThrower.java) + - πŸ“„ [FactorialRecursion](src/main/java/com/thealgorithms/recursion/FactorialRecursion.java) - πŸ“„ [FibonacciSeries](src/main/java/com/thealgorithms/recursion/FibonacciSeries.java) - πŸ“„ [GenerateSubsets](src/main/java/com/thealgorithms/recursion/GenerateSubsets.java) - πŸ“„ [SylvesterSequence](src/main/java/com/thealgorithms/recursion/SylvesterSequence.java) @@ -786,6 +798,7 @@ - πŸ“„ [StackPostfixNotation](src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java) - πŸ“„ [StackUsingTwoQueues](src/main/java/com/thealgorithms/stacks/StackUsingTwoQueues.java) - πŸ“„ [TrappingRainwater](src/main/java/com/thealgorithms/stacks/TrappingRainwater.java) + - πŸ“„ [ValidParentheses](src/main/java/com/thealgorithms/stacks/ValidParentheses.java) - πŸ“ **strings** - πŸ“„ [AhoCorasick](src/main/java/com/thealgorithms/strings/AhoCorasick.java) - πŸ“„ [Alphabetical](src/main/java/com/thealgorithms/strings/Alphabetical.java) @@ -800,6 +813,7 @@ - πŸ“„ [Isogram](src/main/java/com/thealgorithms/strings/Isogram.java) - πŸ“„ [Isomorphic](src/main/java/com/thealgorithms/strings/Isomorphic.java) - πŸ“„ [KMP](src/main/java/com/thealgorithms/strings/KMP.java) + - πŸ“„ [LengthOfLastWord](src/main/java/com/thealgorithms/strings/LengthOfLastWord.java) - πŸ“„ [LetterCombinationsOfPhoneNumber](src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java) - πŸ“„ [LongestCommonPrefix](src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java) - πŸ“„ [LongestNonRepetitiveSubstring](src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java) @@ -904,6 +918,7 @@ - πŸ“„ [ECCTest](src/test/java/com/thealgorithms/ciphers/ECCTest.java) - πŸ“„ [HillCipherTest](src/test/java/com/thealgorithms/ciphers/HillCipherTest.java) - πŸ“„ [MonoAlphabeticTest](src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java) + - πŸ“„ [OneTimePadCipherTest](src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java) - πŸ“„ [PermutationCipherTest](src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java) - πŸ“„ [PlayfairTest](src/test/java/com/thealgorithms/ciphers/PlayfairTest.java) - πŸ“„ [PolybiusTest](src/test/java/com/thealgorithms/ciphers/PolybiusTest.java) @@ -987,8 +1002,10 @@ - πŸ“„ [DynamicArrayTest](src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java) - πŸ“ **graphs** - πŸ“„ [AStarTest](src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java) + - πŸ“„ [BellmanFordTest](src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java) - πŸ“„ [BipartiteGraphDFSTest](src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java) - πŸ“„ [BoruvkaAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java) + - πŸ“„ [ConnectedComponentTest](src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java) - πŸ“„ [DialsAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/DialsAlgorithmTest.java) - πŸ“„ [DijkstraAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java) - πŸ“„ [DijkstraOptimizedAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithmTest.java) @@ -1011,6 +1028,7 @@ - πŸ“„ [GenericHashMapUsingArrayTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java) - πŸ“„ [HashMapCuckooHashingTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java) - πŸ“„ [HashMapTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapTest.java) + - πŸ“„ [ImmutableHashMapTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/ImmutableHashMapTest.java) - πŸ“„ [IntersectionTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/IntersectionTest.java) - πŸ“„ [LinearProbingHashMapTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMapTest.java) - πŸ“„ [MajorityElementTest](src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElementTest.java) @@ -1069,8 +1087,10 @@ - πŸ“„ [BSTRecursiveTest](src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java) - πŸ“„ [BTreeTest](src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java) - πŸ“„ [BinaryTreeTest](src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java) + - πŸ“„ [BinaryTreeToStringTest](src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java) - πŸ“„ [BoundaryTraversalTest](src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java) - πŸ“„ [CeilInBinarySearchTreeTest](src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java) + - πŸ“„ [CentroidDecompositionTest](src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java) - πŸ“„ [CheckBinaryTreeIsValidBSTTest](src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java) - πŸ“„ [CheckIfBinaryTreeBalancedTest](src/test/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalancedTest.java) - πŸ“„ [CheckTreeIsSymmetricTest](src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java) @@ -1084,6 +1104,7 @@ - πŸ“„ [QuadTreeTest](src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java) - πŸ“„ [SameTreesCheckTest](src/test/java/com/thealgorithms/datastructures/trees/SameTreesCheckTest.java) - πŸ“„ [SplayTreeTest](src/test/java/com/thealgorithms/datastructures/trees/SplayTreeTest.java) + - πŸ“„ [ThreadedBinaryTreeTest](src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java) - πŸ“„ [TreapTest](src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java) - πŸ“„ [TreeTestUtils](src/test/java/com/thealgorithms/datastructures/trees/TreeTestUtils.java) - πŸ“„ [TrieTest](src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java) @@ -1171,6 +1192,7 @@ - πŸ“„ [DinicTest](src/test/java/com/thealgorithms/graph/DinicTest.java) - πŸ“„ [EdmondsKarpTest](src/test/java/com/thealgorithms/graph/EdmondsKarpTest.java) - πŸ“„ [EdmondsTest](src/test/java/com/thealgorithms/graph/EdmondsTest.java) + - πŸ“„ [GomoryHuTreeTest](src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java) - πŸ“„ [HierholzerAlgorithmTest](src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java) - πŸ“„ [HierholzerEulerianPathTest](src/test/java/com/thealgorithms/graph/HierholzerEulerianPathTest.java) - πŸ“„ [HopcroftKarpTest](src/test/java/com/thealgorithms/graph/HopcroftKarpTest.java) @@ -1208,6 +1230,7 @@ - πŸ“„ [AbsoluteMaxTest](src/test/java/com/thealgorithms/maths/AbsoluteMaxTest.java) - πŸ“„ [AbsoluteMinTest](src/test/java/com/thealgorithms/maths/AbsoluteMinTest.java) - πŸ“„ [AbsoluteValueTest](src/test/java/com/thealgorithms/maths/AbsoluteValueTest.java) + - πŸ“„ [AbundantNumberTest](src/test/java/com/thealgorithms/maths/AbundantNumberTest.java) - πŸ“„ [AliquotSumTest](src/test/java/com/thealgorithms/maths/AliquotSumTest.java) - πŸ“„ [AmicableNumberTest](src/test/java/com/thealgorithms/maths/AmicableNumberTest.java) - πŸ“„ [AreaTest](src/test/java/com/thealgorithms/maths/AreaTest.java) @@ -1233,8 +1256,9 @@ - πŸ“„ [EulerMethodTest](src/test/java/com/thealgorithms/maths/EulerMethodTest.java) - πŸ“„ [EulerPseudoprimeTest](src/test/java/com/thealgorithms/maths/EulerPseudoprimeTest.java) - πŸ“„ [EulersFunctionTest](src/test/java/com/thealgorithms/maths/EulersFunctionTest.java) + - πŸ“„ [EvilNumberTest](src/test/java/com/thealgorithms/maths/EvilNumberTest.java) + - πŸ“„ [ExtendedEuclideanAlgorithmTest](src/test/java/com/thealgorithms/maths/ExtendedEuclideanAlgorithmTest.java) - πŸ“„ [FFTTest](src/test/java/com/thealgorithms/maths/FFTTest.java) - - πŸ“„ [FactorialRecursionTest](src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java) - πŸ“„ [FactorialTest](src/test/java/com/thealgorithms/maths/FactorialTest.java) - πŸ“„ [FastExponentiationTest](src/test/java/com/thealgorithms/maths/FastExponentiationTest.java) - πŸ“„ [FastInverseSqrtTests](src/test/java/com/thealgorithms/maths/FastInverseSqrtTests.java) @@ -1269,6 +1293,7 @@ - πŸ“„ [LinearDiophantineEquationsSolverTest](src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java) - πŸ“„ [LongDivisionTest](src/test/java/com/thealgorithms/maths/LongDivisionTest.java) - πŸ“„ [LucasSeriesTest](src/test/java/com/thealgorithms/maths/LucasSeriesTest.java) + - πŸ“„ [LuckyNumberTest](src/test/java/com/thealgorithms/maths/LuckyNumberTest.java) - πŸ“„ [MathBuilderTest](src/test/java/com/thealgorithms/maths/MathBuilderTest.java) - πŸ“„ [MaxValueTest](src/test/java/com/thealgorithms/maths/MaxValueTest.java) - πŸ“„ [MeansTest](src/test/java/com/thealgorithms/maths/MeansTest.java) @@ -1336,6 +1361,7 @@ - πŸ“„ [MirrorOfMatrixTest](src/test/java/com/thealgorithms/matrix/MirrorOfMatrixTest.java) - πŸ“„ [PrintAMatrixInSpiralOrderTest](src/test/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrderTest.java) - πŸ“„ [SolveSystemTest](src/test/java/com/thealgorithms/matrix/SolveSystemTest.java) + - πŸ“„ [StochasticMatrixTest](src/test/java/com/thealgorithms/matrix/StochasticMatrixTest.java) - πŸ“ **misc** - πŸ“„ [ColorContrastRatioTest](src/test/java/com/thealgorithms/misc/ColorContrastRatioTest.java) - πŸ“„ [MapReduceTest](src/test/java/com/thealgorithms/misc/MapReduceTest.java) @@ -1389,6 +1415,8 @@ - πŸ“„ [KinematicsTest](src/test/java/com/thealgorithms/physics/KinematicsTest.java) - πŸ“„ [ProjectileMotionTest](src/test/java/com/thealgorithms/physics/ProjectileMotionTest.java) - πŸ“„ [SimplePendulumRK4Test](src/test/java/com/thealgorithms/physics/SimplePendulumRK4Test.java) + - πŸ“„ [SnellLawTest](src/test/java/com/thealgorithms/physics/SnellLawTest.java) + - πŸ“„ [ThinLensTest](src/test/java/com/thealgorithms/physics/ThinLensTest.java) - πŸ“ **puzzlesandgames** - πŸ“„ [TowerOfHanoiTest](src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java) - πŸ“„ [WordBoggleTest](src/test/java/com/thealgorithms/puzzlesandgames/WordBoggleTest.java) @@ -1401,6 +1429,7 @@ - πŸ“„ [ReservoirSamplingTest](src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java) - πŸ“ **recursion** - πŸ“„ [DiceThrowerTest](src/test/java/com/thealgorithms/recursion/DiceThrowerTest.java) + - πŸ“„ [FactorialRecursionTest](src/test/java/com/thealgorithms/recursion/FactorialRecursionTest.java) - πŸ“„ [FibonacciSeriesTest](src/test/java/com/thealgorithms/recursion/FibonacciSeriesTest.java) - πŸ“„ [GenerateSubsetsTest](src/test/java/com/thealgorithms/recursion/GenerateSubsetsTest.java) - πŸ“„ [SylvesterSequenceTest](src/test/java/com/thealgorithms/recursion/SylvesterSequenceTest.java) @@ -1546,6 +1575,7 @@ - πŸ“„ [StackPostfixNotationTest](src/test/java/com/thealgorithms/stacks/StackPostfixNotationTest.java) - πŸ“„ [StackUsingTwoQueuesTest](src/test/java/com/thealgorithms/stacks/StackUsingTwoQueuesTest.java) - πŸ“„ [TrappingRainwaterTest](src/test/java/com/thealgorithms/stacks/TrappingRainwaterTest.java) + - πŸ“„ [ValidParenthesesTest](src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java) - πŸ“ **strings** - πŸ“„ [AhoCorasickTest](src/test/java/com/thealgorithms/strings/AhoCorasickTest.java) - πŸ“„ [AlphabeticalTest](src/test/java/com/thealgorithms/strings/AlphabeticalTest.java) @@ -1559,6 +1589,7 @@ - πŸ“„ [HorspoolSearchTest](src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java) - πŸ“„ [IsogramTest](src/test/java/com/thealgorithms/strings/IsogramTest.java) - πŸ“„ [IsomorphicTest](src/test/java/com/thealgorithms/strings/IsomorphicTest.java) + - πŸ“„ [LengthOfLastWordTest](src/test/java/com/thealgorithms/strings/LengthOfLastWordTest.java) - πŸ“„ [LetterCombinationsOfPhoneNumberTest](src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java) - πŸ“„ [LongestCommonPrefixTest](src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java) - πŸ“„ [LongestNonRepetitiveSubstringTest](src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java) From 0eda2a1db81424206effd7159af191870ab3a842 Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Mon, 29 Dec 2025 21:55:23 +0100 Subject: [PATCH 053/122] Add test requirement to pull request template (#7185) * Add checklist item for test class inclusion * Update pull request template for algorithm tests --- .github/pull_request_template.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d9cc4c3c35c5..30b46bbf45b5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -13,4 +13,5 @@ In order to reduce the number of notifications sent to the maintainers, please: - [ ] All filenames are in PascalCase. - [ ] All functions and variable names follow Java naming conventions. - [ ] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations. -- [ ] All new code is formatted with `clang-format -i --style=file path/to/your/file.java` \ No newline at end of file +- [ ] All new algorithms include a corresponding test class that validates their functionality. +- [ ] All new code is formatted with `clang-format -i --style=file path/to/your/file.java` From e945f1622490ff3b32e59d31780df27d0dc2ede4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 18:23:45 +0100 Subject: [PATCH 054/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 12.3.0 to 12.3.1 (#7188) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 12.3.0 to 12.3.1. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-12.3.0...checkstyle-12.3.1) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 12.3.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d60f8b9668e6..59a497427c0d 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 12.3.0 + 12.3.1 From 24389c06a29c3472eb1e64c211a585eb5f9a0ff4 Mon Sep 17 00:00:00 2001 From: Giridharan S <168095425+S-GIRI@users.noreply.github.com> Date: Mon, 5 Jan 2026 21:40:24 +0530 Subject: [PATCH 055/122] Add detailed JavaDoc for AllPathsFromSourceToTarget algorithm (#7192) Add detailed JavaDoc for AllPathsFromSourceToTarget --- .../AllPathsFromSourceToTarget.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java b/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java index c35a36d97a57..269880b8ddae 100644 --- a/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java +++ b/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java @@ -4,11 +4,36 @@ import java.util.List; /** - * Program description - To find all possible paths from source to destination - * Wikipedia + * Finds all possible simple paths from a given source vertex to a destination vertex + * in a directed graph using backtracking. * - * @author Siddhant Swarup Mallick + *

This algorithm performs a Depth First Search (DFS) traversal while keeping track + * of visited vertices to avoid cycles. Whenever the destination vertex is reached, + * the current path is stored as one valid path.

+ * + *

Key Characteristics:

+ *
    + *
  • Works on directed graphs
  • + *
  • Does not allow revisiting vertices in the same path
  • + *
  • Stores all possible paths from source to destination
  • + *
+ * + *

Time Complexity:

+ *
    + *
  • Worst Case: O(V!) β€” when the graph is fully connected
  • + *
+ * + *

Space Complexity:

+ *
    + *
  • O(V) for recursion stack and visited array
  • + *
  • Additional space for storing all valid paths
  • + *
+ * + *

This implementation is intended for educational purposes.

+ * + * @see Depth First Search */ + @SuppressWarnings({"rawtypes", "unchecked"}) public class AllPathsFromSourceToTarget { From c06c0f03e3f8aa1390b866a1c9483e34c13fbda3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:50:29 +0100 Subject: [PATCH 056/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 12.3.1 to 13.0.0 (#7193) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 12.3.1 to 13.0.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-12.3.1...checkstyle-13.0.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59a497427c0d..73869d6dd942 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 12.3.1 + 13.0.0 From 404e1404aa6f97c711bf9beb3d54c78097b28357 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:38:42 +0100 Subject: [PATCH 057/122] chore(deps): bump org.junit:junit-bom from 6.0.1 to 6.0.2 (#7194) Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.1...r6.0.2) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73869d6dd942..d3db730327d3 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.junit junit-bom - 6.0.1 + 6.0.2 pom import From 938d06abb5f6a15ffadd260e1810f1a502679561 Mon Sep 17 00:00:00 2001 From: Rajesh Reddy Date: Wed, 7 Jan 2026 17:19:51 +0530 Subject: [PATCH 058/122] docs: clarify hash map vs HashMap terminology (#7195) --- .../java/com/thealgorithms/datastructures/hashmap/Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md b/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md index 252b06ea59b0..4400a97d8128 100644 --- a/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md @@ -2,6 +2,8 @@ A hash map organizes data so you can quickly look up values for a given key. +> Note: The term β€œhash map” refers to the data structure concept, while `HashMap` refers specifically to Java’s implementation. + ## Strengths: - **Fast lookups**: Lookups take O(1) time on average. - **Flexible keys**: Most data types can be used for keys, as long as they're hashable. From ca4bebcbd55edd9dd39e4115cf9c0d962f8c4944 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:56:14 +0100 Subject: [PATCH 059/122] chore(deps): bump peter-evans/create-pull-request from 7 to 8 in /.github/workflows (#7199) chore(deps): bump peter-evans/create-pull-request in /.github/workflows Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7 to 8. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/v7...v8) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-directorymd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-directorymd.yml b/.github/workflows/update-directorymd.yml index 101d82427e38..aa553b46a23b 100644 --- a/.github/workflows/update-directorymd.yml +++ b/.github/workflows/update-directorymd.yml @@ -1,4 +1,4 @@ -ο»Ώname: Generate Directory Markdown +name: Generate Directory Markdown on: push: @@ -33,7 +33,7 @@ jobs: git diff --cached --quiet || git commit -m "Update DIRECTORY.md" - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} branch: update-directory From fe6066b332d6e77f5f36292e9958dad367e0b731 Mon Sep 17 00:00:00 2001 From: Ahmed Allam <60698204+GziXnine@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:36:46 +0200 Subject: [PATCH 060/122] =?UTF-8?q?Add=20SmoothSort=20(Dijkstra=E2=80=99s?= =?UTF-8?q?=20adaptive=20in-place=20heapsort=20variant)=20(#7200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: implement Smooth Sort algorithm with detailed JavaDoc and test class * style: format LEONARDO array for improved readability with clang-format --------- Co-authored-by: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com> --- .../com/thealgorithms/sorts/SmoothSort.java | 168 ++++++++++++++++++ .../thealgorithms/sorts/SmoothSortTest.java | 8 + 2 files changed, 176 insertions(+) create mode 100644 src/main/java/com/thealgorithms/sorts/SmoothSort.java create mode 100644 src/test/java/com/thealgorithms/sorts/SmoothSortTest.java diff --git a/src/main/java/com/thealgorithms/sorts/SmoothSort.java b/src/main/java/com/thealgorithms/sorts/SmoothSort.java new file mode 100644 index 000000000000..c45d6f1f02b2 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SmoothSort.java @@ -0,0 +1,168 @@ +package com.thealgorithms.sorts; + +/** + * Smooth Sort is an in-place, comparison-based sorting algorithm proposed by Edsger W. Dijkstra (1981). + * + *

It can be viewed as a variant of heapsort that maintains a forest of heap-ordered Leonardo trees + * (trees whose sizes are Leonardo numbers). The algorithm is adaptive: when the input is already + * sorted or nearly sorted, the heap invariants are often satisfied and the expensive rebalancing + * operations do little work, yielding near-linear behavior. + * + *

Time Complexity: + *

    + *
  • Best case: O(n) for already sorted input
  • + *
  • Average case: O(n log n)
  • + *
  • Worst case: O(n log n)
  • + *
+ * + *

Space Complexity: O(1) auxiliary space (in-place). + * + * @see Smoothsort + * @see Leonardo numbers + * @see SortAlgorithm + */ +public class SmoothSort implements SortAlgorithm { + + /** + * Leonardo numbers (L(0) = L(1) = 1, L(k+2) = L(k+1) + L(k) + 1) up to the largest value that + * fits into a signed 32-bit integer. + */ + private static final int[] LEONARDO = {1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891, 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457, 1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703, 48315633, 78176337, + 126491971, 204668309, 331160281, 535828591, 866988873, 1402817465}; + + /** + * Sorts the given array in ascending order using Smooth Sort. + * + * @param array the array to sort + * @param the element type + * @return the sorted array + */ + @Override + public > T[] sort(final T[] array) { + if (array.length < 2) { + return array; + } + + final int last = array.length - 1; + + // The forest shape is encoded as (p, pshift): p is a bit-vector of present tree orders, + // shifted right by pshift. pshift is the order of the rightmost (current) Leonardo tree. + long p = 1L; + int pshift = 1; + + int head = 0; + while (head < last) { + if ((p & 3L) == 3L) { + sift(array, pshift, head); + p >>>= 2; + pshift += 2; + } else { + // Add a new singleton tree; if it will not be merged anymore, we must fully trinkle. + if (LEONARDO[pshift - 1] >= last - head) { + trinkle(array, p, pshift, head, false); + } else { + // This tree will be merged later, so it is enough to restore its internal heap property. + sift(array, pshift, head); + } + + if (pshift == 1) { + // If L(1) is used, the new singleton is L(0). + p <<= 1; + pshift = 0; + } else { + // Otherwise, shift to order 1 and append a singleton of order 1. + p <<= (pshift - 1); + pshift = 1; + } + } + + p |= 1L; + head++; + } + + trinkle(array, p, pshift, head, false); + + // Repeatedly remove the maximum (always at head) by shrinking the heap region. + while (pshift != 1 || p != 1L) { + if (pshift <= 1) { + // Rightmost tree is a singleton (order 0 or 1). Move to the previous tree root. + final long mask = p & ~1L; + final int shift = Long.numberOfTrailingZeros(mask); + p >>>= shift; + pshift += shift; + } else { + // Split a tree of order (pshift) into two children trees of orders (pshift-1) and (pshift-2). + p <<= 2; + p ^= 7L; + pshift -= 2; + + trinkle(array, p >>> 1, pshift + 1, head - LEONARDO[pshift] - 1, true); + trinkle(array, p, pshift, head - 1, true); + } + + head--; + } + + return array; + } + + private static > void sift(final T[] array, int order, int root) { + final T value = array[root]; + + while (order > 1) { + final int right = root - 1; + final int left = root - 1 - LEONARDO[order - 2]; + + if (!SortUtils.less(value, array[left]) && !SortUtils.less(value, array[right])) { + break; + } + + if (!SortUtils.less(array[left], array[right])) { + array[root] = array[left]; + root = left; + order -= 1; + } else { + array[root] = array[right]; + root = right; + order -= 2; + } + } + + array[root] = value; + } + + private static > void trinkle(final T[] array, long p, int order, int root, boolean trusty) { + final T value = array[root]; + + while (p != 1L) { + final int stepson = root - LEONARDO[order]; + + if (!SortUtils.less(value, array[stepson])) { + break; + } + + if (!trusty && order > 1) { + final int right = root - 1; + final int left = root - 1 - LEONARDO[order - 2]; + + if (!SortUtils.less(array[right], array[stepson]) || !SortUtils.less(array[left], array[stepson])) { + break; + } + } + + array[root] = array[stepson]; + root = stepson; + + final long mask = p & ~1L; + final int shift = Long.numberOfTrailingZeros(mask); + p >>>= shift; + order += shift; + trusty = false; + } + + if (!trusty) { + array[root] = value; + sift(array, order, root); + } + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SmoothSortTest.java b/src/test/java/com/thealgorithms/sorts/SmoothSortTest.java new file mode 100644 index 000000000000..8df0502e80e7 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SmoothSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class SmoothSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new SmoothSort(); + } +} From 1644db2a3449d3a82dfd96ad25a98d7489adcf54 Mon Sep 17 00:00:00 2001 From: Tarunj Gupta <145449390+tarunjgupta@users.noreply.github.com> Date: Sat, 10 Jan 2026 21:02:19 +0530 Subject: [PATCH 061/122] Add Count Nice Subarrays sliding using window algorithm (#7206) * Add Count Nice Subarrays sliding window algorithm * Add detailed comments and reference to CountNiceSubarrays * Fix clang-format issues in CountNiceSubarrays * Added extra edge cases * changes made --- .../slidingwindow/CountNiceSubarrays.java | 99 +++++++++++++++++++ .../slidingwindow/CountNiceSubarraysTest.java | 55 +++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/main/java/com/thealgorithms/slidingwindow/CountNiceSubarrays.java create mode 100644 src/test/java/com/thealgorithms/slidingwindow/CountNiceSubarraysTest.java diff --git a/src/main/java/com/thealgorithms/slidingwindow/CountNiceSubarrays.java b/src/main/java/com/thealgorithms/slidingwindow/CountNiceSubarrays.java new file mode 100644 index 000000000000..46f8deeb58dd --- /dev/null +++ b/src/main/java/com/thealgorithms/slidingwindow/CountNiceSubarrays.java @@ -0,0 +1,99 @@ +package com.thealgorithms.slidingwindow; + +/** + * Counts the number of "nice subarrays". + * A nice subarray is a contiguous subarray that contains exactly k odd numbers. + * + * This implementation uses the sliding window technique. + * + * Reference: + * https://leetcode.com/problems/count-number-of-nice-subarrays/ + * + * Time Complexity: O(n) + * Space Complexity: O(n) + */ +public final class CountNiceSubarrays { + + // Private constructor to prevent instantiation + private CountNiceSubarrays() { + } + + /** + * Returns the count of subarrays containing exactly k odd numbers. + * + * @param nums input array of integers + * @param k number of odd elements required in the subarray + * @return number of nice subarrays + */ + public static int countNiceSubarrays(int[] nums, int k) { + + int n = nums.length; + + // Left pointer of the sliding window + int left = 0; + + // Tracks number of odd elements in the current window + int oddCount = 0; + + // Final answer: total number of nice subarrays + int result = 0; + + /* + * memo[i] stores how many valid starting positions exist + * when the left pointer is at index i. + * + * This avoids recomputing the same values again. + */ + int[] memo = new int[n]; + + // Right pointer moves forward to expand the window + for (int right = 0; right < n; right++) { + + // If current element is odd, increment odd count + if ((nums[right] & 1) == 1) { + oddCount++; + } + + /* + * If oddCount exceeds k, shrink the window from the left + * until oddCount becomes valid again. + */ + if (oddCount > k) { + left += memo[left]; + oddCount--; + } + + /* + * When the window contains exactly k odd numbers, + * count all possible valid subarrays starting at `left`. + */ + if (oddCount == k) { + + /* + * If this left index hasn't been processed before, + * count how many consecutive even numbers follow it. + */ + if (memo[left] == 0) { + int count = 0; + int temp = left; + + // Count consecutive even numbers + while ((nums[temp] & 1) == 0) { + count++; + temp++; + } + + /* + * Number of valid subarrays starting at `left` + * is (count of even numbers + 1) + */ + memo[left] = count + 1; + } + + // Add number of valid subarrays for this left position + result += memo[left]; + } + } + return result; + } +} diff --git a/src/test/java/com/thealgorithms/slidingwindow/CountNiceSubarraysTest.java b/src/test/java/com/thealgorithms/slidingwindow/CountNiceSubarraysTest.java new file mode 100644 index 000000000000..71bf24cc9e30 --- /dev/null +++ b/src/test/java/com/thealgorithms/slidingwindow/CountNiceSubarraysTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.slidingwindow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CountNiceSubarraysTest { + @Test + void testExampleCase() { + int[] nums = {1, 1, 2, 1, 1}; + assertEquals(2, CountNiceSubarrays.countNiceSubarrays(nums, 3)); + } + + @Test + void testAllEvenNumbers() { + int[] nums = {2, 4, 6, 8}; + assertEquals(0, CountNiceSubarrays.countNiceSubarrays(nums, 1)); + } + + @Test + void testSingleOdd() { + int[] nums = {1}; + assertEquals(1, CountNiceSubarrays.countNiceSubarrays(nums, 1)); + } + + @Test + void testMultipleChoices() { + int[] nums = {2, 2, 1, 2, 2, 1, 2}; + assertEquals(6, CountNiceSubarrays.countNiceSubarrays(nums, 2)); + } + + @Test + void testTrailingEvenNumbers() { + int[] nums = {1, 2, 2, 2}; + assertEquals(4, CountNiceSubarrays.countNiceSubarrays(nums, 1)); + } + + @Test + void testMultipleWindowShrinks() { + int[] nums = {1, 1, 1, 1}; + assertEquals(3, CountNiceSubarrays.countNiceSubarrays(nums, 2)); + } + + @Test + void testEvensBetweenOdds() { + int[] nums = {2, 1, 2, 1, 2}; + assertEquals(4, CountNiceSubarrays.countNiceSubarrays(nums, 2)); + } + + @Test + void testShrinkWithTrailingEvens() { + int[] nums = {2, 2, 1, 2, 2, 1, 2, 2}; + assertEquals(9, CountNiceSubarrays.countNiceSubarrays(nums, 2)); + } +} From bdda4fa6b49c94087caab1021d680aa78a50d7e1 Mon Sep 17 00:00:00 2001 From: asmitha-16 Date: Wed, 14 Jan 2026 03:09:41 +0530 Subject: [PATCH 062/122] Handle negative input in perfect square checks (#7207) --- src/main/java/com/thealgorithms/maths/PerfectSquare.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/thealgorithms/maths/PerfectSquare.java b/src/main/java/com/thealgorithms/maths/PerfectSquare.java index e9318bd7d805..aec43062121a 100644 --- a/src/main/java/com/thealgorithms/maths/PerfectSquare.java +++ b/src/main/java/com/thealgorithms/maths/PerfectSquare.java @@ -15,6 +15,9 @@ private PerfectSquare() { * false */ public static boolean isPerfectSquare(final int number) { + if (number < 0) { + return false; + } final int sqrt = (int) Math.sqrt(number); return sqrt * sqrt == number; } @@ -27,6 +30,9 @@ public static boolean isPerfectSquare(final int number) { * {@code false} */ public static boolean isPerfectSquareUsingPow(long number) { + if (number < 0) { + return false; + } long a = (long) Math.pow(number, 1.0 / 2); return a * a == number; } From 7148661e44bb4b27d29f63771ef205235f42192a Mon Sep 17 00:00:00 2001 From: Ahmed Allam <60698204+GziXnine@users.noreply.github.com> Date: Wed, 14 Jan 2026 00:04:14 +0200 Subject: [PATCH 063/122] Feat/tournament sort (#7201) * feat: implement Smooth Sort algorithm with detailed JavaDoc and test class * style: format LEONARDO array for improved readability with clang-format * feat(sorts): add TournamentSort (winner-tree) * test: add unit test for null array handling in TournamentSort --------- Co-authored-by: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com> Co-authored-by: Deniz Altunkapan Co-authored-by: Oleksandr Klymenko <19151554+alxkm@users.noreply.github.com> --- .../thealgorithms/sorts/TournamentSort.java | 84 +++++++++++++++++++ .../sorts/TournamentSortTest.java | 19 +++++ 2 files changed, 103 insertions(+) create mode 100644 src/main/java/com/thealgorithms/sorts/TournamentSort.java create mode 100644 src/test/java/com/thealgorithms/sorts/TournamentSortTest.java diff --git a/src/main/java/com/thealgorithms/sorts/TournamentSort.java b/src/main/java/com/thealgorithms/sorts/TournamentSort.java new file mode 100644 index 000000000000..ec51a1e2c0a9 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/TournamentSort.java @@ -0,0 +1,84 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; + +/** + * Tournament Sort algorithm implementation. + * + * Tournament sort builds a winner tree (a complete binary tree storing the index + * of the smallest element in each subtree). It then repeatedly extracts the + * winner (minimum) and updates the path from the removed leaf to the root. + * + * Time Complexity: + * - Best case: O(n log n) + * - Average case: O(n log n) + * - Worst case: O(n log n) + * + * Space Complexity: O(n) – additional winner-tree storage + * + * @see Tournament Sort Algorithm + * @see SortAlgorithm + */ +public class TournamentSort implements SortAlgorithm { + + @Override + public > T[] sort(T[] array) { + if (array == null || array.length < 2) { + return array; + } + + final int n = array.length; + final int leafCount = nextPowerOfTwo(n); + + // Winner tree represented as an array: + // - Leaves live at [leafCount .. 2*leafCount) + // - Internal nodes live at [1 .. leafCount) + // Each node stores an index into the original array or -1 for "empty". + final int[] tree = new int[2 * leafCount]; + Arrays.fill(tree, -1); + + for (int i = 0; i < n; i++) { + tree[leafCount + i] = i; + } + + for (int node = leafCount - 1; node >= 1; node--) { + tree[node] = winnerIndex(array, tree[node * 2], tree[node * 2 + 1]); + } + + final T[] result = array.clone(); + for (int out = 0; out < n; out++) { + final int winner = tree[1]; + result[out] = array[winner]; + + int node = leafCount + winner; + tree[node] = -1; + + for (node /= 2; node >= 1; node /= 2) { + tree[node] = winnerIndex(array, tree[node * 2], tree[node * 2 + 1]); + } + } + + System.arraycopy(result, 0, array, 0, n); + return array; + } + + private static int nextPowerOfTwo(int n) { + int power = 1; + while (power < n) { + power <<= 1; + } + return power; + } + + private static > int winnerIndex(T[] array, int leftIndex, int rightIndex) { + if (leftIndex == -1) { + return rightIndex; + } + if (rightIndex == -1) { + return leftIndex; + } + + // If equal, prefer the left element to keep ordering deterministic. + return SortUtils.less(array[rightIndex], array[leftIndex]) ? rightIndex : leftIndex; + } +} diff --git a/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java b/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java new file mode 100644 index 000000000000..91da746447a8 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +public class TournamentSortTest extends SortingAlgorithmTest { + + @Test + void shouldAcceptWhenNullArrayIsPassed() { + Integer[] array = null; + assertNull(getSortAlgorithm().sort(array)); + } + + @Override + SortAlgorithm getSortAlgorithm() { + return new TournamentSort(); + } +} From 66f76eb3d920f7a025aaca71ccc6c84da4287658 Mon Sep 17 00:00:00 2001 From: Ahmed Allam <60698204+GziXnine@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:59:25 +0200 Subject: [PATCH 064/122] Add RotatedBinarySearch (search in rotated sorted array) (#7202) * feat: implement Smooth Sort algorithm with detailed JavaDoc and test class * style: format LEONARDO array for improved readability with clang-format * feat(sorts): add TournamentSort (winner-tree) * test: add unit test for null array handling in TournamentSort * feat(search): add rotated binary search * test: add unit test for handling middle element in right sorted half of rotated array --------- Co-authored-by: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com> --- .../searches/RotatedBinarySearch.java | 60 +++++++++++++++++++ .../searches/RotatedBinarySearchTest.java | 53 ++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java create mode 100644 src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java diff --git a/src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java b/src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java new file mode 100644 index 000000000000..86099b2fa2fa --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java @@ -0,0 +1,60 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * Searches for a key in a sorted array that has been rotated at an unknown pivot. + * + *

+ * Example: + * {@code [8, 9, 10, 1, 2, 3, 4, 5, 6, 7]} + * + *

+ * This is a modified binary search. When the array contains no duplicates, the + * time complexity is {@code O(log n)}. With duplicates, the algorithm still + * works but may degrade to {@code O(n)} in the worst case. + * + * @see Search in rotated sorted array + * @see SearchAlgorithm + */ +public final class RotatedBinarySearch implements SearchAlgorithm { + + @Override + public > int find(T[] array, T key) { + int left = 0; + int right = array.length - 1; + + while (left <= right) { + int middle = (left + right) >>> 1; + int cmp = key.compareTo(array[middle]); + if (cmp == 0) { + return middle; + } + + // Handle duplicates: if we cannot determine which side is sorted. + if (array[left].compareTo(array[middle]) == 0 && array[middle].compareTo(array[right]) == 0) { + left++; + right--; + continue; + } + + // Left half is sorted. + if (array[left].compareTo(array[middle]) <= 0) { + if (array[left].compareTo(key) <= 0 && key.compareTo(array[middle]) < 0) { + right = middle - 1; + } else { + left = middle + 1; + } + } else { + // Right half is sorted. + if (array[middle].compareTo(key) < 0 && key.compareTo(array[right]) <= 0) { + left = middle + 1; + } else { + right = middle - 1; + } + } + } + + return -1; + } +} diff --git a/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java new file mode 100644 index 000000000000..1e6ab4c37fcc --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class RotatedBinarySearchTest { + + @Test + void shouldFindElementInRotatedArrayLeftSide() { + RotatedBinarySearch search = new RotatedBinarySearch(); + Integer[] array = {8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7}; + assertEquals(2, search.find(array, 10)); + } + + @Test + void shouldFindElementInRotatedArrayRightSide() { + RotatedBinarySearch search = new RotatedBinarySearch(); + Integer[] array = {8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7}; + assertEquals(6, search.find(array, 2)); + } + + @Test + void shouldFindElementInNotRotatedArray() { + RotatedBinarySearch search = new RotatedBinarySearch(); + Integer[] array = {1, 2, 3, 4, 5, 6, 7}; + assertEquals(4, search.find(array, 5)); + } + + @Test + void shouldReturnMinusOneWhenNotFound() { + RotatedBinarySearch search = new RotatedBinarySearch(); + Integer[] array = {4, 5, 6, 7, 0, 1, 2}; + assertEquals(-1, search.find(array, 3)); + } + + @Test + void shouldHandleWhenMiddleIsGreaterThanKeyInRightSortedHalf() { + RotatedBinarySearch search = new RotatedBinarySearch(); + Integer[] array = {6, 7, 0, 1, 2, 3, 4, 5}; + assertEquals(2, search.find(array, 0)); + } + + @Test + void shouldHandleDuplicates() { + RotatedBinarySearch search = new RotatedBinarySearch(); + Integer[] array = {2, 2, 2, 3, 4, 2}; + int index = search.find(array, 3); + assertTrue(index >= 0 && index < array.length); + assertEquals(3, array[index]); + } +} From babc762478f94491209999ed120538be6b863d80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 23:24:40 +0100 Subject: [PATCH 065/122] chore(deps-dev): bump com.mebigfatguy.fb-contrib:fb-contrib from 7.7.2 to 7.7.3 (#7211) chore(deps-dev): bump com.mebigfatguy.fb-contrib:fb-contrib Bumps [com.mebigfatguy.fb-contrib:fb-contrib](https://github.com/mebigfatguy/fb-contrib) from 7.7.2 to 7.7.3. - [Commits](https://github.com/mebigfatguy/fb-contrib/compare/v7.7.2...v7.7.3) --- updated-dependencies: - dependency-name: com.mebigfatguy.fb-contrib:fb-contrib dependency-version: 7.7.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3db730327d3..a685e334460c 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.mebigfatguy.fb-contrib fb-contrib - 7.7.2 + 7.7.3 com.h3xstream.findsecbugs From fd0bcb79e61743e96e837a48e2fc9a54efd08a42 Mon Sep 17 00:00:00 2001 From: Ahmed Allam <60698204+GziXnine@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:33:29 +0200 Subject: [PATCH 066/122] Add Middle of Linked List (Slow/Fast Pointers) (#7212) * feat: implement Smooth Sort algorithm with detailed JavaDoc and test class * style: format LEONARDO array for improved readability with clang-format * feat: add MiddleOfLinkedList class and corresponding test cases * docs: update documentation for MiddleOfLinkedList class * test: refactor MiddleOfLinkedListTest to improve readability and assertions * test: refactor MiddleOfLinkedListTest for improved null safety and readability --------- Co-authored-by: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com> --- .../lists/MiddleOfLinkedList.java | 46 ++++++++++++ .../lists/MiddleOfLinkedListTest.java | 74 +++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java create mode 100644 src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java new file mode 100644 index 000000000000..0ee788db2ff9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java @@ -0,0 +1,46 @@ +package com.thealgorithms.datastructures.lists; + +/** + * Returns the middle node of a singly linked list using the two-pointer technique. + * + *

The {@code slow} pointer advances by one node per iteration while {@code fast} advances by two. + * When {@code fast == null} or {@code fast.next == null}, {@code slow} points to the middle node. + * For even-length lists, this returns the second middle node.

+ * + *

This method does not modify the input list.

+ * + *

Reference: https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare

+ * + *

Complexity:

+ *
    + *
  • Time: {@code O(n)}
  • + *
  • Space: {@code O(1)}
  • + *
+ */ +public final class MiddleOfLinkedList { + + private MiddleOfLinkedList() { + } + + /** + * Returns the middle node of the list. + * + * @param head the head of the singly linked list; may be {@code null} + * @return the middle node (second middle for even-sized lists), or {@code null} if {@code head} is {@code null} + */ + public static SinglyLinkedListNode middleNode(final SinglyLinkedListNode head) { + if (head == null) { + return null; + } + + SinglyLinkedListNode slow = head; + SinglyLinkedListNode fast = head; + + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + return slow; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java new file mode 100644 index 000000000000..ba5614a07916 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java @@ -0,0 +1,74 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Objects; +import org.junit.jupiter.api.Test; + +public class MiddleOfLinkedListTest { + + private static SinglyLinkedListNode listOf(int firstValue, int... remainingValues) { + SinglyLinkedListNode head = new SinglyLinkedListNode(firstValue); + SinglyLinkedListNode current = head; + + for (int i = 0; i < remainingValues.length; i++) { + current.next = new SinglyLinkedListNode(remainingValues[i]); + current = current.next; + } + return head; + } + + @Test + void middleNodeOddLength() { + SinglyLinkedListNode head = listOf(1, 2, 3, 4, 5); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(3, middle.value); + } + + @Test + void middleNodeEvenLengthReturnsSecondMiddle() { + SinglyLinkedListNode head = listOf(1, 2, 3, 4, 5, 6); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(4, middle.value); + } + + @Test + void middleNodeSingleElement() { + SinglyLinkedListNode head = listOf(42); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(42, middle.value); + } + + @Test + void middleNodeTwoElementsReturnsSecond() { + SinglyLinkedListNode head = listOf(10, 20); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(20, middle.value); + } + + @Test + void middleNodeNullHead() { + assertNull(MiddleOfLinkedList.middleNode(null)); + } + + @Test + void middleNodeDoesNotModifyListStructure() { + SinglyLinkedListNode first = new SinglyLinkedListNode(1); + SinglyLinkedListNode second = new SinglyLinkedListNode(2); + SinglyLinkedListNode third = new SinglyLinkedListNode(3); + SinglyLinkedListNode fourth = new SinglyLinkedListNode(4); + + first.next = second; + second.next = third; + third.next = fourth; + + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(first)); + assertEquals(3, middle.value); + + assertEquals(second, first.next); + assertEquals(third, second.next); + assertEquals(fourth, third.next); + assertNull(fourth.next); + } +} From 782d0755d584c9f5c9931ff6f03976144e10c279 Mon Sep 17 00:00:00 2001 From: SwaatiR <85189166+SwaatiR@users.noreply.github.com> Date: Sat, 17 Jan 2026 23:48:54 +0530 Subject: [PATCH 067/122] Improve documentation for Linear Search algorithm (#7214) --- .../thealgorithms/searches/LinearSearch.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/thealgorithms/searches/LinearSearch.java b/src/main/java/com/thealgorithms/searches/LinearSearch.java index c7b70edb5112..cb483d8dfedc 100644 --- a/src/main/java/com/thealgorithms/searches/LinearSearch.java +++ b/src/main/java/com/thealgorithms/searches/LinearSearch.java @@ -1,21 +1,26 @@ package com.thealgorithms.searches; import com.thealgorithms.devutils.searches.SearchAlgorithm; - /** - * Linear search is the easiest search algorithm It works with sorted and - * unsorted arrays (an binary search works only with sorted array) This - * algorithm just compares all elements of an array to find a value + * Linear Search is a simple searching algorithm that checks + * each element of the array sequentially until the target + * value is found or the array ends. + * + * It works for both sorted and unsorted arrays. * - *

- * Worst-case performance O(n) Best-case performance O(1) Average performance - * O(n) Worst-case space complexity + * Time Complexity: + * - Best case: O(1) + * - Average case: O(n) + * - Worst case: O(n) * - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) + * Space Complexity: O(1) + * + * @author Varun Upadhyay + * @author Podshivalov Nikita * @see BinarySearch * @see SearchAlgorithm */ + public class LinearSearch implements SearchAlgorithm { /** From 48f6322b385f12e8b052d1e04e7d1acdd765b982 Mon Sep 17 00:00:00 2001 From: Chahat Sandhu Date: Sat, 17 Jan 2026 12:22:11 -0600 Subject: [PATCH 068/122] docs: add Javadoc to FibonacciSeries (#7215) * docs: add Javadoc to FibonacciSeries * fix: make small adjustment --------- Co-authored-by: Deniz Altunkapan --- .../recursion/FibonacciSeries.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java index 9bc6da2f7443..9c809858099e 100644 --- a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java +++ b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java @@ -1,16 +1,26 @@ package com.thealgorithms.recursion; -/* - The Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, - starting with 0 and 1. - NUMBER 0 1 2 3 4 5 6 7 8 9 10 ... - FIBONACCI 0 1 1 2 3 5 8 13 21 34 55 ... -*/ +/** + * The Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, + * starting with 0 and 1. + *

+ * Example: + * 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ... + *

+ */ public final class FibonacciSeries { private FibonacciSeries() { throw new UnsupportedOperationException("Utility class"); } + + /** + * Calculates the nth term in the Fibonacci sequence using recursion. + * + * @param n the position in the Fibonacci sequence (must be non-negative) + * @return the nth Fibonacci number + * @throws IllegalArgumentException if n is negative + */ public static int fibonacci(int n) { if (n < 0) { throw new IllegalArgumentException("n must be a non-negative integer"); From 79cdb98193cb34fa32d9bbad06c21a0d0f356bb3 Mon Sep 17 00:00:00 2001 From: SwaatiR <85189166+SwaatiR@users.noreply.github.com> Date: Sun, 18 Jan 2026 21:31:01 +0530 Subject: [PATCH 069/122] Add input validation and clarify sorted array requirement in Binary Search (#7216) Added input validation and clarify sorted array rrequirement in Binary Search --- src/main/java/com/thealgorithms/searches/BinarySearch.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch.java b/src/main/java/com/thealgorithms/searches/BinarySearch.java index bedad1667f33..0cac484d56b4 100644 --- a/src/main/java/com/thealgorithms/searches/BinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/BinarySearch.java @@ -5,7 +5,9 @@ /** * Binary search is one of the most popular algorithms The algorithm finds the * position of a target value within a sorted array - * + * IMPORTANT + * This algorithm works correctly only if the input array is sorted + * in ascending order. *

* Worst-case performance O(log n) Best-case performance O(1) Average * performance O(log n) Worst-case space complexity O(1) @@ -25,6 +27,9 @@ class BinarySearch implements SearchAlgorithm { */ @Override public > int find(T[] array, T key) { + if (array == null || array.length == 0) { + return -1; + } return search(array, key, 0, array.length - 1); } From 1b9373e71a9a3e8e9f1b4f3f2338aee419ee2c1e Mon Sep 17 00:00:00 2001 From: Chahat Sandhu Date: Mon, 19 Jan 2026 03:59:58 -0600 Subject: [PATCH 070/122] feat: add Bell Numbers algorithm using Aitken's Array (#7219) * feat: added Bell Numbers algorithm using Aitken's Array * style: applied clang-format fixes --- .../com/thealgorithms/maths/BellNumbers.java | 59 +++++++++++++++++++ .../thealgorithms/maths/BellNumbersTest.java | 53 +++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/BellNumbers.java create mode 100644 src/test/java/com/thealgorithms/maths/BellNumbersTest.java diff --git a/src/main/java/com/thealgorithms/maths/BellNumbers.java b/src/main/java/com/thealgorithms/maths/BellNumbers.java new file mode 100644 index 000000000000..d4dc1014f48b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/BellNumbers.java @@ -0,0 +1,59 @@ +package com.thealgorithms.maths; + +/** + * The Bell numbers count the number of partitions of a set. + * The n-th Bell number is the number of ways a set of n elements can be partitioned + * into nonempty subsets. + * + *

+ * This implementation uses the Bell Triangle (Aitken's array) method. + * Time Complexity: O(n^2) + * Space Complexity: O(n^2) + *

+ * + * @author Chahat Sandhu, singhc7 + * @see Bell Number (Wikipedia) + */ +public final class BellNumbers { + + private BellNumbers() { + } + + /** + * Calculates the n-th Bell number using the Bell Triangle. + * + * @param n the index of the Bell number (must be non-negative) + * @return the n-th Bell number + * @throws IllegalArgumentException if n is negative or n > 25 + */ + public static long compute(int n) { + if (n < 0) { + throw new IllegalArgumentException("n must be non-negative"); + } + if (n == 0) { + return 1; + } + if (n > 25) { + throw new IllegalArgumentException("n must be <= 25. For larger n, use BigInteger implementation."); + } + + // We use a 2D array to visualize the Bell Triangle + long[][] bellTriangle = new long[n + 1][n + 1]; + + // Base case: The triangle starts with 1 + bellTriangle[0][0] = 1; + + for (int i = 1; i <= n; i++) { + // Rule 1: The first number in a new row is the LAST number of the previous row + bellTriangle[i][0] = bellTriangle[i - 1][i - 1]; + + // Rule 2: Fill the rest of the row by adding the previous neighbor and the upper-left neighbor + for (int j = 1; j <= i; j++) { + bellTriangle[i][j] = bellTriangle[i][j - 1] + bellTriangle[i - 1][j - 1]; + } + } + + // The Bell number B_n is the first number in the n-th row + return bellTriangle[n][0]; + } +} diff --git a/src/test/java/com/thealgorithms/maths/BellNumbersTest.java b/src/test/java/com/thealgorithms/maths/BellNumbersTest.java new file mode 100644 index 000000000000..8dd83cf0f7a9 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/BellNumbersTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class BellNumbersTest { + + @Test + void testStandardCases() { + // Base cases and small numbers + assertEquals(1, BellNumbers.compute(0)); + assertEquals(1, BellNumbers.compute(1)); + assertEquals(2, BellNumbers.compute(2)); + assertEquals(5, BellNumbers.compute(3)); + assertEquals(15, BellNumbers.compute(4)); + assertEquals(52, BellNumbers.compute(5)); + } + + @Test + void testMediumNumber() { + // B10 = 115,975 + assertEquals(115975, BellNumbers.compute(10)); + // B15 = 1,382,958,545 + assertEquals(1382958545L, BellNumbers.compute(15)); + } + + @Test + void testLargeNumber() { + // B20 = 51,724,158,235,372 + // We use the 'L' suffix to tell Java this is a long literal + assertEquals(51724158235372L, BellNumbers.compute(20)); + } + + @Test + void testMaxLongCapacity() { + // B25 is the largest Bell number that fits in a Java long (signed 64-bit) + // B25 = 4,638,590,332,229,999,353 + assertEquals(4638590332229999353L, BellNumbers.compute(25)); + } + + @Test + void testNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> BellNumbers.compute(-1)); + } + + @Test + void testOverflowProtection() { + // We expect an exception if the user asks for the impossible + assertThrows(IllegalArgumentException.class, () -> BellNumbers.compute(26)); + } +} From 109ed2e49743df52cb6aad1a5cfc496432ae3d10 Mon Sep 17 00:00:00 2001 From: Chahat Sandhu Date: Mon, 19 Jan 2026 04:15:59 -0600 Subject: [PATCH 071/122] feat: Add Prefix Sum category with 1D and 2D implementations (#7220) Co-authored-by: Deniz Altunkapan --- .../thealgorithms/prefixsum/PrefixSum.java | 54 +++++++++++ .../thealgorithms/prefixsum/PrefixSum2D.java | 64 +++++++++++++ .../prefixsum/PrefixSum2DTest.java | 92 +++++++++++++++++++ .../prefixsum/PrefixSumTest.java | 80 ++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 src/main/java/com/thealgorithms/prefixsum/PrefixSum.java create mode 100644 src/main/java/com/thealgorithms/prefixsum/PrefixSum2D.java create mode 100644 src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java create mode 100644 src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java diff --git a/src/main/java/com/thealgorithms/prefixsum/PrefixSum.java b/src/main/java/com/thealgorithms/prefixsum/PrefixSum.java new file mode 100644 index 000000000000..47f6366e2924 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/PrefixSum.java @@ -0,0 +1,54 @@ +package com.thealgorithms.prefixsum; + +/** + * A class that implements the Prefix Sum algorithm. + * + *

Prefix Sum is a technique used to preprocess an array such that + * range sum queries can be answered in O(1) time. + * The preprocessing step takes O(N) time. + * + *

This implementation uses a long array for the prefix sums to prevent + * integer overflow when the sum of elements exceeds Integer.MAX_VALUE. + * + * @see Prefix Sum (Wikipedia) + * @author Chahat Sandhu, singhc7 + */ +public class PrefixSum { + + private final long[] prefixSums; + + /** + * Constructor to preprocess the input array. + * + * @param array The input integer array. + * @throws IllegalArgumentException if the array is null. + */ + public PrefixSum(int[] array) { + if (array == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + this.prefixSums = new long[array.length + 1]; + this.prefixSums[0] = 0; + + for (int i = 0; i < array.length; i++) { + // Automatically promotes int to long during addition + this.prefixSums[i + 1] = this.prefixSums[i] + array[i]; + } + } + + /** + * Calculates the sum of elements in the range [left, right]. + * Indices are 0-based. + * + * @param left The starting index (inclusive). + * @param right The ending index (inclusive). + * @return The sum of elements from index left to right as a long. + * @throws IndexOutOfBoundsException if indices are out of valid range. + */ + public long sumRange(int left, int right) { + if (left < 0 || right >= prefixSums.length - 1 || left > right) { + throw new IndexOutOfBoundsException("Invalid range indices"); + } + return prefixSums[right + 1] - prefixSums[left]; + } +} diff --git a/src/main/java/com/thealgorithms/prefixsum/PrefixSum2D.java b/src/main/java/com/thealgorithms/prefixsum/PrefixSum2D.java new file mode 100644 index 000000000000..9c168bc6bcc4 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/PrefixSum2D.java @@ -0,0 +1,64 @@ +package com.thealgorithms.prefixsum; + +/** + * A class that implements the 2D Prefix Sum algorithm. + * + *

2D Prefix Sum is a technique used to preprocess a 2D matrix such that + * sub-matrix sum queries can be answered in O(1) time. + * The preprocessing step takes O(N*M) time. + * + *

This implementation uses a long array for the prefix sums to prevent + * integer overflow. + * + * @see Summed-area table (Wikipedia) + * @author Chahat Sandhu, singhc7 + */ +public class PrefixSum2D { + + private final long[][] prefixSums; + + /** + * Constructor to preprocess the input matrix. + * + * @param matrix The input integer matrix. + * @throws IllegalArgumentException if the matrix is null or empty. + */ + public PrefixSum2D(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + throw new IllegalArgumentException("Input matrix cannot be null or empty"); + } + + int rows = matrix.length; + int cols = matrix[0].length; + this.prefixSums = new long[rows + 1][cols + 1]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + // P[i+1][j+1] = current + above + left - diagonal_overlap + this.prefixSums[i + 1][j + 1] = matrix[i][j] + this.prefixSums[i][j + 1] + this.prefixSums[i + 1][j] - this.prefixSums[i][j]; + } + } + } + + /** + * Calculates the sum of the sub-matrix defined by (row1, col1) to (row2, col2). + * Indices are 0-based. + * + * @param row1 Top row index. + * @param col1 Left column index. + * @param row2 Bottom row index. + * @param col2 Right column index. + * @return The sum of the sub-matrix. + * @throws IndexOutOfBoundsException if indices are invalid. + */ + public long sumRegion(int row1, int col1, int row2, int col2) { + if (row1 < 0 || row2 >= prefixSums.length - 1 || row2 < row1) { + throw new IndexOutOfBoundsException("Invalid row indices"); + } + if (col1 < 0 || col2 >= prefixSums[0].length - 1 || col2 < col1) { + throw new IndexOutOfBoundsException("Invalid column indices"); + } + + return prefixSums[row2 + 1][col2 + 1] - prefixSums[row1][col2 + 1] - prefixSums[row2 + 1][col1] + prefixSums[row1][col1]; + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java b/src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java new file mode 100644 index 000000000000..87feff859356 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java @@ -0,0 +1,92 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PrefixSum2DTest { + + @Test + @DisplayName("Test basic 3x3 square matrix") + void testStandardSquare() { + int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Sum of top-left 2x2: {1,2, 4,5} -> 12 + assertEquals(12L, ps.sumRegion(0, 0, 1, 1)); + // Sum of bottom-right 2x2: {5,6, 8,9} -> 28 + assertEquals(28L, ps.sumRegion(1, 1, 2, 2)); + // Full matrix -> 45 + assertEquals(45L, ps.sumRegion(0, 0, 2, 2)); + } + + @Test + @DisplayName("Test rectangular matrix (more cols than rows)") + void testRectangularWide() { + int[][] matrix = {{1, 1, 1, 1}, {2, 2, 2, 2}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Sum of first 3 columns of both rows -> (1*3) + (2*3) = 9 + assertEquals(9L, ps.sumRegion(0, 0, 1, 2)); + } + + @Test + @DisplayName("Test rectangular matrix (more rows than cols)") + void testRectangularTall() { + int[][] matrix = {{1}, {2}, {3}, {4}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Sum of middle two elements -> 2+3 = 5 + assertEquals(5L, ps.sumRegion(1, 0, 2, 0)); + } + + @Test + @DisplayName("Test single element matrix") + void testSingleElement() { + int[][] matrix = {{100}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + assertEquals(100L, ps.sumRegion(0, 0, 0, 0)); + } + + @Test + @DisplayName("Test large numbers for overflow (Integer -> Long)") + void testLargeNumbers() { + // 2 billion. Two of these sum to > MAX_INT + int val = 2_000_000_000; + int[][] matrix = {{val, val}, {val, val}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // 4 * 2B = 8 Billion + assertEquals(8_000_000_000L, ps.sumRegion(0, 0, 1, 1)); + } + + @Test + @DisplayName("Test invalid inputs") + void testInvalidInputs() { + assertThrows(IllegalArgumentException.class, () -> new PrefixSum2D(null)); + assertThrows(IllegalArgumentException.class, () -> new PrefixSum2D(new int[][] {})); // empty + assertThrows(IllegalArgumentException.class, () -> new PrefixSum2D(new int[][] {{}})); // empty row + } + + @Test + @DisplayName("Test invalid query ranges") + void testInvalidRanges() { + int[][] matrix = {{1, 2}, {3, 4}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Negative indices + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(-1, 0, 0, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, -1, 0, 0)); + + // Out of bounds + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, 0, 2, 0)); // row2 too big + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, 0, 0, 2)); // col2 too big + + // Inverted ranges (start > end) + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(1, 0, 0, 0)); // row1 > row2 + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, 1, 0, 0)); // col1 > col2 + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java b/src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java new file mode 100644 index 000000000000..a421b62e9306 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PrefixSumTest { + + @Test + @DisplayName("Test basic sum with positive integers") + void testStandardCase() { + int[] input = {1, 2, 3, 4, 5}; + PrefixSum ps = new PrefixSum(input); + + // Sum of range [0, 4] -> 15 + assertEquals(15L, ps.sumRange(0, 4)); + + // Sum of range [1, 3] -> 9 + assertEquals(9L, ps.sumRange(1, 3)); + } + + @Test + @DisplayName("Test array with negative numbers and zeros") + void testNegativeAndZeros() { + int[] input = {-2, 0, 3, -5, 2, -1}; + PrefixSum ps = new PrefixSum(input); + + assertEquals(1L, ps.sumRange(0, 2)); + assertEquals(-1L, ps.sumRange(2, 5)); + assertEquals(0L, ps.sumRange(1, 1)); + } + + @Test + @DisplayName("Test with large integers to verify overflow handling") + void testLargeNumbers() { + // Two values that fit in int, but their sum exceeds Integer.MAX_VALUE + // Integer.MAX_VALUE is approx 2.14 billion. + int val = 2_000_000_000; + int[] input = {val, val, val}; + PrefixSum ps = new PrefixSum(input); + + // Sum of three 2 billion values is 6 billion (fits in long, overflows int) + assertEquals(6_000_000_000L, ps.sumRange(0, 2)); + } + + @Test + @DisplayName("Test single element array") + void testSingleElement() { + int[] input = {42}; + PrefixSum ps = new PrefixSum(input); + assertEquals(42L, ps.sumRange(0, 0)); + } + + @Test + @DisplayName("Test constructor with null input") + void testNullInput() { + assertThrows(IllegalArgumentException.class, () -> new PrefixSum(null)); + } + + @Test + @DisplayName("Test empty array behavior") + void testEmptyArray() { + int[] input = {}; + PrefixSum ps = new PrefixSum(input); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(0, 0)); + } + + @Test + @DisplayName("Test invalid range indices") + void testInvalidIndices() { + int[] input = {10, 20, 30}; + PrefixSum ps = new PrefixSum(input); + + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(-1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(0, 3)); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(2, 1)); + } +} From 7339b9dfe9f8dc1e516866c6e159bedbf8de2198 Mon Sep 17 00:00:00 2001 From: Gopesh Pandey Date: Tue, 20 Jan 2026 02:06:03 +0530 Subject: [PATCH 072/122] Add distance between two points algorithm (#7218) * Add distance between two points algorithm * Create DistanceBetweenTwoPointsTest.java * DistanceBetweenTwoPoints.java * Fix test file package and project structure * Delete src/test/java/com/thealgorithms/DistanceBetweenTwoPointsTest.java * Apply clang-format compliant formatting * Apply clang-format --------- Co-authored-by: a <19151554+alxkm@users.noreply.github.com> --- .../maths/DistanceBetweenTwoPoints.java | 33 +++++++++++++++++++ .../maths/DistanceBetweenTwoPointsTest.java | 23 +++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java create mode 100644 src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java diff --git a/src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java b/src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java new file mode 100644 index 000000000000..cd1c9205b328 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +/** + * Distance Between Two Points in 2D Space. + * + *

This class provides a method to calculate the Euclidean distance between two points in a + * two-dimensional plane. + * + *

Formula: d = sqrt((x2 - x1)^2 + (y2 - y1)^2) + * + *

Reference: https://en.wikipedia.org/wiki/Euclidean_distance + */ +public final class DistanceBetweenTwoPoints { + + private DistanceBetweenTwoPoints() { + // Utility class; prevent instantiation + } + + /** + * Calculate the Euclidean distance between two points. + * + * @param x1 x-coordinate of the first point + * @param y1 y-coordinate of the first point + * @param x2 x-coordinate of the second point + * @param y2 y-coordinate of the second point + * @return Euclidean distance between the two points + */ + public static double calculate(final double x1, final double y1, final double x2, final double y2) { + final double deltaX = x2 - x1; + final double deltaY = y2 - y1; + return Math.sqrt(deltaX * deltaX + deltaY * deltaY); + } +} diff --git a/src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java b/src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java new file mode 100644 index 000000000000..6bd124629740 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class DistanceBetweenTwoPointsTest { + + @Test + void testDistanceSimple() { + assertEquals(5.0, DistanceBetweenTwoPoints.calculate(0, 0, 3, 4), 1e-9); + } + + @Test + void testDistanceNegativeCoordinates() { + assertEquals(5.0, DistanceBetweenTwoPoints.calculate(-1, -1, 2, 3), 1e-9); + } + + @Test + void testSamePoint() { + assertEquals(0.0, DistanceBetweenTwoPoints.calculate(2, 2, 2, 2), 1e-9); + } +} From ba5ccbe0c74330c34e3b643f23828fbd78505d96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 12:38:12 +0000 Subject: [PATCH 073/122] chore(deps-dev): bump com.mebigfatguy.fb-contrib:fb-contrib from 7.7.3 to 7.7.4 (#7222) * chore(deps-dev): bump com.mebigfatguy.fb-contrib:fb-contrib Bumps [com.mebigfatguy.fb-contrib:fb-contrib](https://github.com/mebigfatguy/fb-contrib) from 7.7.3 to 7.7.4. - [Commits](https://github.com/mebigfatguy/fb-contrib/compare/v7.7.3...v7.7.4) --- updated-dependencies: - dependency-name: com.mebigfatguy.fb-contrib:fb-contrib dependency-version: 7.7.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * fix: supporess new warnings --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: vil02 <65706193+vil02@users.noreply.github.com> --- pom.xml | 2 +- spotbugs-exclude.xml | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a685e334460c..3f81e66d35c0 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.mebigfatguy.fb-contrib fb-contrib - 7.7.3 + 7.7.4 com.h3xstream.findsecbugs diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 3e2f1ff84ca8..9269e3a87e88 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -207,6 +207,27 @@ + + + + + + + + + + + + + + + + + + + + + From 1b014a2ea470ac41363443aaca90eec424411622 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Tue, 20 Jan 2026 22:02:10 +0100 Subject: [PATCH 074/122] style: include `UTAO_JUNIT_ASSERTION_ODDITIES_USE_ASSERT_NULL` (#7225) --- spotbugs-exclude.xml | 3 --- .../thealgorithms/ciphers/PermutationCipherTest.java | 5 +++-- .../thealgorithms/datastructures/trees/TreapTest.java | 3 ++- .../LongestCommonSubsequenceTest.java | 10 ++++------ 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 9269e3a87e88..f89bad8bebaf 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -222,9 +222,6 @@ - - - diff --git a/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java b/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java index 4ba6787cc97e..ecb7455c1ba2 100644 --- a/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java +++ b/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.ciphers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; @@ -121,8 +122,8 @@ void testNullString() { String decrypted = cipher.decrypt(encrypted, key); // then - assertEquals(null, encrypted); - assertEquals(null, decrypted); + assertNull(encrypted); + assertNull(decrypted); } @Test diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java index 09ada594faca..52b74a7a1faf 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ public void searchAndNotFound() { treap.insert(3); treap.insert(8); treap.insert(1); - assertEquals(null, treap.search(4)); + assertNull(treap.search(4)); } @Test diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java index 40bbdff15ca6..91169c4cc9d8 100644 --- a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.dynamicprogramming; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; @@ -55,27 +56,24 @@ public void testLCSWithBothEmptyStrings() { public void testLCSWithNullFirstString() { String str1 = null; String str2 = "XYZ"; - String expected = null; // Should return null if first string is null String result = LongestCommonSubsequence.getLCS(str1, str2); - assertEquals(expected, result); + assertNull(result); } @Test public void testLCSWithNullSecondString() { String str1 = "ABC"; String str2 = null; - String expected = null; // Should return null if second string is null String result = LongestCommonSubsequence.getLCS(str1, str2); - assertEquals(expected, result); + assertNull(result); } @Test public void testLCSWithNullBothStrings() { String str1 = null; String str2 = null; - String expected = null; // Should return null if both strings are null String result = LongestCommonSubsequence.getLCS(str1, str2); - assertEquals(expected, result); + assertNull(result); } @Test From 0e8291e66900b48e4be236121a63147e3dcf6b5f Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:55:09 +0100 Subject: [PATCH 075/122] style: include `UTAO_JUNIT_ASSERTION_ODDITIES_USE_ASSERT_NOT_NULL` (#7226) --- spotbugs-exclude.xml | 3 --- .../thealgorithms/datastructures/heaps/HeapElementTest.java | 3 ++- .../maths/LinearDiophantineEquationsSolverTest.java | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index f89bad8bebaf..410c1f8c5566 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -219,9 +219,6 @@ - - - diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java index d04a9de8a94b..792969200c82 100644 --- a/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java +++ b/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; @@ -39,7 +40,7 @@ void testEquals() { assertEquals(element1, element2); // Same key and info assertNotEquals(element1, element3); // Different key - assertNotEquals(null, element1); // Check for null + assertNotNull(element1); assertNotEquals("String", element1); // Check for different type } diff --git a/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java b/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java index c4205985dbfd..885382e29ca2 100644 --- a/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java +++ b/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java @@ -176,7 +176,7 @@ void testSolutionEquality() { assertEquals(solution1, solution2); assertNotEquals(solution3, solution1); assertEquals(solution1, solution1); - assertNotEquals(null, solution1); + assertNotNull(solution1); assertNotEquals("string", solution1); } @@ -217,7 +217,7 @@ void testGcdSolutionWrapperEquality() { assertEquals(wrapper1, wrapper2); assertNotEquals(wrapper3, wrapper1); assertEquals(wrapper1, wrapper1); - assertNotEquals(null, wrapper1); + assertNotNull(wrapper1); assertNotEquals("string", wrapper1); } From 0f9139dc42bd9beb86837bd854e7b13d46a961c8 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:24:45 +0100 Subject: [PATCH 076/122] style: include `UTAO_JUNIT_ASSERTION_ODDITIES_USE_ASSERT_NOT_EQUALS` (#7229) --- spotbugs-exclude.xml | 3 --- .../com/thealgorithms/maths/VolumeTest.java | 20 +++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 410c1f8c5566..c8a7f71cd880 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -210,9 +210,6 @@ - - - diff --git a/src/test/java/com/thealgorithms/maths/VolumeTest.java b/src/test/java/com/thealgorithms/maths/VolumeTest.java index 7cd0c6716147..1ba0aec47cef 100644 --- a/src/test/java/com/thealgorithms/maths/VolumeTest.java +++ b/src/test/java/com/thealgorithms/maths/VolumeTest.java @@ -1,6 +1,6 @@ package com.thealgorithms.maths; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; @@ -10,30 +10,30 @@ public class VolumeTest { public void volume() { /* test cube */ - assertTrue(Volume.volumeCube(7) == 343.0); + assertEquals(Volume.volumeCube(7), 343.0); /* test cuboid */ - assertTrue(Volume.volumeCuboid(2, 5, 7) == 70.0); + assertEquals(Volume.volumeCuboid(2, 5, 7), 70.0); /* test sphere */ - assertTrue(Volume.volumeSphere(7) == 1436.7550402417319); + assertEquals(Volume.volumeSphere(7), 1436.7550402417319); /* test cylinder */ - assertTrue(Volume.volumeCylinder(3, 7) == 197.92033717615698); + assertEquals(Volume.volumeCylinder(3, 7), 197.92033717615698); /* test hemisphere */ - assertTrue(Volume.volumeHemisphere(7) == 718.3775201208659); + assertEquals(Volume.volumeHemisphere(7), 718.3775201208659); /* test cone */ - assertTrue(Volume.volumeCone(3, 7) == 65.97344572538566); + assertEquals(Volume.volumeCone(3, 7), 65.97344572538566); /* test prism */ - assertTrue(Volume.volumePrism(10, 2) == 20.0); + assertEquals(Volume.volumePrism(10, 2), 20.0); /* test pyramid */ - assertTrue(Volume.volumePyramid(10, 3) == 10.0); + assertEquals(Volume.volumePyramid(10, 3), 10.0); /* test frustum */ - assertTrue(Volume.volumeFrustumOfCone(3, 5, 7) == 359.188760060433); + assertEquals(Volume.volumeFrustumOfCone(3, 5, 7), 359.188760060433); } } From a7eeee2b5b20153e47a015ff6e8e8cb66d1424a8 Mon Sep 17 00:00:00 2001 From: Mohammed Vijahath <116938255+vizahat36@users.noreply.github.com> Date: Thu, 22 Jan 2026 21:39:23 +0530 Subject: [PATCH 077/122] Fix: NumberFormatException with non-ASCII Unicode digits in MyAtoi (#7231) Fix myAtoi handling of non-ASCII Unicode digits --- src/main/java/com/thealgorithms/strings/MyAtoi.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/strings/MyAtoi.java b/src/main/java/com/thealgorithms/strings/MyAtoi.java index 5a7c2ce53b1c..92de4039a582 100644 --- a/src/main/java/com/thealgorithms/strings/MyAtoi.java +++ b/src/main/java/com/thealgorithms/strings/MyAtoi.java @@ -45,7 +45,9 @@ public static int myAtoi(String s) { int number = 0; while (index < length) { char ch = s.charAt(index); - if (!Character.isDigit(ch)) { + + // Accept only ASCII digits + if (ch < '0' || ch > '9') { break; } From 1a7f8fe79e81163cf4f9f6d82270edd6e39d1d82 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:39:22 +0100 Subject: [PATCH 078/122] style: include `UTAO_JUNIT_ASSERTION_ODDITIES_IMPOSSIBLE_NULL` (#7238) --- spotbugs-exclude.xml | 3 --- src/test/java/com/thealgorithms/compression/LZ78Test.java | 2 -- 2 files changed, 5 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index c8a7f71cd880..8c51fcf42b2e 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -216,9 +216,6 @@ - - - diff --git a/src/test/java/com/thealgorithms/compression/LZ78Test.java b/src/test/java/com/thealgorithms/compression/LZ78Test.java index 7889b50b76f3..da1fd8d23318 100644 --- a/src/test/java/com/thealgorithms/compression/LZ78Test.java +++ b/src/test/java/com/thealgorithms/compression/LZ78Test.java @@ -1,7 +1,6 @@ package com.thealgorithms.compression; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -286,7 +285,6 @@ void testTokenStructure() { // All tokens should have valid indices (>= 0) for (LZ78.Token token : compressed) { assertTrue(token.index() >= 0); - assertNotNull(token.nextChar()); } String decompressed = LZ78.decompress(compressed); From 0b0cefe9f9745ec94ffee3575b98aa953a8c8588 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:16:48 +0100 Subject: [PATCH 079/122] style: include `UTAO_JUNIT_ASSERTION_ODDITIES_ACTUAL_CONSTANT` (#7239) --- spotbugs-exclude.xml | 3 -- .../backtracking/PermutationTest.java | 4 +- .../com/thealgorithms/ciphers/ECCTest.java | 2 +- .../hashmap/hashing/MapTest.java | 6 +-- .../queues/PriorityQueuesTest.java | 28 ++++++------- .../thealgorithms/io/BufferedReaderTest.java | 40 +++++++++---------- .../maths/DistanceFormulaTest.java | 26 ++++++------ .../thealgorithms/maths/FactorialTest.java | 2 +- .../maths/NthUglyNumberTest.java | 8 ++-- .../maths/PalindromeNumberTest.java | 2 +- .../thealgorithms/maths/ParseIntegerTest.java | 4 +- .../maths/QuadraticEquationSolverTest.java | 20 +++++----- .../thealgorithms/maths/SecondMinMaxTest.java | 6 +-- .../maths/StandardDeviationTest.java | 8 ++-- .../maths/StandardScoreTest.java | 8 ++-- .../com/thealgorithms/maths/VolumeTest.java | 18 ++++----- .../misc/MedianOfRunningArrayTest.java | 2 +- .../searches/BinarySearch2dArrayTest.java | 6 +-- .../thealgorithms/searches/KMPSearchTest.java | 10 ++--- .../searches/QuickSelectTest.java | 2 +- .../sorts/TopologicalSortTest.java | 2 +- .../thealgorithms/strings/WordLadderTest.java | 6 +-- .../zigZagPattern/ZigZagPatternTest.java | 4 +- 23 files changed, 107 insertions(+), 110 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 8c51fcf42b2e..1390387bacdf 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -210,9 +210,6 @@ - - - diff --git a/src/test/java/com/thealgorithms/backtracking/PermutationTest.java b/src/test/java/com/thealgorithms/backtracking/PermutationTest.java index 76a714829109..54747e5e73a1 100644 --- a/src/test/java/com/thealgorithms/backtracking/PermutationTest.java +++ b/src/test/java/com/thealgorithms/backtracking/PermutationTest.java @@ -12,13 +12,13 @@ public class PermutationTest { @Test void testNoElement() { List result = Permutation.permutation(new Integer[] {}); - assertEquals(result.get(0).length, 0); + assertEquals(0, result.get(0).length); } @Test void testSingleElement() { List result = Permutation.permutation(new Integer[] {1}); - assertEquals(result.get(0)[0], 1); + assertEquals(1, result.get(0)[0]); } @Test diff --git a/src/test/java/com/thealgorithms/ciphers/ECCTest.java b/src/test/java/com/thealgorithms/ciphers/ECCTest.java index 701f801af1c8..b78ba51f7c3e 100644 --- a/src/test/java/com/thealgorithms/ciphers/ECCTest.java +++ b/src/test/java/com/thealgorithms/ciphers/ECCTest.java @@ -37,7 +37,7 @@ void testEncrypt() { System.out.println("Base Point G: " + curve.getBasePoint()); // Verify that the ciphertext is not empty - assertEquals(cipherText.length, 2); // Check if the ciphertext contains two points (R and S) + assertEquals(2, cipherText.length); // Check if the ciphertext contains two points (R and S) // Output the encrypted coordinate points System.out.println("Encrypted Points:"); diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java index 44551a8adac6..ef7739a2e8a9 100644 --- a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java @@ -81,19 +81,19 @@ void containsTest() { @Test void sizeTest() { Map map = getMap(); - assertEquals(map.size(), 0); + assertEquals(0, map.size()); for (int i = -100; i < 100; i++) { map.put(i, String.valueOf(i)); } - assertEquals(map.size(), 200); + assertEquals(200, map.size()); for (int i = -50; i < 50; i++) { map.delete(i); } - assertEquals(map.size(), 100); + assertEquals(100, map.size()); } @Test diff --git a/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java b/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java index e97fe091c556..3bb8bbabb761 100644 --- a/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java +++ b/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java @@ -9,14 +9,14 @@ class PriorityQueuesTest { void testPQInsertion() { PriorityQueue myQueue = new PriorityQueue(4); myQueue.insert(2); - Assertions.assertEquals(myQueue.peek(), 2); + Assertions.assertEquals(2, myQueue.peek()); myQueue.insert(5); myQueue.insert(3); - Assertions.assertEquals(myQueue.peek(), 5); + Assertions.assertEquals(5, myQueue.peek()); myQueue.insert(10); - Assertions.assertEquals(myQueue.peek(), 10); + Assertions.assertEquals(10, myQueue.peek()); } @Test @@ -28,32 +28,32 @@ void testPQDeletion() { myQueue.insert(10); myQueue.remove(); - Assertions.assertEquals(myQueue.peek(), 5); + Assertions.assertEquals(5, myQueue.peek()); myQueue.remove(); myQueue.remove(); - Assertions.assertEquals(myQueue.peek(), 2); + Assertions.assertEquals(2, myQueue.peek()); } @Test void testPQExtra() { PriorityQueue myQueue = new PriorityQueue(4); - Assertions.assertEquals(myQueue.isEmpty(), true); - Assertions.assertEquals(myQueue.isFull(), false); + Assertions.assertTrue(myQueue.isEmpty()); + Assertions.assertFalse(myQueue.isFull()); myQueue.insert(2); myQueue.insert(5); - Assertions.assertEquals(myQueue.isFull(), false); + Assertions.assertFalse(myQueue.isFull()); myQueue.insert(3); myQueue.insert(10); - Assertions.assertEquals(myQueue.isEmpty(), false); - Assertions.assertEquals(myQueue.isFull(), true); + Assertions.assertFalse(myQueue.isEmpty()); + Assertions.assertTrue(myQueue.isFull()); myQueue.remove(); - Assertions.assertEquals(myQueue.getSize(), 3); - Assertions.assertEquals(myQueue.peek(), 5); + Assertions.assertEquals(3, myQueue.getSize()); + Assertions.assertEquals(5, myQueue.peek()); myQueue.remove(); myQueue.remove(); - Assertions.assertEquals(myQueue.peek(), 2); - Assertions.assertEquals(myQueue.getSize(), 1); + Assertions.assertEquals(2, myQueue.peek()); + Assertions.assertEquals(1, myQueue.getSize()); } @Test diff --git a/src/test/java/com/thealgorithms/io/BufferedReaderTest.java b/src/test/java/com/thealgorithms/io/BufferedReaderTest.java index 891c3066058e..088e86f8f7c5 100644 --- a/src/test/java/com/thealgorithms/io/BufferedReaderTest.java +++ b/src/test/java/com/thealgorithms/io/BufferedReaderTest.java @@ -17,15 +17,15 @@ public void testPeeks() throws IOException { BufferedReader reader = new BufferedReader(input); // read the first letter - assertEquals(reader.read(), 'H'); + assertEquals('H', reader.read()); len--; - assertEquals(reader.available(), len); + assertEquals(len, reader.available()); // position: H[e]llo!\nWorld! // reader.read() will be == 'e' - assertEquals(reader.peek(1), 'l'); - assertEquals(reader.peek(2), 'l'); // second l - assertEquals(reader.peek(3), 'o'); + assertEquals('l', reader.peek(1)); + assertEquals('l', reader.peek(2)); // second l + assertEquals('o', reader.peek(3)); } @Test @@ -38,21 +38,21 @@ public void testMixes() throws IOException { BufferedReader reader = new BufferedReader(input); // read the first letter - assertEquals(reader.read(), 'H'); // first letter + assertEquals('H', reader.read()); // first letter len--; - assertEquals(reader.peek(1), 'l'); // third later (second letter after 'H') - assertEquals(reader.read(), 'e'); // second letter + assertEquals('l', reader.peek(1)); // third later (second letter after 'H') + assertEquals('e', reader.read()); // second letter len--; - assertEquals(reader.available(), len); + assertEquals(len, reader.available()); // position: H[e]llo!\nWorld! - assertEquals(reader.peek(2), 'o'); // second l - assertEquals(reader.peek(3), '!'); - assertEquals(reader.peek(4), '\n'); + assertEquals('o', reader.peek(2)); // second l + assertEquals('!', reader.peek(3)); + assertEquals('\n', reader.peek(4)); - assertEquals(reader.read(), 'l'); // third letter - assertEquals(reader.peek(1), 'o'); // fourth letter + assertEquals('l', reader.read()); // third letter + assertEquals('o', reader.peek(1)); // fourth letter for (int i = 0; i < 6; i++) { reader.read(); @@ -74,23 +74,23 @@ public void testBlockPractical() throws IOException { ByteArrayInputStream input = new ByteArrayInputStream(bytes); BufferedReader reader = new BufferedReader(input); - assertEquals(reader.peek(), 'H'); - assertEquals(reader.read(), '!'); // read the first letter + assertEquals('H', reader.peek()); + assertEquals('!', reader.read()); // read the first letter len--; // this only reads the next 5 bytes (Hello) because // the default buffer size = 5 - assertEquals(new String(reader.readBlock()), "Hello"); + assertEquals("Hello", new String(reader.readBlock())); len -= 5; assertEquals(reader.available(), len); // maybe kind of a practical demonstration / use case if (reader.read() == '\n') { - assertEquals(reader.read(), 'W'); - assertEquals(reader.read(), 'o'); + assertEquals('W', reader.read()); + assertEquals('o', reader.read()); // the rest of the blocks - assertEquals(new String(reader.readBlock()), "rld!"); + assertEquals("rld!", new String(reader.readBlock())); } else { // should not reach throw new IOException("Something not right"); diff --git a/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java b/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java index 3a14b80dd4f9..66f3b7b03938 100644 --- a/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java +++ b/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java @@ -9,78 +9,78 @@ public class DistanceFormulaTest { @Test void euclideanTest1() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(1, 1, 2, 2), 1.4142135623730951); + Assertions.assertEquals(1.4142135623730951, DistanceFormula.euclideanDistance(1, 1, 2, 2)); } @Test void euclideanTest2() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(1, 3, 8, 0), 7.0710678118654755); + Assertions.assertEquals(7.0710678118654755, DistanceFormula.euclideanDistance(1, 3, 8, 0)); } @Test void euclideanTest3() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(2.4, 9.1, 55.1, 100), 110.91911467371168); + Assertions.assertEquals(110.91911467371168, DistanceFormula.euclideanDistance(2.4, 9.1, 55.1, 100)); } @Test void euclideanTest4() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(1000, 13, 20000, 84), 19022.067605809836); + Assertions.assertEquals(19022.067605809836, DistanceFormula.euclideanDistance(1000, 13, 20000, 84)); } @Test public void manhattantest1() { - assertEquals(DistanceFormula.manhattanDistance(1, 2, 3, 4), 4); + assertEquals(4, DistanceFormula.manhattanDistance(1, 2, 3, 4)); } @Test public void manhattantest2() { - assertEquals(DistanceFormula.manhattanDistance(6.5, 8.4, 20.1, 13.6), 18.8); + assertEquals(18.8, DistanceFormula.manhattanDistance(6.5, 8.4, 20.1, 13.6)); } @Test public void manhattanTest3() { - assertEquals(DistanceFormula.manhattanDistance(10.112, 50, 8, 25.67), 26.442); + assertEquals(26.442, DistanceFormula.manhattanDistance(10.112, 50, 8, 25.67)); } @Test public void hammingTest1() { int[] array1 = {1, 1, 1, 1}; int[] array2 = {0, 0, 0, 0}; - assertEquals(DistanceFormula.hammingDistance(array1, array2), 4); + assertEquals(4, DistanceFormula.hammingDistance(array1, array2)); } @Test public void hammingTest2() { int[] array1 = {1, 1, 1, 1}; int[] array2 = {1, 1, 1, 1}; - assertEquals(DistanceFormula.hammingDistance(array1, array2), 0); + assertEquals(0, DistanceFormula.hammingDistance(array1, array2)); } @Test public void hammingTest3() { int[] array1 = {1, 0, 0, 1, 1, 0, 1, 1, 0}; int[] array2 = {0, 1, 0, 0, 1, 1, 1, 0, 0}; - assertEquals(DistanceFormula.hammingDistance(array1, array2), 5); + assertEquals(5, DistanceFormula.hammingDistance(array1, array2)); } @Test public void minkowskiTest1() { double[] array1 = {1, 3, 8, 5}; double[] array2 = {4, 2, 6, 9}; - assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 1), 10); + assertEquals(10, DistanceFormula.minkowskiDistance(array1, array2, 1)); } @Test public void minkowskiTest2() { double[] array1 = {1, 3, 8, 5}; double[] array2 = {4, 2, 6, 9}; - assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 2), 5.477225575051661); + assertEquals(5.477225575051661, DistanceFormula.minkowskiDistance(array1, array2, 2)); } @Test public void minkowskiTest3() { double[] array1 = {1, 3, 8, 5}; double[] array2 = {4, 2, 6, 9}; - assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 3), 4.641588833612778); + assertEquals(4.641588833612778, DistanceFormula.minkowskiDistance(array1, array2, 3)); } } diff --git a/src/test/java/com/thealgorithms/maths/FactorialTest.java b/src/test/java/com/thealgorithms/maths/FactorialTest.java index b38dc45589ee..3ff7097b8113 100644 --- a/src/test/java/com/thealgorithms/maths/FactorialTest.java +++ b/src/test/java/com/thealgorithms/maths/FactorialTest.java @@ -11,7 +11,7 @@ public class FactorialTest { @Test public void testWhenInvalidInoutProvidedShouldThrowException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> Factorial.factorial(-1)); - assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); + assertEquals(EXCEPTION_MESSAGE, exception.getMessage()); } @Test diff --git a/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java b/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java index 3fe58dadf8a5..1ee437b190c5 100644 --- a/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java +++ b/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java @@ -48,22 +48,22 @@ public void testGetWithSameObject() { var uglyNumbers = new NthUglyNumber(new int[] {7, 2, 5, 3}); for (final var tc : testCases.entrySet()) { - assertEquals(uglyNumbers.get(tc.getKey()), tc.getValue()); + assertEquals(tc.getValue(), uglyNumbers.get(tc.getKey())); } - assertEquals(uglyNumbers.get(999), 385875); + assertEquals(385875, uglyNumbers.get(999)); } @Test public void testGetWithBase1() { var uglyNumbers = new NthUglyNumber(new int[] {1}); - assertEquals(uglyNumbers.get(10), 1); + assertEquals(1, uglyNumbers.get(10)); } @Test public void testGetWithBase2() { var uglyNumbers = new NthUglyNumber(new int[] {2}); - assertEquals(uglyNumbers.get(5), 32); + assertEquals(32, uglyNumbers.get(5)); } @Test diff --git a/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java b/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java index a70100c0b913..4e4bd85d07b5 100644 --- a/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java +++ b/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java @@ -25,6 +25,6 @@ public void testNumbersAreNotPalindromes() { @Test public void testIfNegativeInputThenExceptionExpected() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> PalindromeNumber.isPalindrome(-1)); - Assertions.assertEquals(exception.getMessage(), "Input parameter must not be negative!"); + Assertions.assertEquals("Input parameter must not be negative!", exception.getMessage()); } } diff --git a/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java b/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java index 7649e21eb231..a9b78be88042 100644 --- a/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java +++ b/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java @@ -14,13 +14,13 @@ public class ParseIntegerTest { @Test public void testNullInput() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> ParseInteger.parseInt(null)); - Assertions.assertEquals(exception.getMessage(), NULL_PARAMETER_MESSAGE); + Assertions.assertEquals(NULL_PARAMETER_MESSAGE, exception.getMessage()); } @Test public void testEmptyInput() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> ParseInteger.parseInt("")); - Assertions.assertEquals(exception.getMessage(), EMPTY_PARAMETER_MESSAGE); + Assertions.assertEquals(EMPTY_PARAMETER_MESSAGE, exception.getMessage()); } @Test diff --git a/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java b/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java index a2046511ddf5..a6552d56783c 100644 --- a/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java +++ b/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java @@ -14,10 +14,10 @@ public void testSolveEquationRealRoots() { double c = 1.9; ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); - Assertions.assertEquals(roots.length, 2); - Assertions.assertEquals(roots[0].real, -0.27810465435684306); + Assertions.assertEquals(2, roots.length, 2); + Assertions.assertEquals(-0.27810465435684306, roots[0].real); Assertions.assertNull(roots[0].imaginary); - Assertions.assertEquals(roots[1].real, -1.6266572504050616); + Assertions.assertEquals(-1.6266572504050616, roots[1].real); Assertions.assertNull(roots[1].imaginary); } @@ -29,8 +29,8 @@ public void testSolveEquationEqualRoots() { double c = 1; ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); - Assertions.assertEquals(roots.length, 1); - Assertions.assertEquals(roots[0].real, -1); + Assertions.assertEquals(1, roots.length); + Assertions.assertEquals(-1, roots[0].real); } @Test @@ -41,10 +41,10 @@ public void testSolveEquationComplexRoots() { double c = 5.6; ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); - Assertions.assertEquals(roots.length, 2); - Assertions.assertEquals(roots[0].real, -0.8695652173913044); - Assertions.assertEquals(roots[0].imaginary, 1.2956229935435948); - Assertions.assertEquals(roots[1].real, -0.8695652173913044); - Assertions.assertEquals(roots[1].imaginary, -1.2956229935435948); + Assertions.assertEquals(2, roots.length); + Assertions.assertEquals(-0.8695652173913044, roots[0].real); + Assertions.assertEquals(1.2956229935435948, roots[0].imaginary); + Assertions.assertEquals(-0.8695652173913044, roots[1].real); + Assertions.assertEquals(-1.2956229935435948, roots[1].imaginary); } } diff --git a/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java b/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java index c744614e5cfa..c5d47f2213a9 100644 --- a/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java +++ b/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java @@ -29,19 +29,19 @@ public TestCase(final int[] inInputArray, final int inSecondMin, final int inSec @Test public void testForEmptyInputArray() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMin(new int[] {})); - assertEquals(exception.getMessage(), EXP_MSG_ARR_LEN_LESS_2); + assertEquals(EXP_MSG_ARR_LEN_LESS_2, exception.getMessage()); } @Test public void testForArrayWithSingleElement() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMax(new int[] {1})); - assertEquals(exception.getMessage(), EXP_MSG_ARR_LEN_LESS_2); + assertEquals(EXP_MSG_ARR_LEN_LESS_2, exception.getMessage()); } @Test public void testForArrayWithSameElements() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMin(new int[] {1, 1, 1, 1})); - assertEquals(exception.getMessage(), EXP_MSG_ARR_SAME_ELE); + assertEquals(EXP_MSG_ARR_SAME_ELE, exception.getMessage()); } @ParameterizedTest diff --git a/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java b/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java index 2c10d2d14f3e..4716d389a4ca 100644 --- a/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java +++ b/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java @@ -8,19 +8,19 @@ public class StandardDeviationTest { @Test void test1() { double[] t1 = new double[] {1, 1, 1, 1, 1}; - Assertions.assertEquals(StandardDeviation.stdDev(t1), 0.0); + Assertions.assertEquals(0.0, StandardDeviation.stdDev(t1)); } @Test void test2() { double[] t2 = new double[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - Assertions.assertEquals(StandardDeviation.stdDev(t2), 2.8722813232690143); + Assertions.assertEquals(2.8722813232690143, StandardDeviation.stdDev(t2)); } @Test void test3() { double[] t3 = new double[] {1.1, 8.5, 20.3, 2.4, 6.2}; - Assertions.assertEquals(StandardDeviation.stdDev(t3), 6.8308125431752265); + Assertions.assertEquals(6.8308125431752265, StandardDeviation.stdDev(t3)); } @Test @@ -32,6 +32,6 @@ void test4() { 100.00045, 56.7, }; - Assertions.assertEquals(StandardDeviation.stdDev(t4), 38.506117353865775); + Assertions.assertEquals(38.506117353865775, StandardDeviation.stdDev(t4)); } } diff --git a/src/test/java/com/thealgorithms/maths/StandardScoreTest.java b/src/test/java/com/thealgorithms/maths/StandardScoreTest.java index 436b1fd011c6..6858b87ad2c6 100644 --- a/src/test/java/com/thealgorithms/maths/StandardScoreTest.java +++ b/src/test/java/com/thealgorithms/maths/StandardScoreTest.java @@ -7,21 +7,21 @@ public class StandardScoreTest { @Test void test1() { - Assertions.assertEquals(StandardScore.zScore(2, 0, 5), 0.4); + Assertions.assertEquals(0.4, StandardScore.zScore(2, 0, 5)); } @Test void test2() { - Assertions.assertEquals(StandardScore.zScore(1, 1, 1), 0.0); + Assertions.assertEquals(0.0, StandardScore.zScore(1, 1, 1)); } @Test void test3() { - Assertions.assertEquals(StandardScore.zScore(2.5, 1.8, 0.7), 1.0); + Assertions.assertEquals(1.0, StandardScore.zScore(2.5, 1.8, 0.7)); } @Test void test4() { - Assertions.assertEquals(StandardScore.zScore(8.9, 3, 4.2), 1.4047619047619049); + Assertions.assertEquals(1.4047619047619049, StandardScore.zScore(8.9, 3, 4.2)); } } diff --git a/src/test/java/com/thealgorithms/maths/VolumeTest.java b/src/test/java/com/thealgorithms/maths/VolumeTest.java index 1ba0aec47cef..af882eef7563 100644 --- a/src/test/java/com/thealgorithms/maths/VolumeTest.java +++ b/src/test/java/com/thealgorithms/maths/VolumeTest.java @@ -10,30 +10,30 @@ public class VolumeTest { public void volume() { /* test cube */ - assertEquals(Volume.volumeCube(7), 343.0); + assertEquals(343.0, Volume.volumeCube(7)); /* test cuboid */ - assertEquals(Volume.volumeCuboid(2, 5, 7), 70.0); + assertEquals(70.0, Volume.volumeCuboid(2, 5, 7)); /* test sphere */ - assertEquals(Volume.volumeSphere(7), 1436.7550402417319); + assertEquals(1436.7550402417319, Volume.volumeSphere(7)); /* test cylinder */ - assertEquals(Volume.volumeCylinder(3, 7), 197.92033717615698); + assertEquals(197.92033717615698, Volume.volumeCylinder(3, 7)); /* test hemisphere */ - assertEquals(Volume.volumeHemisphere(7), 718.3775201208659); + assertEquals(718.3775201208659, Volume.volumeHemisphere(7)); /* test cone */ - assertEquals(Volume.volumeCone(3, 7), 65.97344572538566); + assertEquals(65.97344572538566, Volume.volumeCone(3, 7)); /* test prism */ - assertEquals(Volume.volumePrism(10, 2), 20.0); + assertEquals(20.0, Volume.volumePrism(10, 2)); /* test pyramid */ - assertEquals(Volume.volumePyramid(10, 3), 10.0); + assertEquals(10.0, Volume.volumePyramid(10, 3)); /* test frustum */ - assertEquals(Volume.volumeFrustumOfCone(3, 5, 7), 359.188760060433); + assertEquals(359.188760060433, Volume.volumeFrustumOfCone(3, 5, 7)); } } diff --git a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java index f41953035846..c4a74af0ba8b 100644 --- a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java +++ b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java @@ -17,7 +17,7 @@ public class MedianOfRunningArrayTest { public void testWhenInvalidInoutProvidedShouldThrowException() { var stream = new MedianOfRunningArrayInteger(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, stream::getMedian); - assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); + assertEquals(EXCEPTION_MESSAGE, exception.getMessage()); } @Test diff --git a/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java b/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java index 18f0afc6a0a6..dec2c86de9c7 100644 --- a/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java +++ b/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java @@ -117,7 +117,7 @@ public void binarySearch2dArrayTestTargetInMiddle() { int target = 8; // Assert that the requirement, that the target is in the middle row and middle column, is // fulfilled. - assertEquals(arr[arr.length / 2][arr[0].length / 2], target); + assertEquals(target, arr[arr.length / 2][arr[0].length / 2]); int[] ans = BinarySearch2dArray.binarySearch(arr, target); System.out.println(Arrays.toString(ans)); assertEquals(1, ans[0]); @@ -135,8 +135,8 @@ public void binarySearch2dArrayTestTargetAboveMiddleRowInMiddleColumn() { // Assert that the requirement, that he target is in the middle column, // in an array with an even number of columns, and on the row "above" the middle row. - assertEquals(arr[0].length % 2, 0); - assertEquals(arr[arr.length / 2 - 1][arr[0].length / 2], target); + assertEquals(0, arr[0].length % 2); + assertEquals(target, arr[arr.length / 2 - 1][arr[0].length / 2]); int[] ans = BinarySearch2dArray.binarySearch(arr, target); System.out.println(Arrays.toString(ans)); assertEquals(0, ans[0]); diff --git a/src/test/java/com/thealgorithms/searches/KMPSearchTest.java b/src/test/java/com/thealgorithms/searches/KMPSearchTest.java index cb804ac6a6a3..216c5fcd7d2c 100644 --- a/src/test/java/com/thealgorithms/searches/KMPSearchTest.java +++ b/src/test/java/com/thealgorithms/searches/KMPSearchTest.java @@ -14,7 +14,7 @@ public void kmpSearchTestLast() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 10); + assertEquals(10, value); } @Test @@ -25,7 +25,7 @@ public void kmpSearchTestFront() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 0); + assertEquals(0, value); } @Test @@ -36,7 +36,7 @@ public void kmpSearchTestMiddle() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 4); + assertEquals(4, value); } @Test @@ -47,7 +47,7 @@ public void kmpSearchTestNotFound() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 4); + assertEquals(4, value); } @Test @@ -58,6 +58,6 @@ public void kmpSearchTest4() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, -1); + assertEquals(-1, value); } } diff --git a/src/test/java/com/thealgorithms/searches/QuickSelectTest.java b/src/test/java/com/thealgorithms/searches/QuickSelectTest.java index cf160b0ff4b5..4c96be76861a 100644 --- a/src/test/java/com/thealgorithms/searches/QuickSelectTest.java +++ b/src/test/java/com/thealgorithms/searches/QuickSelectTest.java @@ -172,7 +172,7 @@ void quickSelect70thPercentileOfManyElements() { void quickSelectMedianOfThreeCharacters() { List elements = Arrays.asList('X', 'Z', 'Y'); char actual = QuickSelect.select(elements, 1); - assertEquals(actual, 'Y'); + assertEquals('Y', actual); } @Test diff --git a/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java b/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java index d5588b2b968e..e19f5b928263 100644 --- a/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java +++ b/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java @@ -58,7 +58,7 @@ public void failureTest() { Exception exception = assertThrows(RuntimeException.class, () -> TopologicalSort.sort(graph)); String expected = "This graph contains a cycle. No linear ordering is possible. " + "Back edge: 6 -> 2"; - assertEquals(exception.getMessage(), expected); + assertEquals(expected, exception.getMessage()); } @Test void testEmptyGraph() { diff --git a/src/test/java/com/thealgorithms/strings/WordLadderTest.java b/src/test/java/com/thealgorithms/strings/WordLadderTest.java index 221953411da7..c029940abfb0 100644 --- a/src/test/java/com/thealgorithms/strings/WordLadderTest.java +++ b/src/test/java/com/thealgorithms/strings/WordLadderTest.java @@ -24,7 +24,7 @@ public class WordLadderTest { public void testWordLadder() { List wordList1 = Arrays.asList("hot", "dot", "dog", "lot", "log", "cog"); - assertEquals(WordLadder.ladderLength("hit", "cog", wordList1), 5); + assertEquals(5, WordLadder.ladderLength("hit", "cog", wordList1)); } /** @@ -39,7 +39,7 @@ public void testWordLadder() { public void testWordLadder2() { List wordList2 = Arrays.asList("hot", "dot", "dog", "lot", "log"); - assertEquals(WordLadder.ladderLength("hit", "cog", wordList2), 0); + assertEquals(0, WordLadder.ladderLength("hit", "cog", wordList2)); } /** @@ -54,7 +54,7 @@ public void testWordLadder2() { public void testWordLadder3() { List wordList3 = emptyList(); - assertEquals(WordLadder.ladderLength("hit", "cog", wordList3), 0); + assertEquals(0, WordLadder.ladderLength("hit", "cog", wordList3)); } @ParameterizedTest diff --git a/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java b/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java index 2cbbfe3d2dd8..9bf118c9b844 100644 --- a/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java +++ b/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java @@ -9,8 +9,8 @@ public class ZigZagPatternTest { public void testZigZagPattern() { String input1 = "HelloWorldFromJava"; String input2 = "javaIsAProgrammingLanguage"; - Assertions.assertEquals(ZigZagPattern.encode(input1, 4), "HooeWrrmalolFJvlda"); - Assertions.assertEquals(ZigZagPattern.encode(input2, 4), "jAaLgasPrmgaaevIrgmnnuaoig"); + Assertions.assertEquals("HooeWrrmalolFJvlda", ZigZagPattern.encode(input1, 4)); + Assertions.assertEquals("jAaLgasPrmgaaevIrgmnnuaoig", ZigZagPattern.encode(input2, 4)); // Edge cases Assertions.assertEquals("ABC", ZigZagPattern.encode("ABC", 1)); // Single row Assertions.assertEquals("A", ZigZagPattern.encode("A", 2)); // numRows > length of string From 6fdf2db2989b2cea5ecbf27953f24ff6ac461f82 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:44:41 +0100 Subject: [PATCH 080/122] style: resolve some of the `UTAO_JUNIT_ASSERTION_ODDITIES_USE_ASSERT_EQUALS` warnings (#7240) --- .../com/thealgorithms/backtracking/CombinationTest.java | 8 ++++---- .../java/com/thealgorithms/misc/ShuffleArrayTest.java | 3 ++- .../java/com/thealgorithms/others/PasswordGenTest.java | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/thealgorithms/backtracking/CombinationTest.java b/src/test/java/com/thealgorithms/backtracking/CombinationTest.java index a9d1163f3ecd..5d2f99ccadf8 100644 --- a/src/test/java/com/thealgorithms/backtracking/CombinationTest.java +++ b/src/test/java/com/thealgorithms/backtracking/CombinationTest.java @@ -28,16 +28,16 @@ void testNoElement() { @Test void testLengthOne() { List> result = Combination.combination(new Integer[] {1, 2}, 1); - assertTrue(result.get(0).iterator().next() == 1); - assertTrue(result.get(1).iterator().next() == 2); + assertEquals(1, result.get(0).iterator().next()); + assertEquals(2, result.get(1).iterator().next()); } @Test void testLengthTwo() { List> result = Combination.combination(new Integer[] {1, 2}, 2); Integer[] arr = result.get(0).toArray(new Integer[2]); - assertTrue(arr[0] == 1); - assertTrue(arr[1] == 2); + assertEquals(1, arr[0]); + assertEquals(2, arr[1]); } @Test diff --git a/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java b/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java index 915b83e376b6..c1adafa18d9f 100644 --- a/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java +++ b/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.misc; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -67,7 +68,7 @@ void testShuffleRetainsElements() { ShuffleArray.shuffle(arr); // Check that the shuffled array contains the same elements - assertTrue(arr.length == 5); + assertEquals(5, arr.length); for (int i = 1; i <= 5; i++) { assertTrue(contains(arr, i)); } diff --git a/src/test/java/com/thealgorithms/others/PasswordGenTest.java b/src/test/java/com/thealgorithms/others/PasswordGenTest.java index 76492556e75f..4dcdf6b9cf4f 100644 --- a/src/test/java/com/thealgorithms/others/PasswordGenTest.java +++ b/src/test/java/com/thealgorithms/others/PasswordGenTest.java @@ -17,7 +17,7 @@ public void failGenerationWithSameMinMaxLengthTest() { @Test public void generateOneCharacterPassword() { String tempPassword = PasswordGen.generatePassword(1, 2); - assertTrue(tempPassword.length() == 1); + assertEquals(1, tempPassword.length()); } @Test From a3efc108b8970adfe71baef44510b1f5552ca342 Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Mon, 26 Jan 2026 22:33:04 +0100 Subject: [PATCH 081/122] Docs/remove readme korean readme file (#7242) * fix: prevent duplicate auth header in GitHub Actions workflow * chore: remove Korean README file --- .github/workflows/update-directorymd.yml | 2 +- README-ko.md | 191 ----------------------- 2 files changed, 1 insertion(+), 192 deletions(-) delete mode 100644 README-ko.md diff --git a/.github/workflows/update-directorymd.yml b/.github/workflows/update-directorymd.yml index aa553b46a23b..1cfee6e36e4e 100644 --- a/.github/workflows/update-directorymd.yml +++ b/.github/workflows/update-directorymd.yml @@ -1,4 +1,4 @@ -name: Generate Directory Markdown +ο»Ώname: Generate Directory Markdown on: push: diff --git a/README-ko.md b/README-ko.md deleted file mode 100644 index 4f8cab92fc42..000000000000 --- a/README-ko.md +++ /dev/null @@ -1,191 +0,0 @@ -# μ•Œκ³ λ¦¬μ¦˜ - μžλ°” - -## 이 [개발브런치](https://github.com/TheAlgorithms/Java/tree/Development)λŠ” κΈ°μ‘΄ ν”„λ‘œμ νŠΈλ₯Ό Java ν”„λ‘œμ νŠΈ ꡬ쑰둜 μž¬κ°œλ°œν•˜κΈ° μœ„ν•΄ μž‘μ„±λ˜μ—ˆλ‹€. 기여도λ₯Ό μœ„ν•΄ 개발 μ§€μ‚¬λ‘œ μ „ν™˜ν•  수 μžˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ 이 문제λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€. μ»¨νŠΈλ¦¬λ·°μ…˜μ„ μœ„ν•΄ [개발브런치](https://github.com/TheAlgorithms/Java/tree/Development)둜 μ „ν™˜ν•  수 μžˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ [이 이슈](https://github.com/TheAlgorithms/Java/issues/474)λ₯Ό μ°Έκ³ ν•˜μ‹­μ‹œμ˜€. - -### μžλ°”λ‘œ κ΅¬ν˜„λœ λͺ¨λ“  μ•Œκ³ λ¦¬μ¦˜λ“€ (ꡐ윑용) - -이것듀은 단지 μ‹œλ²”μ„ μœ„ν•œ 것이닀. ν‘œμ€€ μžλ°” λΌμ΄λΈŒλŸ¬λ¦¬μ—λŠ” μ„±λŠ₯μƒμ˜ 이유둜 더 λ‚˜μ€ 것듀이 κ΅¬ν˜„λ˜μ–΄μžˆλ‹€ - -## μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ - -### Bubble(버블 μ •λ ¬) - -![alt text][bubble-image] - -From [Wikipedia][bubble-wiki]: 버블 μ†ŒνŠΈ(sinking sor라고도 λΆˆλ¦¬μ›€)λŠ” 리슀트λ₯Ό 반볡적인 λ‹¨κ³„λ‘œ μ ‘κ·Όν•˜μ—¬ μ •λ ¬ν•œλ‹€. 각각의 짝을 λΉ„κ΅ν•˜λ©°, μˆœμ„œκ°€ 잘λͺ»λœ 경우 κ·Έμ ‘ν•œ μ•„μ΄ν…œλ“€μ„ μŠ€μ™‘ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. 더 이상 μŠ€μ™‘ν•  것이 없을 λ•ŒκΉŒμ§€ λ°˜λ³΅ν•˜λ©°, 반볡이 λλ‚¨μŒ λ¦¬μŠ€νŠΈκ°€ μ •λ ¬λ˜μ—ˆμŒμ„ μ˜λ―Έν•œλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n) -- 평균 μ„±λŠ₯ O(n^2) - -###### View the algorithm in [action][bubble-toptal] - -### Insertion(μ‚½μž… μ •λ ¬) - -![alt text][insertion-image] - -From [Wikipedia][insertion-wiki]: μ‚½μž… 정렬은 μ΅œμ’… μ •λ ¬λœ λ°°μ—΄(λ˜λŠ” 리슀트)을 ν•œλ²ˆμ— ν•˜λ‚˜μ”© κ΅¬μΆ•ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. 이것은 큰 λ¦¬μŠ€νŠΈμ—μ„œ 더 λ‚˜μ€ μ•Œκ³ λ¦¬μ¦˜μΈ 퀡 μ†ŒνŠΈ, νž™ μ†ŒνŠΈ, λ˜λŠ” λ¨Έμ§€ μ†ŒνŠΈλ³΄λ‹€ 훨씬 μ•ˆμ’‹μ€ νš¨μœ¨μ„ κ°€μ§„λ‹€. κ·Έλ¦Όμ—μ„œ 각 λ§‰λŒ€λŠ” μ •λ ¬ν•΄μ•Ό ν•˜λŠ” λ°°μ—΄μ˜ μš”μ†Œλ₯Ό λ‚˜νƒ€λ‚Έλ‹€. 상단과 두 번째 상단 λ§‰λŒ€μ˜ 첫 번째 κ΅μ°¨μ μ—μ„œ λ°œμƒν•˜λŠ” 것은 두 번째 μš”μ†Œκ°€ 첫 번째 μš”μ†Œλ³΄λ‹€ 더 높은 μš°μ„  μˆœμœ„λ₯Ό κ°€μ§€κΈ° λ•Œλ¬Έμ— λ§‰λŒ€λ‘œ ν‘œμ‹œλ˜λŠ” μ΄λŸ¬ν•œ μš”μ†Œλ₯Ό κ΅ν™˜ν•œ 것이닀. 이 방법을 λ°˜λ³΅ν•˜λ©΄ μ‚½μž… 정렬이 μ™„λ£Œλœλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n) -- 평균 O(n^2) - -###### View the algorithm in [action][insertion-toptal] - -### Merge(합병 μ •λ ¬) - -![alt text][merge-image] - -From [Wikipedia][merge-wiki]: 컴퓨터 κ³Όν•™μ—μ„œ, 합병 정렬은 효율적인, λ²”μš©μ μΈ, 비ꡐ 기반 μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. λŒ€λΆ€λΆ„μ˜ κ΅¬ν˜„μ€ μ•ˆμ •μ μΈ λΆ„λ₯˜λ₯Ό μ΄λ£¨λŠ”λ°, 이것은 κ΅¬ν˜„μ΄ μ •λ ¬λœ 좜λ ₯에 λ™μΌν•œ μš”μ†Œμ˜ μž…λ ₯ μˆœμ„œλ₯Ό μœ μ§€ν•œλ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€. 합병 정렬은 1945년에 John von Neumann이 발λͺ…ν•œ λΆ„ν•  정볡 μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n log n) (일반적) -- 졜고의 μ„±λŠ₯ O(n log n) -- 평균 O(n log n) - -###### View the algorithm in [action][merge-toptal] - -### Quick(퀡 μ •λ ¬) - -![alt text][quick-image] - -From [Wikipedia][quick-wiki]: 퀡 μ •λ ¬sometimes called partition-exchange sort)은 효율적인 μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ, λ°°μ—΄μ˜ μš”μ†Œλ₯Ό μˆœμ„œλŒ€λ‘œ μ •λ ¬ν•˜λŠ” 체계적인 방법 μ—­ν™œμ„ ν•œλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n log n) or O(n) with three-way partition -- 평균 O(n log n) - -###### View the algorithm in [action][quick-toptal] - -### Selection(선택 μ •λ ¬) - -![alt text][selection-image] - -From [Wikipedia][selection-wiki]: μ•Œκ³ λ¦¬μ¦˜ μž…λ ₯ 리슀트λ₯Ό 두 λΆ€λΆ„μœΌλ‘œ λ‚˜λˆˆλ‹€ : 첫 뢀뢄은 μ•„μ΄ν…œλ“€μ΄ 이미 μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ μ •λ ¬λ˜μ—ˆλ‹€. 그리고 남은 λΆ€λΆ„μ˜ μ•„μ΄ν…œλ“€μ€ λ‚˜λ¨Έμ§€ ν•­λͺ©μ„ μ°¨μ§€ν•˜λŠ” λ¦¬μŠ€νŠΈμ΄λ‹€. μ²˜μŒμ—λŠ” μ •λ ¬λœ λ¦¬μŠ€νŠΈλŠ” 곡백이고 λ‚˜λ¨Έμ§€κ°€ 전뢀이닀. 였λ₯΄μ°¨μˆœ(λ˜λŠ” λ‚΄λ¦Όμ°¨μˆœ) μ•Œκ³ λ¦¬μ¦˜μ€ κ°€μž₯ μž‘μ€ μš”μ†Œλ₯Ό μ •λ ¬λ˜μ§€ μ•Šμ€ λ¦¬μŠ€νŠΈμ—μ„œ μ°Ύκ³  정렬이 μ•ˆλœ κ°€μž₯ μ™Όμͺ½(μ •λ ¬λœ 리슀트) λ¦¬μŠ€νŠΈμ™€ λ°”κΎΌλ‹€. μ΄λ ‡κ²Œ 였λ₯Έμͺ½μœΌλ‘œ λ‚˜μ•„κ°„λ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n^2) -- 평균 O(n^2) - -###### View the algorithm in [action][selection-toptal] - -### Shell(μ‰˜ μ •λ ¬) - -![alt text][shell-image] - -From [Wikipedia][shell-wiki]: μ‰˜ 정렬은 멀리 λ–¨μ–΄μ Έ μžˆλŠ” ν•­λͺ©μ˜ κ΅ν™˜μ„ ν—ˆμš©ν•˜λŠ” μ‚½μž… μ’…λ₯˜μ˜ μΌλ°˜ν™”μ΄λ‹€. κ·Έ μ•„μ΄λ””μ–΄λŠ” λͺ¨λ“  n번째 μš”μ†Œκ°€ μ •λ ¬λœ λͺ©λ‘μ„ μ œκ³΅ν•œλ‹€λŠ” 것을 κ³ λ €ν•˜μ—¬ μ–΄λŠ κ³³μ—μ„œλ“ μ§€ μ‹œμž‘ν•˜λ„λ‘ μš”μ†Œμ˜ λͺ©λ‘μ„ λ°°μ—΄ν•˜λŠ” 것이닀. μ΄λŸ¬ν•œ λͺ©λ‘μ€ h-sorted둜 μ•Œλ €μ Έ μžˆλ‹€. λ§ˆμ°¬κ°€μ§€λ‘œ, 각각 κ°œλ³„μ μœΌλ‘œ μ •λ ¬λœ h μΈν„°λ¦¬λΈŒ λͺ©λ‘μœΌλ‘œ κ°„μ£Όν•  수 μžˆλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(nlog2 2n) -- 졜고의 μ„±λŠ₯ O(n log n) -- Average case performance depends on gap sequence - -###### View the algorithm in [action][shell-toptal] - -### μ‹œκ°„ λ³΅μž‘μ„± κ·Έλž˜ν”„ - -μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μ˜ λ³΅μž‘μ„± 비ꡐ (버블 μ •λ ¬, μ‚½μž… μ •λ ¬, 선택 μ •λ ¬) - -[λ³΅μž‘μ„± κ·Έλž˜ν”„](https://github.com/prateekiiest/Python/blob/master/sorts/sortinggraphs.png) - ---- - -## 검색 μ•Œκ³ λ¦¬μ¦˜ - -### Linear (μ„ ν˜• 탐색) - -![alt text][linear-image] - -From [Wikipedia][linear-wiki]: μ„ ν˜• 탐색 λ˜λŠ” 순차 탐색은 λͺ©λ‘ λ‚΄μ—μ„œ λͺ©ν‘œκ°’을 μ°ΎλŠ” 방법이닀. 일치 ν•­λͺ©μ΄ λ°œκ²¬λ˜κ±°λ‚˜ λͺ¨λ“  μš”μ†Œκ°€ 탐색될 λ•ŒκΉŒμ§€ λͺ©λ‘μ˜ 각 μš”μ†Œμ— λŒ€ν•΄ λͺ©ν‘œκ°’을 순차적으둜 κ²€μ‚¬ν•œλ‹€. -μ„ ν˜• 검색은 μ΅œμ•…μ˜ μ„ ν˜• μ‹œκ°„μœΌλ‘œ μ‹€ν–‰λ˜λ©° μ΅œλŒ€ n개의 λΉ„κ΅μ—μ„œ 이루어진닀. μ—¬κΈ°μ„œ n은 λͺ©λ‘μ˜ 길이닀. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n) -- 졜고의 μ„±λŠ₯ O(1) -- 평균 O(n) -- μ΅œμ•…μ˜ 경우 곡간 λ³΅μž‘μ„± O(1) iterative - -### Binary (이진 탐색) - -![alt text][binary-image] - -From [Wikipedia][binary-wiki]: 이진 탐색, (also known as half-interval search or logarithmic search), 은 μ •λ ¬λœ λ°°μ—΄ λ‚΄μ—μ„œ λͺ©ν‘œκ°’μ˜ μœ„μΉ˜λ₯Ό μ°ΎλŠ” 검색 μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. λͺ©ν‘œκ°’을 λ°°μ—΄μ˜ 쀑간 μš”μ†Œμ™€ λΉ„κ΅ν•œλ‹€; λ§Œμ•½ λͺ©ν‘œκ°’이 λ™μΌν•˜μ§€ μ•ŠμœΌλ©΄, λͺ©ν‘œλ¬Όμ˜ 절반이 제거되고 검색이 성곡할 λ•ŒκΉŒμ§€ λ‚˜λ¨Έμ§€ μ ˆλ°˜μ—μ„œ μ†λœλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(log n) -- 졜고의 μ„±λŠ₯ O(1) -- 평균 O(log n) -- μ΅œμ•…μ˜ 경우 곡간 λ³΅μž‘μ„± O(1) - -[bubble-toptal]: https://www.toptal.com/developers/sorting-algorithms/bubble-sort -[bubble-wiki]: https://en.wikipedia.org/wiki/Bubble_sort -[bubble-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Bubblesort-edited-color.svg/220px-Bubblesort-edited-color.svg.png "Bubble Sort" -[insertion-toptal]: https://www.toptal.com/developers/sorting-algorithms/insertion-sort -[insertion-wiki]: https://en.wikipedia.org/wiki/Insertion_sort -[insertion-image]: https://upload.wikimedia.org/wikipedia/commons/7/7e/Insertionsort-edited.png "Insertion Sort" -[quick-toptal]: https://www.toptal.com/developers/sorting-algorithms/quick-sort -[quick-wiki]: https://en.wikipedia.org/wiki/Quicksort -[quick-image]: https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif "Quick Sort" -[merge-toptal]: https://www.toptal.com/developers/sorting-algorithms/merge-sort -[merge-wiki]: https://en.wikipedia.org/wiki/Merge_sort -[merge-image]: https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif "Merge Sort" -[selection-toptal]: https://www.toptal.com/developers/sorting-algorithms/selection-sort -[selection-wiki]: https://en.wikipedia.org/wiki/Selection_sort -[selection-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Selection_sort_animation.gif/250px-Selection_sort_animation.gif "Selection Sort Sort" -[shell-toptal]: https://www.toptal.com/developers/sorting-algorithms/shell-sort -[shell-wiki]: https://en.wikipedia.org/wiki/Shellsort -[shell-image]: https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif "Shell Sort" -[linear-wiki]: https://en.wikipedia.org/wiki/Linear_search -[linear-image]: http://www.tutorialspoint.com/data_structures_algorithms/images/linear_search.gif -[binary-wiki]: https://en.wikipedia.org/wiki/Binary_search_algorithm -[binary-image]: https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_search_into_array.png - ---- - -## λ‚˜λ¨Έμ§€ μ•Œκ³ λ¦¬μ¦˜μ— λŒ€ν•œ 링크 - -| μ „ν™˜ | λ‹€μ΄λ‚˜λ―Ήν”„λ‘œκ·Έλž˜λ°(DP) | μ•”ν˜Έ | κ·Έ μ™Έ 것듀 | -| --------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------ | -| [Any Base to Any Base](Conversions/AnyBaseToAnyBase.java) | [Coin Change](DynamicProgramming/CoinChange.java) | [Caesar](Ciphers/Caesar.java) | [Heap Sort](Sorts/HeapSort.java) | -| [Any Base to Decimal](Conversions/AnyBaseToDecimal.java) | [Egg Dropping](DynamicProgramming/EggDropping.java) | [Columnar Transposition Cipher](Ciphers/ColumnarTranspositionCipher.java) | [Palindromic Prime Checker](Misc/PalindromePrime.java) | -| [Binary to Decimal](Conversions/BinaryToDecimal.java) | [Fibonacci](DynamicProgramming/Fibonacci.java) | [RSA](Ciphers/RSA.java) | More soon... | -| [Binary to HexaDecimal](Conversions/BinaryToHexadecimal.java) | [Kadane Algorithm](DynamicProgramming/KadaneAlgorithm.java) | more coming soon... | -| [Binary to Octal](Conversions/BinaryToOctal.java) | [Knapsack](DynamicProgramming/Knapsack.java) | -| [Decimal To Any Base](Conversions/DecimalToAnyBase.java) | [Longest Common Subsequence](DynamicProgramming/LongestCommonSubsequence.java) | -| [Decimal To Binary](Conversions/DecimalToBinary.java) | [Longest Increasing Subsequence](DynamicProgramming/LongestIncreasingSubsequence.java) | -| [Decimal To Hexadecimal](Conversions/DecimalToHexaDecimal.java) | [Rod Cutting](DynamicProgramming/RodCutting.java) | -| and much more... | and more... | - -### 자료 ꡬ쑰 - -| κ·Έλž˜ν”„ | νž™ | 리슀트 | 큐 | -| ------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------- | -| | [빈 νž™ μ˜ˆμ™Έμ²˜λ¦¬](DataStructures/Heaps/EmptyHeapException.java) | [μ›ν˜• μ—°κ²°λ¦¬μŠ€νŠΈ](DataStructures/Lists/CircleLinkedList.java) | [μ œλ„ˆλ¦­ μ–΄λ ˆμ΄ 리슀트 큐](DataStructures/Queues/GenericArrayListQueue.java) | -| | [νž™](DataStructures/Heaps/Heap.java) | [이쀑 μ—°κ²°λ¦¬μŠ€νŠΈ](DataStructures/Lists/DoublyLinkedList.java) | [큐](DataStructures/Queues/Queues.java) | -| [κ·Έλž˜ν”„](DataStructures/Graphs/Graphs.java) | [νž™ μš”μ†Œ](DataStructures/Heaps/HeapElement.java) | [λ‹¨μˆœ μ—°κ²°λ¦¬μŠ€νŠΈ](DataStructures/Lists/SinglyLinkedList.java) | -| [크루슀칼 μ•Œκ³ λ¦¬μ¦˜](DataStructures/Graphs/Kruskal.java) | [μ΅œλŒ€νž™](DataStructures/Heaps/MaxHeap.java) | -| [ν–‰λ ¬ κ·Έλž˜ν”„](DataStructures/Graphs/MatrixGraphs.java) | [μ΅œμ†Œνž™](DataStructures/Heaps/MinHeap.java) | -| [ν”„λ¦Ό μ΅œμ†Œμ‹ μž₯트리](DataStructures/Graphs/PrimMST.java) | - -| μŠ€νƒ | 트리 | -| --------------------------------------------------------------- | ------------------------------------------------- | -| [λ…Έλ“œ μŠ€νƒ](DataStructures/Stacks/NodeStack.java) | [AVL 트리](DataStructures/Trees/AVLTree.java) | -| [μ—°κ²°λ¦¬μŠ€νŠΈ μŠ€νƒ](DataStructures/Stacks/StackOfLinkedList.java) | [이진 트리](DataStructures/Trees/BinaryTree.java) | -| [μŠ€νƒ](DataStructures/Stacks) | And much more... | - -- [Bags](DataStructures/Bags/Bag.java) -- [Buffer](DataStructures/Buffers/CircularBuffer.java) -- [HashMap](DataStructures/HashMap/Hashing/HashMap.java) -- From 2ea3873b9ff15d64d6160715365aae7e6590958a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:46:45 +0100 Subject: [PATCH 082/122] chore(deps-dev): bump org.assertj:assertj-core from 3.27.6 to 3.27.7 (#7243) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.6 to 3.27.7. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.27.6...assertj-build-3.27.7) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.7 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3f81e66d35c0..170e3900b77f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ UTF-8 21 21 - 3.27.6 + 3.27.7 From dc3d64f51d59268e9de8f2334a0327fb0bcbb957 Mon Sep 17 00:00:00 2001 From: Chahat Sandhu Date: Tue, 27 Jan 2026 15:29:05 -0600 Subject: [PATCH 083/122] feat: add Difference Array algorithm implementation and tests (#7244) --- .../prefixsum/DifferenceArray.java | 87 ++++++++++++++ .../prefixsum/DifferenceArrayTest.java | 110 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java create mode 100644 src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java diff --git a/src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java b/src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java new file mode 100644 index 000000000000..1be55039cff0 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java @@ -0,0 +1,87 @@ +package com.thealgorithms.prefixsum; + +/** + * Implements the Difference Array algorithm. + * + *

+ * The Difference Array is an auxiliary data structure that enables efficient range update operations. + * It is based on the mathematical concept of Finite Differences. + *

+ * + *

+ * Key Operations: + *

    + *
  • Range Update (Add value to [L, R]): O(1)
  • + *
  • Reconstruction (Prefix Sum): O(N)
  • + *
+ *

+ * + * @see Finite Difference (Wikipedia) + * @see Prefix Sum (Wikipedia) + * @author Chahat Sandhu, singhc7 + */ +public class DifferenceArray { + + private final long[] differenceArray; + private final int n; + + /** + * Initializes the Difference Array from a given integer array. + * + * @param inputArray The initial array. Cannot be null or empty. + * @throws IllegalArgumentException if the input array is null or empty. + */ + public DifferenceArray(int[] inputArray) { + if (inputArray == null || inputArray.length == 0) { + throw new IllegalArgumentException("Input array cannot be null or empty."); + } + this.n = inputArray.length; + // Size n + 1 allows for branchless updates at the right boundary (r + 1). + this.differenceArray = new long[n + 1]; + initializeDifferenceArray(inputArray); + } + + private void initializeDifferenceArray(int[] inputArray) { + differenceArray[0] = inputArray[0]; + for (int i = 1; i < n; i++) { + differenceArray[i] = inputArray[i] - inputArray[i - 1]; + } + } + + /** + * Adds a value to all elements in the range [l, r]. + * + *

+ * This method uses a branchless approach by allocating an extra element at the end + * of the array, avoiding the conditional check for the right boundary. + *

+ * + * @param l The starting index (inclusive). + * @param r The ending index (inclusive). + * @param val The value to add. + * @throws IllegalArgumentException if the range is invalid. + */ + public void update(int l, int r, int val) { + if (l < 0 || r >= n || l > r) { + throw new IllegalArgumentException(String.format("Invalid range: [%d, %d] for array of size %d", l, r, n)); + } + + differenceArray[l] += val; + differenceArray[r + 1] -= val; + } + + /** + * Reconstructs the final array using prefix sums. + * + * @return The resulting array after all updates. Returns long[] to handle potential overflows. + */ + public long[] getResultArray() { + long[] result = new long[n]; + result[0] = differenceArray[0]; + + for (int i = 1; i < n; i++) { + result[i] = differenceArray[i] + result[i - 1]; + } + return result; + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java b/src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java new file mode 100644 index 000000000000..88a480f25f1a --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java @@ -0,0 +1,110 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class DifferenceArrayTest { + + @Test + void testStandardRangeUpdate() { + int[] input = {10, 20, 30, 40, 50}; + DifferenceArray da = new DifferenceArray(input); + + da.update(1, 3, 5); + + long[] expected = {10, 25, 35, 45, 50}; + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testMultipleOverlappingUpdates() { + int[] input = {10, 10, 10, 10, 10}; + DifferenceArray da = new DifferenceArray(input); + + da.update(0, 2, 10); + da.update(2, 4, 20); + + long[] expected = {20, 20, 40, 30, 30}; + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testIntegerOverflowSafety() { + int[] input = {Integer.MAX_VALUE, 100}; + DifferenceArray da = new DifferenceArray(input); + + da.update(0, 0, 100); + + long[] result = da.getResultArray(); + long expectedVal = (long) Integer.MAX_VALUE + 100; + + assertEquals(expectedVal, result[0]); + } + + @Test + void testFullRangeUpdate() { + int[] input = {1, 2, 3}; + DifferenceArray da = new DifferenceArray(input); + + da.update(0, 2, 100); + + long[] expected = {101, 102, 103}; + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testBoundaryWriteOptimization() { + int[] input = {5, 5}; + DifferenceArray da = new DifferenceArray(input); + + da.update(1, 1, 5); + + long[] expected = {5, 10}; + + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testLargeMassiveUpdate() { + int[] input = {0}; + DifferenceArray da = new DifferenceArray(input); + + int iterations = 100000; + for (int i = 0; i < iterations; i++) { + da.update(0, 0, 1); + } + + assertEquals(100000L, da.getResultArray()[0]); + } + + @Test + void testNullInputThrowsException() { + assertThrows(IllegalArgumentException.class, () -> new DifferenceArray(null)); + } + + @Test + void testEmptyInputThrowsException() { + assertThrows(IllegalArgumentException.class, () -> new DifferenceArray(new int[] {})); + } + + @Test + void testInvalidRangeNegativeIndex() { + DifferenceArray da = new DifferenceArray(new int[] {1, 2, 3}); + assertThrows(IllegalArgumentException.class, () -> da.update(-1, 1, 5)); + } + + @Test + void testInvalidRangeOutOfBounds() { + DifferenceArray da = new DifferenceArray(new int[] {1, 2, 3}); + assertThrows(IllegalArgumentException.class, () -> da.update(0, 3, 5)); + } + + @Test + void testInvalidRangeStartGreaterThanEnd() { + DifferenceArray da = new DifferenceArray(new int[] {1, 2, 3}); + assertThrows(IllegalArgumentException.class, () -> da.update(2, 1, 5)); + } +} From 11eec787702199795249905d6f624b757f37cc07 Mon Sep 17 00:00:00 2001 From: Pranav Ghorpade <153404855+ghorpadeire@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:01:10 +0000 Subject: [PATCH 084/122] docs: Add comprehensive documentation to BinarySearch algorithm (#7245) * docs: Add comprehensive documentation to BinarySearch algorithm - Added detailed JavaDoc with @param, @return, @throws tags - Included step-by-step algorithm walkthrough example - Added inline comments explaining each code section - Documented time and space complexity analysis - Provided concrete usage examples with expected outputs - Explained edge cases and overflow prevention technique * style: Apply proper Java formatting to BinarySearch - Fixed line length to meet style guidelines - Applied proper JavaDoc formatting - Corrected indentation and spacing - Ensured compliance with project formatting standards * fix: correct Javadoc formatting and add missing newline at EOF - Fix Javadoc structure with proper tag ordering (description before @params) - Remove incorrect @throws tag (method returns -1, doesn't throw) - Format algorithm steps as proper HTML ordered list - Move complexity analysis before @param tags - Add missing newline at end of file - Fix example code to use instance method call --- .../thealgorithms/searches/BinarySearch.java | 114 ++++++++++++++---- 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch.java b/src/main/java/com/thealgorithms/searches/BinarySearch.java index 0cac484d56b4..7a5361b280ea 100644 --- a/src/main/java/com/thealgorithms/searches/BinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/BinarySearch.java @@ -3,14 +3,32 @@ import com.thealgorithms.devutils.searches.SearchAlgorithm; /** - * Binary search is one of the most popular algorithms The algorithm finds the - * position of a target value within a sorted array - * IMPORTANT - * This algorithm works correctly only if the input array is sorted - * in ascending order. - *

- * Worst-case performance O(log n) Best-case performance O(1) Average - * performance O(log n) Worst-case space complexity O(1) + * Binary Search Algorithm Implementation + * + *

Binary search is one of the most efficient searching algorithms for finding a target element + * in a SORTED array. It works by repeatedly dividing the search space in half, eliminating half of + * the remaining elements in each step. + * + *

IMPORTANT: This algorithm ONLY works correctly if the input array is sorted in ascending + * order. + * + *

Algorithm Overview: 1. Start with the entire array (left = 0, right = array.length - 1) 2. + * Calculate the middle index 3. Compare the middle element with the target: - If middle element + * equals target: Found! Return the index - If middle element is less than target: Search the right + * half - If middle element is greater than target: Search the left half 4. Repeat until element is + * found or search space is exhausted + * + *

Performance Analysis: - Best-case time complexity: O(1) - Element found at middle on first + * try - Average-case time complexity: O(log n) - Most common scenario - Worst-case time + * complexity: O(log n) - Element not found or at extreme end - Space complexity: O(1) - Only uses + * a constant amount of extra space + * + *

Example Walkthrough: Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] Target: 7 + * + *

Step 1: left=0, right=9, mid=4, array[4]=9 (9 > 7, search left half) Step 2: left=0, + * right=3, mid=1, array[1]=3 (3 < 7, search right half) Step 3: left=2, right=3, mid=2, + * array[2]=5 (5 < 7, search right half) Step 4: left=3, right=3, mid=3, array[3]=7 (Found! + * Return index 3) * * @author Varun Upadhyay (https://github.com/varunu28) * @author Podshivalov Nikita (https://github.com/nikitap492) @@ -20,41 +38,89 @@ class BinarySearch implements SearchAlgorithm { /** - * @param array is an array where the element should be found - * @param key is an element which should be found - * @param is any comparable type - * @return index of the element + * Generic method to perform binary search on any comparable type. This is the main entry point + * for binary search operations. + * + *

Example Usage: + *

+     * Integer[] numbers = {1, 3, 5, 7, 9, 11};
+     * int result = new BinarySearch().find(numbers, 7);
+     * // result will be 3 (index of element 7)
+     *
+     * int notFound = new BinarySearch().find(numbers, 4);
+     * // notFound will be -1 (element 4 does not exist)
+     * 
+ * + * @param The type of elements in the array (must be Comparable) + * @param array The sorted array to search in (MUST be sorted in ascending order) + * @param key The element to search for + * @return The index of the key if found, -1 if not found or if array is null/empty */ @Override public > int find(T[] array, T key) { + // Handle edge case: empty array if (array == null || array.length == 0) { return -1; } + + // Delegate to the core search implementation return search(array, key, 0, array.length - 1); } /** - * This method implements the Generic Binary Search + * Core recursive implementation of binary search algorithm. This method divides the problem + * into smaller subproblems recursively. + * + *

How it works: + *

    + *
  1. Calculate the middle index to avoid integer overflow
  2. + *
  3. Check if middle element matches the target
  4. + *
  5. If not, recursively search either left or right half
  6. + *
  7. Base case: left > right means element not found
  8. + *
+ * + *

Time Complexity: O(log n) because we halve the search space each time. + * Space Complexity: O(log n) due to recursive call stack. * - * @param array The array to make the binary search - * @param key The number you are looking for - * @param left The lower bound - * @param right The upper bound - * @return the location of the key + * @param The type of elements (must be Comparable) + * @param array The sorted array to search in + * @param key The element we're looking for + * @param left The leftmost index of current search range (inclusive) + * @param right The rightmost index of current search range (inclusive) + * @return The index where key is located, or -1 if not found */ private > int search(T[] array, T key, int left, int right) { + // Base case: Search space is exhausted + // This happens when left pointer crosses right pointer if (right < left) { - return -1; // this means that the key not found + return -1; // Key not found in the array } - // find median - int median = (left + right) >>> 1; + + // Calculate middle index + // Using (left + right) / 2 could cause integer overflow for large arrays + // So we use: left + (right - left) / 2 which is mathematically equivalent + // but prevents overflow + int median = (left + right) >>> 1; // Unsigned right shift is faster division by 2 + + // Get the value at middle position for comparison int comp = key.compareTo(array[median]); + // Case 1: Found the target element at middle position if (comp == 0) { - return median; - } else if (comp < 0) { + return median; // Return the index where element was found + } + // Case 2: Target is smaller than middle element + // This means if target exists, it must be in the LEFT half + else if (comp < 0) { + // Recursively search the left half + // New search range: [left, median - 1] return search(array, key, left, median - 1); - } else { + } + // Case 3: Target is greater than middle element + // This means if target exists, it must be in the RIGHT half + else { + // Recursively search the right half + // New search range: [median + 1, right] return search(array, key, median + 1, right); } } From f3fd9ca3851c974afb7bfafc197a45d7e3678594 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 31 Jan 2026 19:32:08 +0100 Subject: [PATCH 085/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 13.0.0 to 13.1.0 (#7251) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 13.0.0 to 13.1.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-13.0.0...checkstyle-13.1.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 170e3900b77f..65a8fc229647 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 13.0.0 + 13.1.0 From dfaa4956392b06bb2e55578e883213a9ea946b8f Mon Sep 17 00:00:00 2001 From: Divyansh Saxena <119129875+divyanshsaxena002@users.noreply.github.com> Date: Sun, 1 Feb 2026 20:21:13 +0530 Subject: [PATCH 086/122] Refactor KMP and RabinKarp: Improve Reusability and Test Coverage (#7250) * first commit * Running KMPTest and RabinKarpTest with fixed formatting * now build failed error resolved * now build failed error resolved 2 --------- Co-authored-by: Divyansh Saxena Co-authored-by: Deniz Altunkapan --- .../java/com/thealgorithms/strings/KMP.java | 27 +++++--- .../com/thealgorithms/strings/RabinKarp.java | 62 ++++++++----------- .../com/thealgorithms/strings/KMPTest.java | 29 +++++++++ .../thealgorithms/strings/RabinKarpTest.java | 46 ++++++++++++++ 4 files changed, 119 insertions(+), 45 deletions(-) create mode 100644 src/test/java/com/thealgorithms/strings/KMPTest.java create mode 100644 src/test/java/com/thealgorithms/strings/RabinKarpTest.java diff --git a/src/main/java/com/thealgorithms/strings/KMP.java b/src/main/java/com/thealgorithms/strings/KMP.java index 07d3b0415006..0317abe6f39a 100644 --- a/src/main/java/com/thealgorithms/strings/KMP.java +++ b/src/main/java/com/thealgorithms/strings/KMP.java @@ -1,5 +1,8 @@ package com.thealgorithms.strings; +import java.util.ArrayList; +import java.util.List; + /** * Implementation of Knuth–Morris–Pratt algorithm Usage: see the main function * for an example @@ -8,16 +11,19 @@ public final class KMP { private KMP() { } - // a working example - - public static void main(String[] args) { - final String haystack = "AAAAABAAABA"; // This is the full string - final String needle = "AAAA"; // This is the substring that we want to find - kmpMatcher(haystack, needle); - } + /** + * find the starting index in string haystack[] that matches the search word P[] + * + * @param haystack The text to be searched + * @param needle The pattern to be searched for + * @return A list of starting indices where the pattern is found + */ + public static List kmpMatcher(final String haystack, final String needle) { + List occurrences = new ArrayList<>(); + if (haystack == null || needle == null || needle.isEmpty()) { + return occurrences; + } - // find the starting index in string haystack[] that matches the search word P[] - public static void kmpMatcher(final String haystack, final String needle) { final int m = haystack.length(); final int n = needle.length(); final int[] pi = computePrefixFunction(needle); @@ -32,10 +38,11 @@ public static void kmpMatcher(final String haystack, final String needle) { } if (q == n) { - System.out.println("Pattern starts: " + (i + 1 - n)); + occurrences.add(i + 1 - n); q = pi[q - 1]; } } + return occurrences; } // return the prefix function diff --git a/src/main/java/com/thealgorithms/strings/RabinKarp.java b/src/main/java/com/thealgorithms/strings/RabinKarp.java index bb8df3358453..be17f87c3656 100644 --- a/src/main/java/com/thealgorithms/strings/RabinKarp.java +++ b/src/main/java/com/thealgorithms/strings/RabinKarp.java @@ -1,32 +1,30 @@ package com.thealgorithms.strings; -import java.util.Scanner; +import java.util.ArrayList; +import java.util.List; /** * @author Prateek Kumar Oraon (https://github.com/prateekKrOraon) * - An implementation of Rabin-Karp string matching algorithm - Program will simply end if there is no match + * An implementation of Rabin-Karp string matching algorithm + * Program will simply end if there is no match */ public final class RabinKarp { private RabinKarp() { } - public static Scanner scanner = null; - public static final int ALPHABET_SIZE = 256; + private static final int ALPHABET_SIZE = 256; - public static void main(String[] args) { - scanner = new Scanner(System.in); - System.out.println("Enter String"); - String text = scanner.nextLine(); - System.out.println("Enter pattern"); - String pattern = scanner.nextLine(); - - int q = 101; - searchPat(text, pattern, q); + public static List search(String text, String pattern) { + return search(text, pattern, 101); } - private static void searchPat(String text, String pattern, int q) { + public static List search(String text, String pattern, int q) { + List occurrences = new ArrayList<>(); + if (text == null || pattern == null || pattern.isEmpty()) { + return occurrences; + } + int m = pattern.length(); int n = text.length(); int t = 0; @@ -35,48 +33,42 @@ private static void searchPat(String text, String pattern, int q) { int j = 0; int i = 0; - h = (int) Math.pow(ALPHABET_SIZE, m - 1) % q; + if (m > n) { + return new ArrayList<>(); + } + + // h = pow(ALPHABET_SIZE, m-1) % q + for (i = 0; i < m - 1; i++) { + h = h * ALPHABET_SIZE % q; + } for (i = 0; i < m; i++) { - // hash value is calculated for each character and then added with the hash value of the - // next character for pattern as well as the text for length equal to the length of - // pattern p = (ALPHABET_SIZE * p + pattern.charAt(i)) % q; t = (ALPHABET_SIZE * t + text.charAt(i)) % q; } for (i = 0; i <= n - m; i++) { - // if the calculated hash value of the pattern and text matches then - // all the characters of the pattern is matched with the text of length equal to length - // of the pattern if all matches then pattern exist in string if not then the hash value - // of the first character of the text is subtracted and hash value of the next character - // after the end of the evaluated characters is added if (p == t) { - // if hash value matches then the individual characters are matched for (j = 0; j < m; j++) { - // if not matched then break out of the loop if (text.charAt(i + j) != pattern.charAt(j)) { break; } } - // if all characters are matched then pattern exist in the string if (j == m) { - System.out.println("Pattern found at index " + i); + occurrences.add(i); } } - // if i Date: Sun, 1 Feb 2026 20:56:57 +0530 Subject: [PATCH 087/122] add Subarray Sum Equals K using prefix sum (#7252) Co-authored-by: Deniz Altunkapan --- .../prefixsum/SubarraySumEqualsK.java | 72 +++++++++++++++++++ .../prefixsum/SubarraySumEqualskTest.java | 59 +++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java create mode 100644 src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java diff --git a/src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java b/src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java new file mode 100644 index 000000000000..d6a6bbc01663 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java @@ -0,0 +1,72 @@ +package com.thealgorithms.prefixsum; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implements an algorithm to count the number of continuous subarrays + * whose sum equals a given value k. + * + *

+ * This algorithm uses the Prefix Sum technique combined with a HashMap + * to achieve O(N) time complexity. + *

+ * + *

+ * Let prefixSum[i] be the sum of elements from index 0 to i. + * A subarray (j + 1) to i has sum k if: + * + *

+ * prefixSum[i] - prefixSum[j] = k
+ * 
+ *

+ * + *

+ * The HashMap stores the frequency of each prefix sum encountered so far. + *

+ * + *

+ * Time Complexity: O(N)
+ * Space Complexity: O(N) + *

+ * + * @see Prefix Sum (Wikipedia) + * @author Ruturaj Jadhav, ruturajjadhav07 + */ +public final class SubarraySumEqualsK { + + private SubarraySumEqualsK() { + // Utility class; prevent instantiation + } + + /** + * Counts the number of subarrays whose sum equals k. + * + * @param nums The input integer array. + * @param k The target sum. + * @return The number of continuous subarrays summing to k. + * @throws IllegalArgumentException if nums is null. + */ + public static int countSubarrays(int[] nums, int k) { + if (nums == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + Map prefixSumFrequency = new HashMap<>(); + prefixSumFrequency.put(0L, 1); + + long prefixSum = 0; + int count = 0; + + for (int num : nums) { + prefixSum += num; + + long requiredSum = prefixSum - k; + count += prefixSumFrequency.getOrDefault(requiredSum, 0); + + prefixSumFrequency.put(prefixSum, prefixSumFrequency.getOrDefault(prefixSum, 0) + 1); + } + + return count; + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java b/src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java new file mode 100644 index 000000000000..68f85b713046 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java @@ -0,0 +1,59 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link SubarraySumEqualsK}. + */ +class SubarraySumEqualsKTest { + + @Test + void testBasicExample() { + int[] nums = {1, 1, 1}; + int k = 2; + assertEquals(2, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testWithNegativeNumbers() { + int[] nums = {1, -1, 0}; + int k = 0; + assertEquals(3, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testSingleElementEqualToK() { + int[] nums = {5}; + int k = 5; + assertEquals(1, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testSingleElementNotEqualToK() { + int[] nums = {5}; + int k = 3; + assertEquals(0, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testAllZeros() { + int[] nums = {0, 0, 0}; + int k = 0; + assertEquals(6, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testEmptyArray() { + int[] nums = {}; + int k = 0; + assertEquals(0, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testNullArrayThrowsException() { + assertThrows(IllegalArgumentException.class, () -> SubarraySumEqualsK.countSubarrays(null, 0)); + } +} From c6703d337edb74783406d5cafa4a363a2415d8eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:55:55 +0100 Subject: [PATCH 088/122] chore(deps-dev): bump org.apache.maven.plugins:maven-compiler-plugin from 3.14.1 to 3.15.0 (#7254) chore(deps-dev): bump org.apache.maven.plugins:maven-compiler-plugin Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.14.1 to 3.15.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.1...maven-compiler-plugin-3.15.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.15.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65a8fc229647..f13169cece97 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.15.0 21 From 8e30bcbb02f14f118cc63f24b3c44b7ff7f66ba8 Mon Sep 17 00:00:00 2001 From: Ahmed Allam <60698204+GziXnine@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:32:37 +0200 Subject: [PATCH 089/122] refactor: clean up duplicate algorithm implementations to reduce maintenance overhead (#7256) refactor: remove duplicate algorithm implementations Removed the following duplicate implementations: - searches/PerfectBinarySearch.java (duplicate of IterativeBinarySearch) - searches/SortOrderAgnosticBinarySearch.java (duplicate of OrderAgnosticBinarySearch) - strings/LongestPalindromicSubstring.java (duplicate of dynamicprogramming version) - strings/ValidParentheses.java (duplicate of stacks version) - others/cn/HammingDistance.java (duplicate - strings version handles text) - others/NewManShanksPrimeTest.java (orphan test in wrong package) Updated DIRECTORY.md to reflect the changes. Fixes #7253 Co-authored-by: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com> --- DIRECTORY.md | 13 --- .../others/cn/HammingDistance.java | 32 -------- .../searches/PerfectBinarySearch.java | 54 ------------ .../SortOrderAgnosticBinarySearch.java | 30 ------- .../strings/LongestPalindromicSubstring.java | 37 --------- .../strings/ValidParentheses.java | 53 ------------ .../others/NewManShanksPrimeTest.java | 49 ----------- .../others/cn/HammingDistanceTest.java | 82 ------------------- .../searches/PerfectBinarySearchTest.java | 44 ---------- .../SortOrderAgnosticBinarySearchTest.java | 26 ------ .../LongestPalindromicSubstringTest.java | 21 ----- .../strings/ValidParenthesesTest.java | 33 -------- 12 files changed, 474 deletions(-) delete mode 100644 src/main/java/com/thealgorithms/others/cn/HammingDistance.java delete mode 100644 src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java delete mode 100644 src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java delete mode 100644 src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java delete mode 100644 src/main/java/com/thealgorithms/strings/ValidParentheses.java delete mode 100644 src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java delete mode 100644 src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java delete mode 100644 src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java delete mode 100644 src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java delete mode 100644 src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java delete mode 100644 src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java diff --git a/DIRECTORY.md b/DIRECTORY.md index deaf59636fa4..585c634c3429 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -626,8 +626,6 @@ - πŸ“„ [SkylineProblem](src/main/java/com/thealgorithms/others/SkylineProblem.java) - πŸ“„ [TwoPointers](src/main/java/com/thealgorithms/others/TwoPointers.java) - πŸ“„ [Verhoeff](src/main/java/com/thealgorithms/others/Verhoeff.java) - - πŸ“ **cn** - - πŸ“„ [HammingDistance](src/main/java/com/thealgorithms/others/cn/HammingDistance.java) - πŸ“ **physics** - πŸ“„ [CoulombsLaw](src/main/java/com/thealgorithms/physics/CoulombsLaw.java) - πŸ“„ [DampedOscillator](src/main/java/com/thealgorithms/physics/DampedOscillator.java) @@ -701,7 +699,6 @@ - πŸ“„ [LowerBound](src/main/java/com/thealgorithms/searches/LowerBound.java) - πŸ“„ [MonteCarloTreeSearch](src/main/java/com/thealgorithms/searches/MonteCarloTreeSearch.java) - πŸ“„ [OrderAgnosticBinarySearch](src/main/java/com/thealgorithms/searches/OrderAgnosticBinarySearch.java) - - πŸ“„ [PerfectBinarySearch](src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java) - πŸ“„ [QuickSelect](src/main/java/com/thealgorithms/searches/QuickSelect.java) - πŸ“„ [RabinKarpAlgorithm](src/main/java/com/thealgorithms/searches/RabinKarpAlgorithm.java) - πŸ“„ [RandomSearch](src/main/java/com/thealgorithms/searches/RandomSearch.java) @@ -710,7 +707,6 @@ - πŸ“„ [SaddlebackSearch](src/main/java/com/thealgorithms/searches/SaddlebackSearch.java) - πŸ“„ [SearchInARowAndColWiseSortedMatrix](src/main/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrix.java) - πŸ“„ [SentinelLinearSearch](src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java) - - πŸ“„ [SortOrderAgnosticBinarySearch](src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java) - πŸ“„ [SquareRootBinarySearch](src/main/java/com/thealgorithms/searches/SquareRootBinarySearch.java) - πŸ“„ [TernarySearch](src/main/java/com/thealgorithms/searches/TernarySearch.java) - πŸ“„ [UnionFind](src/main/java/com/thealgorithms/searches/UnionFind.java) @@ -817,7 +813,6 @@ - πŸ“„ [LetterCombinationsOfPhoneNumber](src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java) - πŸ“„ [LongestCommonPrefix](src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java) - πŸ“„ [LongestNonRepetitiveSubstring](src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java) - - πŸ“„ [LongestPalindromicSubstring](src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java) - πŸ“„ [Lower](src/main/java/com/thealgorithms/strings/Lower.java) - πŸ“„ [Manacher](src/main/java/com/thealgorithms/strings/Manacher.java) - πŸ“„ [MyAtoi](src/main/java/com/thealgorithms/strings/MyAtoi.java) @@ -834,7 +829,6 @@ - πŸ“„ [StringMatchFiniteAutomata](src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java) - πŸ“„ [SuffixArray](src/main/java/com/thealgorithms/strings/SuffixArray.java) - πŸ“„ [Upper](src/main/java/com/thealgorithms/strings/Upper.java) - - πŸ“„ [ValidParentheses](src/main/java/com/thealgorithms/strings/ValidParentheses.java) - πŸ“„ [WordLadder](src/main/java/com/thealgorithms/strings/WordLadder.java) - πŸ“„ [ZAlgorithm](src/main/java/com/thealgorithms/strings/ZAlgorithm.java) - πŸ“ **zigZagPattern** @@ -1395,7 +1389,6 @@ - πŸ“„ [MaximumSumOfDistinctSubarraysWithLengthKTest](src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java) - πŸ“„ [MiniMaxAlgorithmTest](src/test/java/com/thealgorithms/others/MiniMaxAlgorithmTest.java) - πŸ“„ [MosAlgorithmTest](src/test/java/com/thealgorithms/others/MosAlgorithmTest.java) - - πŸ“„ [NewManShanksPrimeTest](src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java) - πŸ“„ [NextFitTest](src/test/java/com/thealgorithms/others/NextFitTest.java) - πŸ“„ [PageRankTest](src/test/java/com/thealgorithms/others/PageRankTest.java) - πŸ“„ [PasswordGenTest](src/test/java/com/thealgorithms/others/PasswordGenTest.java) @@ -1404,8 +1397,6 @@ - πŸ“„ [SkylineProblemTest](src/test/java/com/thealgorithms/others/SkylineProblemTest.java) - πŸ“„ [TwoPointersTest](src/test/java/com/thealgorithms/others/TwoPointersTest.java) - πŸ“„ [WorstFitCPUTest](src/test/java/com/thealgorithms/others/WorstFitCPUTest.java) - - πŸ“ **cn** - - πŸ“„ [HammingDistanceTest](src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java) - πŸ“ **physics** - πŸ“„ [CoulombsLawTest](src/test/java/com/thealgorithms/physics/CoulombsLawTest.java) - πŸ“„ [DampedOscillatorTest](src/test/java/com/thealgorithms/physics/DampedOscillatorTest.java) @@ -1479,7 +1470,6 @@ - πŸ“„ [LowerBoundTest](src/test/java/com/thealgorithms/searches/LowerBoundTest.java) - πŸ“„ [MonteCarloTreeSearchTest](src/test/java/com/thealgorithms/searches/MonteCarloTreeSearchTest.java) - πŸ“„ [OrderAgnosticBinarySearchTest](src/test/java/com/thealgorithms/searches/OrderAgnosticBinarySearchTest.java) - - πŸ“„ [PerfectBinarySearchTest](src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java) - πŸ“„ [QuickSelectTest](src/test/java/com/thealgorithms/searches/QuickSelectTest.java) - πŸ“„ [RabinKarpAlgorithmTest](src/test/java/com/thealgorithms/searches/RabinKarpAlgorithmTest.java) - πŸ“„ [RandomSearchTest](src/test/java/com/thealgorithms/searches/RandomSearchTest.java) @@ -1488,7 +1478,6 @@ - πŸ“„ [SaddlebackSearchTest](src/test/java/com/thealgorithms/searches/SaddlebackSearchTest.java) - πŸ“„ [SearchInARowAndColWiseSortedMatrixTest](src/test/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrixTest.java) - πŸ“„ [SentinelLinearSearchTest](src/test/java/com/thealgorithms/searches/SentinelLinearSearchTest.java) - - πŸ“„ [SortOrderAgnosticBinarySearchTest](src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java) - πŸ“„ [SquareRootBinarySearchTest](src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java) - πŸ“„ [TernarySearchTest](src/test/java/com/thealgorithms/searches/TernarySearchTest.java) - πŸ“„ [TestSearchInARowAndColWiseSortedMatrix](src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java) @@ -1593,7 +1582,6 @@ - πŸ“„ [LetterCombinationsOfPhoneNumberTest](src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java) - πŸ“„ [LongestCommonPrefixTest](src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java) - πŸ“„ [LongestNonRepetitiveSubstringTest](src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java) - - πŸ“„ [LongestPalindromicSubstringTest](src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java) - πŸ“„ [LowerTest](src/test/java/com/thealgorithms/strings/LowerTest.java) - πŸ“„ [ManacherTest](src/test/java/com/thealgorithms/strings/ManacherTest.java) - πŸ“„ [MyAtoiTest](src/test/java/com/thealgorithms/strings/MyAtoiTest.java) @@ -1609,7 +1597,6 @@ - πŸ“„ [StringMatchFiniteAutomataTest](src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java) - πŸ“„ [SuffixArrayTest](src/test/java/com/thealgorithms/strings/SuffixArrayTest.java) - πŸ“„ [UpperTest](src/test/java/com/thealgorithms/strings/UpperTest.java) - - πŸ“„ [ValidParenthesesTest](src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java) - πŸ“„ [WordLadderTest](src/test/java/com/thealgorithms/strings/WordLadderTest.java) - πŸ“„ [ZAlgorithmTest](src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java) - πŸ“ **zigZagPattern** diff --git a/src/main/java/com/thealgorithms/others/cn/HammingDistance.java b/src/main/java/com/thealgorithms/others/cn/HammingDistance.java deleted file mode 100644 index c8239d53d606..000000000000 --- a/src/main/java/com/thealgorithms/others/cn/HammingDistance.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.thealgorithms.others.cn; - -public final class HammingDistance { - private HammingDistance() { - } - - private static void checkChar(char inChar) { - if (inChar != '0' && inChar != '1') { - throw new IllegalArgumentException("Input must be a binary string."); - } - } - - public static int compute(char charA, char charB) { - checkChar(charA); - checkChar(charB); - return charA == charB ? 0 : 1; - } - - public static int compute(String bitsStrA, String bitsStrB) { - if (bitsStrA.length() != bitsStrB.length()) { - throw new IllegalArgumentException("Input strings must have the same length."); - } - - int totalErrorBitCount = 0; - - for (int i = 0; i < bitsStrA.length(); i++) { - totalErrorBitCount += compute(bitsStrA.charAt(i), bitsStrB.charAt(i)); - } - - return totalErrorBitCount; - } -} diff --git a/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java b/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java deleted file mode 100644 index 495e2e41bc5b..000000000000 --- a/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.thealgorithms.searches; - -import com.thealgorithms.devutils.searches.SearchAlgorithm; - -/** - * Binary search is one of the most popular algorithms The algorithm finds the - * position of a target value within a sorted array - * - *

- * Worst-case performance O(log n) Best-case performance O(1) Average - * performance O(log n) Worst-case space complexity O(1) - * - * @author D Sunil (https://github.com/sunilnitdgp) - * @see SearchAlgorithm - */ - -public class PerfectBinarySearch implements SearchAlgorithm { - - /** - * @param array is an array where the element should be found - * @param key is an element which should be found - * @param is any comparable type - * @return index of the element - */ - @Override - public > int find(T[] array, T key) { - return search(array, key, 0, array.length - 1); - } - - /** - * This method implements the Generic Binary Search iteratively. - * - * @param array The array to make the binary search - * @param key The number you are looking for - * @return the location of the key, or -1 if not found - */ - private static > int search(T[] array, T key, int left, int right) { - while (left <= right) { - int median = (left + right) >>> 1; - int comp = key.compareTo(array[median]); - - if (comp == 0) { - return median; // Key found - } - - if (comp < 0) { - right = median - 1; // Adjust the right bound - } else { - left = median + 1; // Adjust the left bound - } - } - return -1; // Key not found - } -} diff --git a/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java b/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java deleted file mode 100644 index 6a2a46c2821f..000000000000 --- a/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.thealgorithms.searches; -public final class SortOrderAgnosticBinarySearch { - private SortOrderAgnosticBinarySearch() { - } - public static int find(int[] arr, int key) { - int start = 0; - int end = arr.length - 1; - boolean arrDescending = arr[start] > arr[end]; // checking for Array is in ascending order or descending order. - while (start <= end) { - int mid = end - start / 2; - if (arr[mid] == key) { - return mid; - } - if (arrDescending) { // boolean is true then our array is in descending order - if (key < arr[mid]) { - start = mid + 1; - } else { - end = mid - 1; - } - } else { // otherwise our array is in ascending order - if (key > arr[mid]) { - start = mid + 1; - } else { - end = mid - 1; - } - } - } - return -1; - } -} diff --git a/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java b/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java deleted file mode 100644 index ca500357ba77..000000000000 --- a/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.thealgorithms.strings; - -final class LongestPalindromicSubstring { - private LongestPalindromicSubstring() { - } - - /** - * Finds the longest palindromic substring in the given string. - * - * @param s the input string - * @return the longest palindromic substring - */ - public static String longestPalindrome(String s) { - if (s == null || s.isEmpty()) { - return ""; - } - String maxStr = ""; - for (int i = 0; i < s.length(); ++i) { - for (int j = i; j < s.length(); ++j) { - if (isValid(s, i, j) && (j - i + 1 > maxStr.length())) { - maxStr = s.substring(i, j + 1); - } - } - } - return maxStr; - } - - private static boolean isValid(String s, int lo, int hi) { - int n = hi - lo + 1; - for (int i = 0; i < n / 2; ++i) { - if (s.charAt(lo + i) != s.charAt(hi - i)) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/com/thealgorithms/strings/ValidParentheses.java b/src/main/java/com/thealgorithms/strings/ValidParentheses.java deleted file mode 100644 index 25a72f379dec..000000000000 --- a/src/main/java/com/thealgorithms/strings/ValidParentheses.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.thealgorithms.strings; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Map; - -/** - * Validates if a given string has valid matching parentheses. - *

- * A string is considered valid if: - *

    - *
  • Open brackets are closed by the same type of brackets.
  • - *
  • Brackets are closed in the correct order.
  • - *
  • Every closing bracket has a corresponding open bracket of the same type.
  • - *
- * - * Allowed characters: '(', ')', '{', '}', '[', ']' - */ -public final class ValidParentheses { - private ValidParentheses() { - } - - private static final Map BRACKET_PAIRS = Map.of(')', '(', '}', '{', ']', '['); - - /** - * Checks if the input string has valid parentheses. - * - * @param s the string containing only bracket characters - * @return true if valid, false otherwise - * @throws IllegalArgumentException if the string contains invalid characters or is null - */ - public static boolean isValid(String s) { - if (s == null) { - throw new IllegalArgumentException("Input string cannot be null"); - } - - Deque stack = new ArrayDeque<>(); - - for (char c : s.toCharArray()) { - if (BRACKET_PAIRS.containsValue(c)) { - stack.push(c); // opening bracket - } else if (BRACKET_PAIRS.containsKey(c)) { - if (stack.isEmpty() || stack.pop() != BRACKET_PAIRS.get(c)) { - return false; - } - } else { - throw new IllegalArgumentException("Unexpected character: " + c); - } - } - - return stack.isEmpty(); - } -} diff --git a/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java b/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java deleted file mode 100644 index 3b657e441b1c..000000000000 --- a/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.thealgorithms.others; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.thealgorithms.dynamicprogramming.NewManShanksPrime; -import org.junit.jupiter.api.Test; - -public class NewManShanksPrimeTest { - - @Test - void testOne() { - assertTrue(NewManShanksPrime.nthManShanksPrime(1, 1)); - } - - @Test - void testTwo() { - assertTrue(NewManShanksPrime.nthManShanksPrime(2, 3)); - } - - @Test - void testThree() { - assertTrue(NewManShanksPrime.nthManShanksPrime(3, 7)); - } - - @Test - void testFour() { - assertTrue(NewManShanksPrime.nthManShanksPrime(4, 17)); - } - - @Test - void testFive() { - assertTrue(NewManShanksPrime.nthManShanksPrime(5, 41)); - } - - @Test - void testSix() { - assertTrue(NewManShanksPrime.nthManShanksPrime(6, 99)); - } - - @Test - void testSeven() { - assertTrue(NewManShanksPrime.nthManShanksPrime(7, 239)); - } - - @Test - void testEight() { - assertTrue(NewManShanksPrime.nthManShanksPrime(8, 577)); - } -} diff --git a/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java b/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java deleted file mode 100644 index 669f928cd247..000000000000 --- a/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.thealgorithms.others.cn; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -public class HammingDistanceTest { - @Test - public void checkForDifferentBits() { - int answer = HammingDistance.compute("000", "011"); - Assertions.assertThat(answer).isEqualTo(2); - } - - /* - - 1 0 1 0 1 - 1 1 1 1 0 - ---------- - 0 1 0 1 1 - - - */ - @Test - public void checkForDifferentBitsLength() { - int answer = HammingDistance.compute("10101", "11110"); - Assertions.assertThat(answer).isEqualTo(3); - } - - @Test - public void checkForSameBits() { - String someBits = "111"; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void checkForLongDataBits() { - int answer = HammingDistance.compute("10010101101010000100110100", "00110100001011001100110101"); - Assertions.assertThat(answer).isEqualTo(7); - } - - @Test - public void mismatchDataBits() { - Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("100010", "00011"); }); - - Assertions.assertThat(ex.getMessage()).contains("must have the same length"); - } - - @Test - public void mismatchDataBits2() { - Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("1", "11"); }); - - Assertions.assertThat(ex.getMessage()).contains("must have the same length"); - } - - @Test - public void checkForLongDataBitsSame() { - String someBits = "10010101101010000100110100"; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void checkForEmptyInput() { - String someBits = ""; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void checkForInputOfLength1() { - String someBits = "0"; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void computeThrowsExceptionWhenInputsAreNotBitStrs() { - Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("1A", "11"); }); - - Assertions.assertThat(ex.getMessage()).contains("must be a binary string"); - } -} diff --git a/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java deleted file mode 100644 index 6eab20f45467..000000000000 --- a/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.thealgorithms.searches; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -/** - * @author D Sunil (https://github.com/sunilnitdgp) - * @see PerfectBinarySearch - */ -public class PerfectBinarySearchTest { - - @Test - public void testIntegerBinarySearch() { - Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - PerfectBinarySearch binarySearch = new PerfectBinarySearch<>(); - - // Test cases for elements present in the array - assertEquals(0, binarySearch.find(array, 1)); // First element - assertEquals(4, binarySearch.find(array, 5)); // Middle element - assertEquals(9, binarySearch.find(array, 10)); // Last element - assertEquals(6, binarySearch.find(array, 7)); // Element in the middle - - // Test cases for elements not in the array - assertEquals(-1, binarySearch.find(array, 0)); // Element before the array - assertEquals(-1, binarySearch.find(array, 11)); // Element after the array - assertEquals(-1, binarySearch.find(array, 100)); // Element not in the array - } - - @Test - public void testStringBinarySearch() { - String[] array = {"apple", "banana", "cherry", "date", "fig"}; - PerfectBinarySearch binarySearch = new PerfectBinarySearch<>(); - - // Test cases for elements not in the array - assertEquals(-1, binarySearch.find(array, "apricot")); // Element not in the array - assertEquals(-1, binarySearch.find(array, "bananaa")); // Element not in the array - - // Test cases for elements present in the array - assertEquals(0, binarySearch.find(array, "apple")); // First element - assertEquals(2, binarySearch.find(array, "cherry")); // Middle element - assertEquals(4, binarySearch.find(array, "fig")); // Last element - } -} diff --git a/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java deleted file mode 100644 index e2917733d1d9..000000000000 --- a/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.thealgorithms.searches; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class SortOrderAgnosticBinarySearchTest { - - @Test - public void testAscending() { - int[] arr = {1, 2, 3, 4, 5}; // for ascending order. - int target = 2; - int ans = SortOrderAgnosticBinarySearch.find(arr, target); - int excepted = 1; - assertEquals(excepted, ans); - } - - @Test - public void testDescending() { - int[] arr = {5, 4, 3, 2, 1}; // for descending order. - int target = 2; - int ans = SortOrderAgnosticBinarySearch.find(arr, target); - int excepted = 3; - assertEquals(excepted, ans); - } -} diff --git a/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java deleted file mode 100644 index aa13c0f4a474..000000000000 --- a/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.thealgorithms.strings; - -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; - -class LongestPalindromicSubstringTest { - - @ParameterizedTest - @MethodSource("provideTestCasesForLongestPalindrome") - void testLongestPalindrome(String input, String expected) { - assertEquals(expected, LongestPalindromicSubstring.longestPalindrome(input)); - } - - private static Stream provideTestCasesForLongestPalindrome() { - return Stream.of(Arguments.of("babad", "bab"), Arguments.of("cbbd", "bb"), Arguments.of("a", "a"), Arguments.of("", ""), Arguments.of("abc", "a"), Arguments.of(null, ""), Arguments.of("aaaaa", "aaaaa")); - } -} diff --git a/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java deleted file mode 100644 index 411b11e743b8..000000000000 --- a/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.thealgorithms.strings; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -public class ValidParenthesesTest { - - @ParameterizedTest(name = "Input: \"{0}\" β†’ Expected: {1}") - @CsvSource({"'()', true", "'()[]{}', true", "'(]', false", "'{[]}', true", "'([{}])', true", "'([)]', false", "'', true", "'(', false", "')', false", "'{{{{}}}}', true", "'[({})]', true", "'[(])', false", "'[', false", "']', false", "'()()()()', true", "'(()', false", "'())', false", - "'{[()()]()}', true"}) - void - testIsValid(String input, boolean expected) { - assertEquals(expected, ValidParentheses.isValid(input)); - } - - @Test - void testNullInputThrows() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> ValidParentheses.isValid(null)); - assertEquals("Input string cannot be null", ex.getMessage()); - } - - @ParameterizedTest(name = "Input: \"{0}\" β†’ throws IllegalArgumentException") - @CsvSource({"'a'", "'()a'", "'[123]'", "'{hello}'", "'( )'", "'\t'", "'\n'", "'@#$%'"}) - void testInvalidCharactersThrow(String input) { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> ValidParentheses.isValid(input)); - assertTrue(ex.getMessage().startsWith("Unexpected character")); - } -} From a14b2345f0a40a043ce78ebaf1ff056bbb362891 Mon Sep 17 00:00:00 2001 From: Chahat Sandhu Date: Wed, 4 Feb 2026 09:49:15 -0600 Subject: [PATCH 090/122] feat: add ElGamalCipher with Safe Prime generation and stateless design (#7257) --- .../thealgorithms/ciphers/ElGamalCipher.java | 174 ++++++++++++++++++ .../ciphers/ElGamalCipherTest.java | 145 +++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java create mode 100644 src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java diff --git a/src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java b/src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java new file mode 100644 index 000000000000..6383caa59b1f --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java @@ -0,0 +1,174 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * ElGamal Encryption Algorithm Implementation. + * + *

+ * ElGamal is an asymmetric key encryption algorithm for public-key cryptography + * based on the Diffie–Hellman key exchange. It relies on the difficulty + * of computing discrete logarithms in a cyclic group. + *

+ * + *

+ * Key Features: + *

    + *
  • Uses Safe Primes (p = 2q + 1) to ensure group security.
  • + *
  • Verifies the generator is a primitive root modulo p.
  • + *
  • Stateless design using Java Records.
  • + *
  • SecureRandom for all cryptographic operations.
  • + *
+ *

+ * + * @author Chahat Sandhu, singhc7 + * @see ElGamal Encryption (Wikipedia) + * @see Safe Primes + */ +public final class ElGamalCipher { + + private static final SecureRandom RANDOM = new SecureRandom(); + private static final int PRIME_CERTAINTY = 40; + private static final int MIN_BIT_LENGTH = 256; + + private ElGamalCipher() { + } + + /** + * A container for the Public and Private keys. + * + * @param p The prime modulus. + * @param g The generator (primitive root). + * @param y The public key component (g^x mod p). + * @param x The private key. + */ + public record KeyPair(BigInteger p, BigInteger g, BigInteger y, BigInteger x) { + } + + /** + * Container for the encryption result. + * + * @param a The first component (g^k mod p). + * @param b The second component (y^k * m mod p). + */ + public record CipherText(BigInteger a, BigInteger b) { + } + + /** + * Generates a valid ElGamal KeyPair using a Safe Prime. + * + * @param bitLength The bit length of the prime modulus p. Must be at least 256. + * @return A valid KeyPair (p, g, y, x). + * @throws IllegalArgumentException if bitLength is too small. + */ + public static KeyPair generateKeys(int bitLength) { + if (bitLength < MIN_BIT_LENGTH) { + throw new IllegalArgumentException("Bit length must be at least " + MIN_BIT_LENGTH + " for security."); + } + + BigInteger p; + BigInteger q; + BigInteger g; + BigInteger x; + BigInteger y; + + // Generate Safe Prime p = 2q + 1 + do { + q = new BigInteger(bitLength - 1, PRIME_CERTAINTY, RANDOM); + p = q.multiply(BigInteger.TWO).add(BigInteger.ONE); + } while (!p.isProbablePrime(PRIME_CERTAINTY)); + + // Find a Generator g (Primitive Root modulo p) + do { + g = new BigInteger(bitLength, RANDOM).mod(p.subtract(BigInteger.TWO)).add(BigInteger.TWO); + } while (!isValidGenerator(g, p, q)); + + // Generate Private Key x in range [2, p-2] + do { + x = new BigInteger(bitLength, RANDOM); + } while (x.compareTo(BigInteger.TWO) < 0 || x.compareTo(p.subtract(BigInteger.TWO)) > 0); + + // Compute Public Key y = g^x mod p + y = g.modPow(x, p); + + return new KeyPair(p, g, y, x); + } + + /** + * Encrypts a message using the public key. + * + * @param message The message converted to BigInteger. + * @param p The prime modulus. + * @param g The generator. + * @param y The public key component. + * @return The CipherText pair (a, b). + * @throws IllegalArgumentException if inputs are null, negative, or message >= p. + */ + public static CipherText encrypt(BigInteger message, BigInteger p, BigInteger g, BigInteger y) { + if (message == null || p == null || g == null || y == null) { + throw new IllegalArgumentException("Inputs cannot be null."); + } + if (message.compareTo(BigInteger.ZERO) < 0) { + throw new IllegalArgumentException("Message must be non-negative."); + } + if (message.compareTo(p) >= 0) { + throw new IllegalArgumentException("Message must be smaller than the prime modulus p."); + } + + BigInteger k; + BigInteger pMinus1 = p.subtract(BigInteger.ONE); + + // Select ephemeral key k such that 1 < k < p-1 and gcd(k, p-1) = 1 + do { + k = new BigInteger(p.bitLength(), RANDOM); + } while (k.compareTo(BigInteger.ONE) <= 0 || k.compareTo(pMinus1) >= 0 || !k.gcd(pMinus1).equals(BigInteger.ONE)); + + BigInteger a = g.modPow(k, p); + BigInteger b = y.modPow(k, p).multiply(message).mod(p); + + return new CipherText(a, b); + } + + /** + * Decrypts a ciphertext using the private key. + * + * @param cipher The CipherText (a, b). + * @param x The private key. + * @param p The prime modulus. + * @return The decrypted message as BigInteger. + * @throws IllegalArgumentException if inputs are null. + */ + public static BigInteger decrypt(CipherText cipher, BigInteger x, BigInteger p) { + if (cipher == null || x == null || p == null) { + throw new IllegalArgumentException("Inputs cannot be null."); + } + + BigInteger a = cipher.a(); + BigInteger b = cipher.b(); + + BigInteger s = a.modPow(x, p); + BigInteger sInverse = s.modInverse(p); + + return b.multiply(sInverse).mod(p); + } + + /** + * Verifies if g is a valid generator for safe prime p = 2q + 1. + * + * @param g The candidate generator. + * @param p The safe prime. + * @param q The Sophie Germain prime (p-1)/2. + * @return True if g is a primitive root, False otherwise. + */ + private static boolean isValidGenerator(BigInteger g, BigInteger p, BigInteger q) { + // Fix: Must use braces {} for all if statements + if (g.equals(BigInteger.ONE)) { + return false; + } + if (g.modPow(BigInteger.TWO, p).equals(BigInteger.ONE)) { + return false; + } + return !g.modPow(q, p).equals(BigInteger.ONE); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java b/src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java new file mode 100644 index 000000000000..63dec4846bbc --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java @@ -0,0 +1,145 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Unit tests for ElGamalCipher. + * Includes property-based testing (homomorphism), probabilistic checks, + * and boundary validation. + */ +class ElGamalCipherTest { + + private static ElGamalCipher.KeyPair sharedKeys; + + @BeforeAll + static void setup() { + // Generate 256-bit keys for efficient unit testing + sharedKeys = ElGamalCipher.generateKeys(256); + } + + @Test + @DisplayName("Test Key Generation Validity") + void testKeyGeneration() { + Assertions.assertNotNull(sharedKeys.p()); + Assertions.assertNotNull(sharedKeys.g()); + Assertions.assertNotNull(sharedKeys.x()); + Assertions.assertNotNull(sharedKeys.y()); + + // Verify generator bounds: 1 < g < p + Assertions.assertTrue(sharedKeys.g().compareTo(BigInteger.ONE) > 0); + Assertions.assertTrue(sharedKeys.g().compareTo(sharedKeys.p()) < 0); + + // Verify private key bounds: 1 < x < p-1 + Assertions.assertTrue(sharedKeys.x().compareTo(BigInteger.ONE) > 0); + Assertions.assertTrue(sharedKeys.x().compareTo(sharedKeys.p().subtract(BigInteger.ONE)) < 0); + } + + @Test + @DisplayName("Security Check: Probabilistic Encryption") + void testSemanticSecurity() { + // Encrypting the same message twice MUST yield different ciphertexts + // due to the random ephemeral key 'k'. + BigInteger message = new BigInteger("123456789"); + + ElGamalCipher.CipherText c1 = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + ElGamalCipher.CipherText c2 = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + + // Check that the ephemeral keys (and thus 'a' components) were different + Assertions.assertNotEquals(c1.a(), c2.a(), "Ciphertexts must be randomized (Semantic Security violation)"); + Assertions.assertNotEquals(c1.b(), c2.b()); + + // But both must decrypt to the original message + Assertions.assertEquals(ElGamalCipher.decrypt(c1, sharedKeys.x(), sharedKeys.p()), message); + Assertions.assertEquals(ElGamalCipher.decrypt(c2, sharedKeys.x(), sharedKeys.p()), message); + } + + @ParameterizedTest + @MethodSource("provideMessages") + @DisplayName("Parameterized Test: Encrypt and Decrypt various messages") + void testEncryptDecrypt(String messageStr) { + BigInteger message = new BigInteger(messageStr.getBytes()); + + // Skip if message exceeds the test key size (256 bits) + if (message.compareTo(sharedKeys.p()) >= 0) { + return; + } + + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, sharedKeys.x(), sharedKeys.p()); + + Assertions.assertEquals(message, decrypted, "Decrypted BigInteger must match original"); + Assertions.assertEquals(messageStr, new String(decrypted.toByteArray()), "Decrypted string must match original"); + } + + static Stream provideMessages() { + return Stream.of("Hello World", "TheAlgorithms", "A", "1234567890", "!@#$%^&*()"); + } + + @Test + @DisplayName("Edge Case: Message equals 0") + void testMessageZero() { + BigInteger zero = BigInteger.ZERO; + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(zero, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, sharedKeys.x(), sharedKeys.p()); + + Assertions.assertEquals(zero, decrypted, "Should successfully encrypt/decrypt zero"); + } + + @Test + @DisplayName("Edge Case: Message equals p-1") + void testMessageMaxBound() { + BigInteger pMinus1 = sharedKeys.p().subtract(BigInteger.ONE); + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(pMinus1, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, sharedKeys.x(), sharedKeys.p()); + + Assertions.assertEquals(pMinus1, decrypted, "Should successfully encrypt/decrypt p-1"); + } + + @Test + @DisplayName("Negative Test: Message >= p should fail") + void testMessageTooLarge() { + BigInteger tooLarge = sharedKeys.p(); + Assertions.assertThrows(IllegalArgumentException.class, () -> ElGamalCipher.encrypt(tooLarge, sharedKeys.p(), sharedKeys.g(), sharedKeys.y())); + } + + @Test + @DisplayName("Negative Test: Decrypt with wrong private key") + void testWrongKeyDecryption() { + BigInteger message = new BigInteger("99999"); + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + + // Generate a fake private key + BigInteger wrongX = sharedKeys.x().add(BigInteger.ONE); + + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, wrongX, sharedKeys.p()); + + Assertions.assertNotEquals(message, decrypted, "Decryption with wrong key must yield incorrect result"); + } + + @Test + @DisplayName("Property Test: Multiplicative Homomorphism") + void testHomomorphism() { + BigInteger m1 = new BigInteger("50"); + BigInteger m2 = BigInteger.TEN; // Fix: Replaced new BigInteger("10") with BigInteger.TEN + + ElGamalCipher.CipherText c1 = ElGamalCipher.encrypt(m1, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + ElGamalCipher.CipherText c2 = ElGamalCipher.encrypt(m2, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + + // Multiply ciphertexts component-wise: (a1*a2, b1*b2) + BigInteger aNew = c1.a().multiply(c2.a()).mod(sharedKeys.p()); + BigInteger bNew = c1.b().multiply(c2.b()).mod(sharedKeys.p()); + ElGamalCipher.CipherText cCombined = new ElGamalCipher.CipherText(aNew, bNew); + + BigInteger decrypted = ElGamalCipher.decrypt(cCombined, sharedKeys.x(), sharedKeys.p()); + BigInteger expected = m1.multiply(m2).mod(sharedKeys.p()); + + Assertions.assertEquals(expected, decrypted, "Cipher must satisfy multiplicative homomorphism"); + } +} From 249b88fea2d8e7d425af8db053196e9b2cf1ed2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:03:26 +0100 Subject: [PATCH 091/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 13.1.0 to 13.2.0 (#7259) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 13.1.0 to 13.2.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-13.1.0...checkstyle-13.2.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f13169cece97..3716323bd115 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 13.1.0 + 13.2.0
From 3835c4822a651f90be03830f778bbc41c898c64c Mon Sep 17 00:00:00 2001 From: swativ15 <122958079+swativ15@users.noreply.github.com> Date: Fri, 6 Feb 2026 03:37:11 +0530 Subject: [PATCH 092/122] Refactor: simplify validation and improve backtracking cleanup (#7258) ### Summary This PR makes small readability and maintainability improvements to the algorithm implementation. ### Changes - Removed a redundant `n < 0` validation check since the method contract already ensures valid `n` - Replaced `current.remove(current.size() - 1)` with `current.removeLast()` to better express backtracking intent ### Rationale - Simplifies input validation without changing behavior - Uses the `Deque` API to make the backtracking step clearer and less error-prone ### Impact - No change in algorithm logic or time/space complexity - Output remains identical Co-authored-by: Swati Vusurumarthi --- .../com/thealgorithms/backtracking/ArrayCombination.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java b/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java index f8cd0c40c20e..d05e33a4242f 100644 --- a/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java +++ b/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java @@ -20,8 +20,8 @@ private ArrayCombination() { * @throws IllegalArgumentException if n or k are negative, or if k is greater than n. */ public static List> combination(int n, int k) { - if (n < 0 || k < 0 || k > n) { - throw new IllegalArgumentException("Invalid input: n must be non-negative, k must be non-negative and less than or equal to n."); + if (k < 0 || k > n) { + throw new IllegalArgumentException("Invalid input: 0 ≀ k ≀ n is required."); } List> combinations = new ArrayList<>(); @@ -48,7 +48,7 @@ private static void combine(List> combinations, List curr for (int i = start; i < n; i++) { current.add(i); combine(combinations, current, i + 1, n, k); - current.remove(current.size() - 1); // Backtrack + current.removeLast(); // Backtrack } } } From 8403b8feb198ce250a1e2aef4416216154300961 Mon Sep 17 00:00:00 2001 From: Adarsh-Melath Date: Mon, 9 Feb 2026 23:02:56 +0530 Subject: [PATCH 093/122] fix:shape's dimension constraints added correctyl (issue id: #7260) (#7261) * fix:shape's dimension constraints added correctyl (issue id: #7260) * fix: base changed to baseLength for better understanding * fix: base changed to baseLength for better understanding * base changed to baseLength for better understanding * fix:cleared some format issues * fix:cleared some format issues --- .../java/com/thealgorithms/maths/Area.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/Area.java b/src/main/java/com/thealgorithms/maths/Area.java index 1eba6666dde3..08807580cb03 100644 --- a/src/main/java/com/thealgorithms/maths/Area.java +++ b/src/main/java/com/thealgorithms/maths/Area.java @@ -10,17 +10,17 @@ private Area() { /** * String of IllegalArgumentException for radius */ - private static final String POSITIVE_RADIUS = "Must be a positive radius"; + private static final String POSITIVE_RADIUS = "Radius must be greater than 0"; /** * String of IllegalArgumentException for height */ - private static final String POSITIVE_HEIGHT = "Must be a positive height"; + private static final String POSITIVE_HEIGHT = "Height must be greater than 0"; /** * String of IllegalArgumentException for base */ - private static final String POSITIVE_BASE = "Must be a positive base"; + private static final String POSITIVE_BASE = "Base must be greater than 0"; /** * Calculate the surface area of a cube. @@ -30,7 +30,7 @@ private Area() { */ public static double surfaceAreaCube(final double sideLength) { if (sideLength <= 0) { - throw new IllegalArgumentException("Must be a positive sideLength"); + throw new IllegalArgumentException("Side length must be greater than 0"); } return 6 * sideLength * sideLength; } @@ -57,10 +57,10 @@ public static double surfaceAreaSphere(final double radius) { */ public static double surfaceAreaPyramid(final double sideLength, final double slantHeight) { if (sideLength <= 0) { - throw new IllegalArgumentException("Must be a positive sideLength"); + throw new IllegalArgumentException(""); } if (slantHeight <= 0) { - throw new IllegalArgumentException("Must be a positive slantHeight"); + throw new IllegalArgumentException("slant height must be greater than 0"); } double baseArea = sideLength * sideLength; double lateralSurfaceArea = 2 * sideLength * slantHeight; @@ -76,10 +76,10 @@ public static double surfaceAreaPyramid(final double sideLength, final double sl */ public static double surfaceAreaRectangle(final double length, final double width) { if (length <= 0) { - throw new IllegalArgumentException("Must be a positive length"); + throw new IllegalArgumentException("Length must be greater than 0"); } if (width <= 0) { - throw new IllegalArgumentException("Must be a positive width"); + throw new IllegalArgumentException("Width must be greater than 0"); } return length * width; } @@ -109,7 +109,7 @@ public static double surfaceAreaCylinder(final double radius, final double heigh */ public static double surfaceAreaSquare(final double sideLength) { if (sideLength <= 0) { - throw new IllegalArgumentException("Must be a positive sideLength"); + throw new IllegalArgumentException("Side Length must be greater than 0"); } return sideLength * sideLength; } @@ -121,14 +121,14 @@ public static double surfaceAreaSquare(final double sideLength) { * @param height height of triangle * @return area of given triangle */ - public static double surfaceAreaTriangle(final double base, final double height) { - if (base <= 0) { + public static double surfaceAreaTriangle(final double baseLength, final double height) { + if (baseLength <= 0) { throw new IllegalArgumentException(POSITIVE_BASE); } if (height <= 0) { throw new IllegalArgumentException(POSITIVE_HEIGHT); } - return base * height / 2; + return baseLength * height / 2; } /** @@ -138,14 +138,14 @@ public static double surfaceAreaTriangle(final double base, final double height) * @param height height of a parallelogram * @return area of given parallelogram */ - public static double surfaceAreaParallelogram(final double base, final double height) { - if (base <= 0) { + public static double surfaceAreaParallelogram(final double baseLength, final double height) { + if (baseLength <= 0) { throw new IllegalArgumentException(POSITIVE_BASE); } if (height <= 0) { throw new IllegalArgumentException(POSITIVE_HEIGHT); } - return base * height; + return baseLength * height; } /** @@ -156,17 +156,17 @@ public static double surfaceAreaParallelogram(final double base, final double he * @param height height of trapezium * @return area of given trapezium */ - public static double surfaceAreaTrapezium(final double base1, final double base2, final double height) { - if (base1 <= 0) { + public static double surfaceAreaTrapezium(final double baseLength1, final double baseLength2, final double height) { + if (baseLength1 <= 0) { throw new IllegalArgumentException(POSITIVE_BASE + 1); } - if (base2 <= 0) { + if (baseLength2 <= 0) { throw new IllegalArgumentException(POSITIVE_BASE + 2); } if (height <= 0) { throw new IllegalArgumentException(POSITIVE_HEIGHT); } - return (base1 + base2) * height / 2; + return (baseLength1 + baseLength2) * height / 2; } /** From 0c79d33eb591842c8cc809f02a9303ab843a1c37 Mon Sep 17 00:00:00 2001 From: Mohammed Vijahath <116938255+vizahat36@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:30:15 +0530 Subject: [PATCH 094/122] Add Tower of Hanoi recursive algorithm (#7235) * Add Tower of Hanoi recursive algorithm with tests * Fix SpotBugs issues and format TowerOfHanoi tests * Enhance existing TowerOfHanoi and remove duplicate recursion version * Fix clang-format issue --------- Co-authored-by: Deniz Altunkapan --- .../puzzlesandgames/TowerOfHanoi.java | 51 +++++++++++-------- .../puzzlesandgames/TowerOfHanoiTest.java | 32 ++++++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java index 72e9a14ac070..d94bef69cd3a 100644 --- a/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java +++ b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java @@ -3,27 +3,32 @@ import java.util.List; /** - * The {@code TowerOfHanoi} class provides a recursive solution to the Tower of Hanoi puzzle. - * This puzzle involves moving a set of discs from one pole to another, following specific rules: + * Recursive solution to the Tower of Hanoi puzzle. + * + *

+ * The puzzle rules are: * 1. Only one disc can be moved at a time. * 2. A disc can only be placed on top of a larger disc. * 3. All discs must start on one pole and end on another. + *

* - * This implementation recursively calculates the steps required to solve the puzzle and stores them - * in a provided list. + *

+ * The recursion follows three steps: + * 1. Move {@code n-1} discs from start to intermediate. + * 2. Move the largest disc from start to end. + * 3. Move {@code n-1} discs from intermediate to end. + *

* *

- * For more information about the Tower of Hanoi, see - * Tower of Hanoi on Wikipedia. + * Time Complexity: O(2^n) - exponential due to recursive expansion. + * Space Complexity: O(n) - recursion stack depth. *

* - * The {@code shift} method takes the number of discs and the names of the poles, - * and appends the steps required to solve the puzzle to the provided list. - * Time Complexity: O(2^n) - Exponential time complexity due to the recursive nature of the problem. - * Space Complexity: O(n) - Linear space complexity due to the recursion stack. - * Wikipedia: https://en.wikipedia.org/wiki/Tower_of_Hanoi + *

+ * See Tower of Hanoi on Wikipedia. + *

*/ -final class TowerOfHanoi { +public final class TowerOfHanoi { private TowerOfHanoi() { } @@ -36,6 +41,7 @@ private TowerOfHanoi() { * @param intermediatePole The name of the intermediate pole used as a temporary holding area. * @param endPole The name of the end pole to which discs are moved. * @param result A list to store the steps required to solve the puzzle. + * @throws IllegalArgumentException if {@code n} is negative. * *

* This method is called recursively to move n-1 discs @@ -51,15 +57,20 @@ private TowerOfHanoi() { *

*/ public static void shift(int n, String startPole, String intermediatePole, String endPole, List result) { - if (n != 0) { - // Move n-1 discs from startPole to intermediatePole - shift(n - 1, startPole, endPole, intermediatePole, result); + if (n < 0) { + throw new IllegalArgumentException("Number of discs must be non-negative"); + } + if (n == 0) { + return; + } - // Add the move of the nth disc from startPole to endPole - result.add(String.format("Move %d from %s to %s", n, startPole, endPole)); + // Move n-1 discs from startPole to intermediatePole + shift(n - 1, startPole, endPole, intermediatePole, result); - // Move the n-1 discs from intermediatePole to endPole - shift(n - 1, intermediatePole, startPole, endPole, result); - } + // Add the move of the nth disc from startPole to endPole + result.add(String.format("Move %d from %s to %s", n, startPole, endPole)); + + // Move the n-1 discs from intermediatePole to endPole + shift(n - 1, intermediatePole, startPole, endPole, result); } } diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java index 42669eb03bb4..f0a2686d3e4b 100644 --- a/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java +++ b/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java @@ -1,14 +1,31 @@ package com.thealgorithms.puzzlesandgames; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class TowerOfHanoiTest { + @ParameterizedTest + @MethodSource("diskCountAndMoveCount") + void testMoveCountMatchesFormula(int disks, int expectedMoves) { + List result = new ArrayList<>(); + TowerOfHanoi.shift(disks, "A", "B", "C", result); + assertEquals(expectedMoves, result.size()); + } + + private static Stream diskCountAndMoveCount() { + return Stream.of(Arguments.of(1, 1), Arguments.of(2, 3), Arguments.of(3, 7), Arguments.of(4, 15), Arguments.of(5, 31), Arguments.of(10, 1023)); + } + @Test public void testHanoiWithOneDisc() { List result = new ArrayList<>(); @@ -39,6 +56,15 @@ public void testHanoiWithThreeDiscs() { assertEquals(expected, result); } + @Test + public void testHanoiWithDifferentPoles() { + List result = new ArrayList<>(); + TowerOfHanoi.shift(2, "X", "Y", "Z", result); + + List expected = List.of("Move 1 from X to Y", "Move 2 from X to Z", "Move 1 from Y to Z"); + assertEquals(expected, result); + } + @Test public void testHanoiWithZeroDiscs() { List result = new ArrayList<>(); @@ -47,4 +73,10 @@ public void testHanoiWithZeroDiscs() { // There should be no moves if there are 0 discs assertTrue(result.isEmpty()); } + + @Test + public void testHanoiWithNegativeDiscsThrows() { + List result = new ArrayList<>(); + assertThrows(IllegalArgumentException.class, () -> TowerOfHanoi.shift(-1, "Pole1", "Pole2", "Pole3", result)); + } } From 504b5283eb75f9416693740eba48c5cea9eb28bd Mon Sep 17 00:00:00 2001 From: Muhammad Muneeb Mubashar Date: Fri, 13 Feb 2026 01:43:27 -0800 Subject: [PATCH 095/122] Refactor getAbsValue method to use Math.abs (#7266) --- src/main/java/com/thealgorithms/maths/AbsoluteValue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteValue.java b/src/main/java/com/thealgorithms/maths/AbsoluteValue.java index b9279d5a244a..114eb71b1015 100644 --- a/src/main/java/com/thealgorithms/maths/AbsoluteValue.java +++ b/src/main/java/com/thealgorithms/maths/AbsoluteValue.java @@ -11,6 +11,6 @@ private AbsoluteValue() { * @return The absolute value of the {@code number} */ public static int getAbsValue(int number) { - return number < 0 ? -number : number; + return Math.abs(number); } } From c8d029107ce397c6d427a23a5127003d87fb0792 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:57:18 +0100 Subject: [PATCH 096/122] chore(deps): bump org.junit:junit-bom from 6.0.2 to 6.0.3 (#7271) Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.2...r6.0.3) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3716323bd115..2445a1e920a8 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.junit junit-bom - 6.0.2 + 6.0.3 pom import From dfa6bf06910b2c989a0e4acb82155ebf51d869f9 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:20:34 +0100 Subject: [PATCH 097/122] style: remove redundant PMD exclusions (#7272) --- pmd-exclude.properties | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pmd-exclude.properties b/pmd-exclude.properties index a3c95b12fa4b..64562c524728 100644 --- a/pmd-exclude.properties +++ b/pmd-exclude.properties @@ -53,14 +53,11 @@ com.thealgorithms.maths.Gaussian=UselessParentheses com.thealgorithms.maths.GcdSolutionWrapper=UselessParentheses com.thealgorithms.maths.HeronsFormula=UselessParentheses com.thealgorithms.maths.JugglerSequence=UselessMainMethod -com.thealgorithms.maths.KaprekarNumbers=UselessParentheses com.thealgorithms.maths.KeithNumber=UselessMainMethod,UselessParentheses -com.thealgorithms.maths.LeonardoNumber=UselessParentheses com.thealgorithms.maths.LinearDiophantineEquationsSolver=UselessMainMethod,UselessParentheses com.thealgorithms.maths.MagicSquare=UselessMainMethod com.thealgorithms.maths.PiNilakantha=UselessMainMethod com.thealgorithms.maths.Prime.PrimeCheck=UselessMainMethod -com.thealgorithms.maths.PythagoreanTriple=UselessMainMethod com.thealgorithms.maths.RomanNumeralUtil=UselessParentheses com.thealgorithms.maths.SecondMinMax=UselessParentheses com.thealgorithms.maths.SecondMinMaxTest=UnnecessaryFullyQualifiedName @@ -71,7 +68,6 @@ com.thealgorithms.maths.TrinomialTriangle=UselessMainMethod,UselessParentheses com.thealgorithms.maths.VectorCrossProduct=UselessMainMethod com.thealgorithms.maths.Volume=UselessParentheses com.thealgorithms.matrix.RotateMatrixBy90Degrees=UselessMainMethod -com.thealgorithms.misc.Sparsity=UselessParentheses com.thealgorithms.others.BankersAlgorithm=UselessMainMethod com.thealgorithms.others.BrianKernighanAlgorithm=UselessMainMethod com.thealgorithms.others.CRC16=UselessMainMethod,UselessParentheses @@ -79,11 +75,9 @@ com.thealgorithms.others.CRC32=UselessMainMethod com.thealgorithms.others.Damm=UnnecessaryFullyQualifiedName,UselessMainMethod com.thealgorithms.others.Dijkstra=UselessMainMethod com.thealgorithms.others.GaussLegendre=UselessMainMethod -com.thealgorithms.others.HappyNumbersSeq=UselessMainMethod com.thealgorithms.others.Huffman=UselessMainMethod com.thealgorithms.others.InsertDeleteInArray=UselessMainMethod com.thealgorithms.others.KochSnowflake=UselessMainMethod -com.thealgorithms.others.Krishnamurthy=UselessMainMethod com.thealgorithms.others.LinearCongruentialGenerator=UselessMainMethod com.thealgorithms.others.Luhn=UnnecessaryFullyQualifiedName,UselessMainMethod com.thealgorithms.others.Mandelbrot=UselessMainMethod,UselessParentheses @@ -94,7 +88,6 @@ com.thealgorithms.others.PerlinNoise=UselessMainMethod,UselessParentheses com.thealgorithms.others.QueueUsingTwoStacks=UselessParentheses com.thealgorithms.others.Trieac=UselessMainMethod,UselessParentheses com.thealgorithms.others.Verhoeff=UnnecessaryFullyQualifiedName,UselessMainMethod -com.thealgorithms.puzzlesandgames.Sudoku=UselessMainMethod com.thealgorithms.recursion.DiceThrower=UselessMainMethod com.thealgorithms.searches.HowManyTimesRotated=UselessMainMethod com.thealgorithms.searches.InterpolationSearch=UselessParentheses @@ -108,15 +101,11 @@ com.thealgorithms.sorts.MergeSortNoExtraSpace=UselessParentheses com.thealgorithms.sorts.RadixSort=UselessParentheses com.thealgorithms.sorts.TreeSort=UselessMainMethod com.thealgorithms.sorts.WiggleSort=UselessParentheses -com.thealgorithms.stacks.LargestRectangle=UselessMainMethod com.thealgorithms.stacks.MaximumMinimumWindow=UselessMainMethod com.thealgorithms.stacks.PostfixToInfix=UselessParentheses -com.thealgorithms.strings.Alphabetical=UselessMainMethod com.thealgorithms.strings.HorspoolSearch=UnnecessaryFullyQualifiedName,UselessParentheses -com.thealgorithms.strings.KMP=UselessMainMethod com.thealgorithms.strings.Lower=UselessMainMethod com.thealgorithms.strings.Palindrome=UselessParentheses com.thealgorithms.strings.Pangram=UselessMainMethod -com.thealgorithms.strings.RabinKarp=UselessMainMethod com.thealgorithms.strings.Rotation=UselessMainMethod com.thealgorithms.strings.Upper=UselessMainMethod From 1646edaeb96a27add8d83788782550ee2048b263 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Wed, 18 Feb 2026 18:06:08 +0100 Subject: [PATCH 098/122] style: remove redundant exclusions (#7276) --- spotbugs-exclude.xml | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 1390387bacdf..7483d37daa57 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -8,18 +8,12 @@ - - - - - - @@ -32,15 +26,9 @@ - - - - - - @@ -50,15 +38,9 @@ - - - - - - @@ -71,9 +53,6 @@ - - - @@ -83,9 +62,6 @@ - - - @@ -117,9 +93,6 @@ - - - @@ -150,9 +123,6 @@ - - - @@ -189,24 +159,15 @@ - - - - - - - - - From c9bda3dad761c5b27edc36e32b8b9c746ebda345 Mon Sep 17 00:00:00 2001 From: Syed Mohammad Saad <134770714+SYEDMDSAAD@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:54:34 +0530 Subject: [PATCH 099/122] Add string algorithms: RemoveStars and ComplexNumberMultiply (#7275) * Add RemoveStars and ComplexNumberMultiply string algorithms * Add RemoveStars and ComplexNumberMultiply string algorithms * Add unit tests for RemoveStars and ComplexNumber Multiply * Fix checkstyle * Remove redundant main method * Move ComplexNumberMultiply to maths package and add input validation with tests * Apply spotless formatting --------- Co-authored-by: Deniz Altunkapan --- .../maths/ComplexNumberMultiply.java | 32 +++++++++++++++++ .../thealgorithms/strings/RemoveStars.java | 31 +++++++++++++++++ .../maths/ComplexNumberMultiplyTest.java | 34 +++++++++++++++++++ .../strings/RemoveStarsTest.java | 28 +++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java create mode 100644 src/main/java/com/thealgorithms/strings/RemoveStars.java create mode 100644 src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java create mode 100644 src/test/java/com/thealgorithms/strings/RemoveStarsTest.java diff --git a/src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java b/src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java new file mode 100644 index 000000000000..4b68b7824574 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java @@ -0,0 +1,32 @@ +package com.thealgorithms.maths; + +/** + * Multiplies two complex numbers represented as strings in the form "a+bi". + * Supports negative values and validates input format. + */ +public final class ComplexNumberMultiply { + + private ComplexNumberMultiply() { + } + + private static int[] parse(String num) { + if (num == null || !num.matches("-?\\d+\\+-?\\d+i")) { + throw new IllegalArgumentException("Invalid complex number format: " + num); + } + + String[] parts = num.split("\\+"); + int real = Integer.parseInt(parts[0]); + int imaginary = Integer.parseInt(parts[1].replace("i", "")); + return new int[] {real, imaginary}; + } + + public static String multiply(String num1, String num2) { + int[] a = parse(num1); + int[] b = parse(num2); + + int real = a[0] * b[0] - a[1] * b[1]; + int imaginary = a[0] * b[1] + a[1] * b[0]; + + return real + "+" + imaginary + "i"; + } +} diff --git a/src/main/java/com/thealgorithms/strings/RemoveStars.java b/src/main/java/com/thealgorithms/strings/RemoveStars.java new file mode 100644 index 000000000000..816311e9da84 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/RemoveStars.java @@ -0,0 +1,31 @@ +package com.thealgorithms.strings; + +/** + * Removes characters affected by '*' in a string. + * Each '*' deletes the closest non-star character to its left. + * + * Example: + * Input: leet**cod*e + * Output: lecoe + */ + +public final class RemoveStars { + + private RemoveStars() { + } + + public static String removeStars(String s) { + StringBuilder result = new StringBuilder(); + + for (char c : s.toCharArray()) { + if (c == '*') { + if (result.length() > 0) { + result.deleteCharAt(result.length() - 1); + } + } else { + result.append(c); + } + } + return result.toString(); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java b/src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java new file mode 100644 index 000000000000..02e964b53771 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class ComplexNumberMultiplyTest { + + @Test + void testExample() { + assertEquals("0+2i", ComplexNumberMultiply.multiply("1+1i", "1+1i")); + } + + @Test + void testNegative() { + assertEquals("0+-2i", ComplexNumberMultiply.multiply("1+-1i", "1+-1i")); + } + + @Test + void testZero() { + assertEquals("0+0i", ComplexNumberMultiply.multiply("0+0i", "5+3i")); + } + + @Test + void testInvalidFormat() { + assertThrows(IllegalArgumentException.class, () -> ComplexNumberMultiply.multiply("1+1", "1+1i")); + } + + @Test + void testNullInput() { + assertThrows(IllegalArgumentException.class, () -> ComplexNumberMultiply.multiply(null, "1+1i")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/RemoveStarsTest.java b/src/test/java/com/thealgorithms/strings/RemoveStarsTest.java new file mode 100644 index 000000000000..3beb2e83399b --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/RemoveStarsTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class RemoveStarsTest { + + @Test + void testExampleCase() { + assertEquals("lecoe", RemoveStars.removeStars("leet**cod*e")); + } + + @Test + void testAllStars() { + assertEquals("", RemoveStars.removeStars("abc***")); + } + + @Test + void testNoStars() { + assertEquals("hello", RemoveStars.removeStars("hello")); + } + + @Test + void testSingleCharacter() { + assertEquals("", RemoveStars.removeStars("a*")); + } +} From 2ae1bdfd9a3897d206c0e62751b7ec562dcafb08 Mon Sep 17 00:00:00 2001 From: Neha-2005-VCE Date: Thu, 19 Feb 2026 00:04:41 +0530 Subject: [PATCH 100/122] feat: add contains() method to DynamicArray (#7270) * feat: add contains() method to DynamicArray * style: fix clang formatting * style: fix clang format issues * style: apply remaining clang formatting --------- Co-authored-by: Deniz Altunkapan --- .../dynamicarray/DynamicArray.java | 43 +++++++++++++++---- .../dynamicarray/DynamicArrayTest.java | 19 ++++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java b/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java index cd5dc580b694..dbdb2d806209 100644 --- a/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java +++ b/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java @@ -63,7 +63,8 @@ public void add(final E element) { * * @param index the index at which the element is to be placed * @param element the element to be inserted at the specified index - * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the number of elements + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or + * equal to the number of elements */ public void put(final int index, E element) { if (index < 0) { @@ -82,7 +83,8 @@ public void put(final int index, E element) { * * @param index the index of the element to retrieve * @return the element at the specified index - * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the current size + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or + * equal to the current size */ @SuppressWarnings("unchecked") public E get(final int index) { @@ -97,7 +99,8 @@ public E get(final int index) { * * @param index the index of the element to be removed * @return the element that was removed from the array - * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the current size + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or + * equal to the current size */ public E remove(final int index) { if (index < 0 || index >= size) { @@ -127,6 +130,21 @@ public boolean isEmpty() { return size == 0; } + /** + * Checks whether the array contains the specified element. + * + * @param element the element to check for + * @return true if the array contains the specified element, false otherwise + */ + public boolean contains(final E element) { + for (int i = 0; i < size; i++) { + if (Objects.equals(elements[i], element)) { + return true; + } + } + return false; + } + /** * Returns a sequential stream with this collection as its source. * @@ -137,7 +155,8 @@ public Stream stream() { } /** - * Ensures that the array has enough capacity to hold the specified number of elements. + * Ensures that the array has enough capacity to hold the specified number of + * elements. * * @param minCapacity the minimum capacity required */ @@ -150,7 +169,8 @@ private void ensureCapacity(int minCapacity) { /** * Removes the element at the specified index without resizing the array. - * This method shifts any subsequent elements to the left and clears the last element. + * This method shifts any subsequent elements to the left and clears the last + * element. * * @param index the index of the element to remove */ @@ -163,7 +183,8 @@ private void fastRemove(int index) { } /** - * Returns a string representation of the array, including only the elements that are currently stored. + * Returns a string representation of the array, including only the elements + * that are currently stored. * * @return a string containing the elements in the array */ @@ -227,7 +248,9 @@ public E next() { /** * Removes the last element returned by this iterator. * - * @throws IllegalStateException if the next method has not yet been called, or the remove method has already been called after the last call to the next method + * @throws IllegalStateException if the next method has not yet been called, or + * the remove method has already been called after + * the last call to the next method */ @Override public void remove() { @@ -242,7 +265,8 @@ public void remove() { /** * Checks for concurrent modifications to the array during iteration. * - * @throws ConcurrentModificationException if the array has been modified structurally + * @throws ConcurrentModificationException if the array has been modified + * structurally */ private void checkForComodification() { if (modCount != expectedModCount) { @@ -251,7 +275,8 @@ private void checkForComodification() { } /** - * Performs the given action for each remaining element in the iterator until all elements have been processed. + * Performs the given action for each remaining element in the iterator until + * all elements have been processed. * * @param action the action to be performed for each element * @throws NullPointerException if the specified action is null diff --git a/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java b/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java index 8fdc93e1ca22..39e3fa0abe77 100644 --- a/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java +++ b/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java @@ -255,4 +255,23 @@ public void testCapacityDoubling() { assertEquals(3, array.getSize()); assertEquals("Charlie", array.get(2)); } + + @Test + public void testContains() { + DynamicArray array = new DynamicArray<>(); + array.add(1); + array.add(2); + array.add(3); + + assertTrue(array.contains(2)); + assertFalse(array.contains(5)); + } + + @Test + public void testContainsWithNull() { + DynamicArray array = new DynamicArray<>(); + array.add(null); + + assertTrue(array.contains(null)); + } } From 0a2c7f2e3bb539d3aee68673b5d743d178d455b2 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Wed, 18 Feb 2026 20:32:32 +0100 Subject: [PATCH 101/122] style: include `BL_BURYING_LOGIC` (#7277) --- spotbugs-exclude.xml | 3 -- .../searches/RecursiveBinarySearch.java | 33 +++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 7483d37daa57..a8eedcfed402 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -90,9 +90,6 @@ - - - diff --git a/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java b/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java index daf0c12c0978..1716e78964ae 100644 --- a/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java @@ -23,28 +23,27 @@ public int find(T[] arr, T target) { // Recursive binary search function public int binsear(T[] arr, int left, int right, T target) { - if (right >= left) { - int mid = left + (right - left) / 2; - - // Compare the element at the middle with the target - int comparison = arr[mid].compareTo(target); + if (right < left) { + // Element is not present in the array + return -1; + } + final int mid = left + (right - left) / 2; - // If the element is equal to the target, return its index - if (comparison == 0) { - return mid; - } + // Compare the element at the middle with the target + final int comparison = arr[mid].compareTo(target); - // If the element is greater than the target, search in the left subarray - if (comparison > 0) { - return binsear(arr, left, mid - 1, target); - } + // If the element is equal to the target, return its index + if (comparison == 0) { + return mid; + } - // Otherwise, search in the right subarray - return binsear(arr, mid + 1, right, target); + // If the element is greater than the target, search in the left subarray + if (comparison > 0) { + return binsear(arr, left, mid - 1, target); } - // Element is not present in the array - return -1; + // Otherwise, search in the right subarray + return binsear(arr, mid + 1, right, target); } public static void main(String[] args) { From 29ce2ef66607083e922b389650af160b7606903a Mon Sep 17 00:00:00 2001 From: Ruturaj Jadhav Date: Fri, 20 Feb 2026 02:41:56 +0530 Subject: [PATCH 102/122] Add RangeSumQuery algorithm in prefix folder (#7280) * add Subarray Sum Equals K using prefix sum * Add RangeSumQuery algorithm in prefix folder * chore: apply clang-format to RangeSumQueryTest file * Add import statement for JUnit test class --------- Co-authored-by: Deniz Altunkapan --- .../prefixsum/RangeSumQuery.java | 73 +++++++++++++++++++ .../prefixsum/RangeSumQueryTest.java | 73 +++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java create mode 100644 src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java diff --git a/src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java b/src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java new file mode 100644 index 000000000000..14a02a2de4d0 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java @@ -0,0 +1,73 @@ +package com.thealgorithms.prefixsum; + +/** + * Implements an algorithm to efficiently compute the sum of elements + * between any two indices in an integer array using the Prefix Sum technique. + * + *

+ * Given an array nums, this algorithm precomputes the prefix sum array + * to allow O(1) sum queries for any range [left, right]. + *

+ * + *

+ * Let prefixSum[i] be the sum of elements from index 0 to i-1. + * The sum of elements from left to right is: + * + *

+ * prefixSum[right + 1] - prefixSum[left]
+ * 
+ *

+ * + *

+ * Time Complexity: O(N) for preprocessing, O(1) per query
+ * Space Complexity: O(N) + *

+ * + * @author Ruturaj Jadhav, ruturajjadhav07 + */ +public final class RangeSumQuery { + + private RangeSumQuery() { + // Utility class; prevent instantiation + } + + /** + * Computes the prefix sum array for efficient range queries. + * + * @param nums The input integer array. + * @return Prefix sum array where prefixSum[i+1] = sum of nums[0..i]. + * @throws IllegalArgumentException if nums is null. + */ + public static int[] buildPrefixSum(int[] nums) { + if (nums == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + return prefixSum; + } + + /** + * Returns the sum of elements from index left to right (inclusive) + * using the provided prefix sum array. + * + * @param prefixSum The prefix sum array computed using buildPrefixSum. + * @param left The start index (inclusive). + * @param right The end index (inclusive). + * @return The sum of elements in the range [left, right]. + * @throws IllegalArgumentException if indices are invalid. + */ + public static int sumRange(int[] prefixSum, int left, int right) { + if (prefixSum == null) { + throw new IllegalArgumentException("Prefix sum array cannot be null"); + } + if (left < 0 || right >= prefixSum.length - 1 || left > right) { + throw new IllegalArgumentException("Invalid range indices"); + } + return prefixSum[right + 1] - prefixSum[left]; + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java b/src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java new file mode 100644 index 000000000000..12072318ac74 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java @@ -0,0 +1,73 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link RangeSumQuery}. + */ +class RangeSumQueryTest { + + @Test + void testBasicExample() { + int[] nums = {1, 2, 3, 4, 5}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(6, RangeSumQuery.sumRange(prefixSum, 0, 2)); // 1+2+3 + assertEquals(9, RangeSumQuery.sumRange(prefixSum, 1, 3)); // 2+3+4 + assertEquals(15, RangeSumQuery.sumRange(prefixSum, 0, 4)); // 1+2+3+4+5 + assertEquals(12, RangeSumQuery.sumRange(prefixSum, 2, 4)); // 3+4+5 + } + + @Test + void testSingleElement() { + int[] nums = {7}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(7, RangeSumQuery.sumRange(prefixSum, 0, 0)); + } + + @Test + void testAllZeros() { + int[] nums = {0, 0, 0, 0}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(0, RangeSumQuery.sumRange(prefixSum, 0, 3)); + assertEquals(0, RangeSumQuery.sumRange(prefixSum, 1, 2)); + } + + @Test + void testNegativeNumbers() { + int[] nums = {-1, 2, -3, 4}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(-2, RangeSumQuery.sumRange(prefixSum, 0, 2)); // -1+2-3 + assertEquals(3, RangeSumQuery.sumRange(prefixSum, 1, 3)); // 2-3+4 + } + + @Test + void testEmptyArrayThrowsException() { + int[] nums = {}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, 0, 0)); + } + + @Test + void testNullArrayThrowsException() { + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.buildPrefixSum(null)); + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(null, 0, 0)); + } + + @Test + void testInvalidIndicesThrowsException() { + int[] nums = {1, 2, 3}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, 1, 5)); + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, 2, 1)); + } +} From 7c38e5acd29a22e29c6954ae7088889a4afde689 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:48:12 +0100 Subject: [PATCH 103/122] style: include `DM_NEXTINT_VIA_NEXTDOUBLE` (#7282) --- spotbugs-exclude.xml | 3 --- .../com/thealgorithms/searches/LinearSearchThreadTest.java | 6 ++++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index a8eedcfed402..13d72334e594 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -11,9 +11,6 @@ - - - diff --git a/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java b/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java index 534c2a4487b2..c0d82489209f 100644 --- a/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java +++ b/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Random; import org.junit.jupiter.api.Test; class LinearSearchThreadTest { @@ -62,10 +63,11 @@ void testSearcherEmptySegment() throws InterruptedException { void testSearcherRandomNumbers() throws InterruptedException { int size = 200; int[] array = new int[size]; + Random random = new Random(); for (int i = 0; i < size; i++) { - array[i] = (int) (Math.random() * 100); + array[i] = random.nextInt(100); } - int target = array[(int) (Math.random() * size)]; // Randomly select a target that is present + final int target = array[random.nextInt(size)]; // Randomly select a target that is present Searcher searcher = new Searcher(array, 0, size, target); searcher.start(); searcher.join(); From 2d443a9991f7a80f1ab6bca437d1b953f7d05420 Mon Sep 17 00:00:00 2001 From: Mohan E <777emohan@gmail.com> Date: Sat, 21 Feb 2026 18:40:37 +0530 Subject: [PATCH 104/122] Add time and space complexity documentation to LongestNonRepetitiveSubstring (#7284) --- .../strings/LongestNonRepetitiveSubstring.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java b/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java index 6808cd50602f..51e8dc6b02c3 100644 --- a/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java +++ b/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java @@ -13,6 +13,12 @@ private LongestNonRepetitiveSubstring() { /** * Finds the length of the longest substring without repeating characters. * + * Uses the sliding window technique with a HashMap to track + * the last seen index of each character. + * + * Time Complexity: O(n), where n is the length of the input string. + * Space Complexity: O(min(n, m)), where m is the size of the character set. + * * @param s the input string * @return the length of the longest non-repetitive substring */ From 109df1f39837d0102646920dcfa353ae3c09e7cc Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Sat, 21 Feb 2026 16:27:13 +0100 Subject: [PATCH 105/122] style: include `SUA_SUSPICIOUS_UNINITIALIZED_ARRAY` (#7285) --- spotbugs-exclude.xml | 3 --- .../com/thealgorithms/divideandconquer/ClosestPair.java | 4 ---- .../thealgorithms/divideandconquer/ClosestPairTest.java | 8 -------- 3 files changed, 15 deletions(-) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 13d72334e594..8c42802520e3 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -96,9 +96,6 @@ - - - diff --git a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java index 4c9c40c83174..323098a99887 100644 --- a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java +++ b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java @@ -66,10 +66,6 @@ public static class Location { } } - public Location[] createLocation(int numberValues) { - return new Location[numberValues]; - } - public Location buildLocation(double x, double y) { return new Location(x, y); } diff --git a/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java b/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java index 38784228d68e..b25fd796b112 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java @@ -16,14 +16,6 @@ public void testBuildLocation() { assertEquals(4.0, point.y); } - @Test - public void testCreateLocation() { - ClosestPair cp = new ClosestPair(5); - ClosestPair.Location[] locations = cp.createLocation(5); - assertNotNull(locations); - assertEquals(5, locations.length); - } - @Test public void testXPartition() { ClosestPair cp = new ClosestPair(5); From 12935c291def0a4079c961a0aa7652177b1b05f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Eker?= Date: Sun, 22 Feb 2026 21:15:22 +0300 Subject: [PATCH 106/122] Add Longest Repeated Substring algorithm (#7286) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Longest Repeated Substring algorithm Implement LongestRepeatedSubstring in the strings package using the existing SuffixArray and Kasai's algorithm for LCP array construction. Includes parameterized unit tests covering typical and edge cases. * style: reformat test file per clang-format and add coverage test --------- Co-authored-by: Çağlar Eker --- .../strings/LongestRepeatedSubstring.java | 83 +++++++++++++++++++ .../strings/LongestRepeatedSubstringTest.java | 33 ++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java create mode 100644 src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java diff --git a/src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java b/src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java new file mode 100644 index 000000000000..87c9278fd4bf --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java @@ -0,0 +1,83 @@ +package com.thealgorithms.strings; + +/** + * Finds the longest substring that occurs at least twice in a given string. + * + *

Uses the suffix array (via {@link SuffixArray}) and Kasai's algorithm + * to build the LCP (Longest Common Prefix) array, then returns the substring + * corresponding to the maximum LCP value.

+ * + *

Time complexity: O(n logΒ² n) for suffix array construction + O(n) for LCP.

+ * + * @see Longest repeated substring problem + * @see SuffixArray + */ +public final class LongestRepeatedSubstring { + + private LongestRepeatedSubstring() { + } + + /** + * Returns the longest substring that appears at least twice in the given text. + * + * @param text the input string + * @return the longest repeated substring, or an empty string if none exists + */ + public static String longestRepeatedSubstring(String text) { + if (text == null || text.length() <= 1) { + return ""; + } + + final int[] suffixArray = SuffixArray.buildSuffixArray(text); + final int[] lcp = buildLcpArray(text, suffixArray); + + int maxLen = 0; + int maxIdx = 0; + for (int i = 0; i < lcp.length; i++) { + if (lcp[i] > maxLen) { + maxLen = lcp[i]; + maxIdx = suffixArray[i + 1]; + } + } + + return text.substring(maxIdx, maxIdx + maxLen); + } + + /** + * Builds the LCP (Longest Common Prefix) array using Kasai's algorithm. + * + *

LCP[i] is the length of the longest common prefix between the suffixes + * at positions suffixArray[i] and suffixArray[i+1] in sorted order.

+ * + * @param text the original string + * @param suffixArray the suffix array of the string + * @return the LCP array of length n-1 + */ + static int[] buildLcpArray(String text, int[] suffixArray) { + final int n = text.length(); + final int[] rank = new int[n]; + final int[] lcp = new int[n - 1]; + + for (int i = 0; i < n; i++) { + rank[suffixArray[i]] = i; + } + + int k = 0; + for (int i = 0; i < n; i++) { + if (rank[i] == n - 1) { + k = 0; + continue; + } + final int j = suffixArray[rank[i] + 1]; + while (i + k < n && j + k < n && text.charAt(i + k) == text.charAt(j + k)) { + k++; + } + lcp[rank[i]] = k; + if (k > 0) { + k--; + } + } + + return lcp; + } +} diff --git a/src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java new file mode 100644 index 000000000000..366f6863340d --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +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; + +class LongestRepeatedSubstringTest { + + @ParameterizedTest(name = "\"{0}\" -> \"{1}\"") + @MethodSource("provideTestCases") + void testLongestRepeatedSubstring(String input, String expected) { + assertEquals(expected, LongestRepeatedSubstring.longestRepeatedSubstring(input)); + } + + private static Stream provideTestCases() { + return Stream.of(Arguments.of("banana", "ana"), Arguments.of("abcabc", "abc"), Arguments.of("aaaa", "aaa"), Arguments.of("abcd", ""), Arguments.of("a", ""), Arguments.of("", ""), Arguments.of(null, ""), Arguments.of("aab", "a"), Arguments.of("aa", "a"), Arguments.of("mississippi", "issi")); + } + + @ParameterizedTest(name = "\"{0}\" -> LCP={1}") + @MethodSource("provideLcpTestCases") + void testBuildLcpArray(String input, int[] expectedLcp) { + int[] suffixArray = SuffixArray.buildSuffixArray(input); + assertArrayEquals(expectedLcp, LongestRepeatedSubstring.buildLcpArray(input, suffixArray)); + } + + private static Stream provideLcpTestCases() { + return Stream.of(Arguments.of("banana", new int[] {1, 3, 0, 0, 2}), Arguments.of("ab", new int[] {0})); + } +} From 10114b79ae8869e9351c8fd38eca798978119204 Mon Sep 17 00:00:00 2001 From: Vasundhara117 Date: Tue, 24 Feb 2026 00:36:51 +0530 Subject: [PATCH 107/122] Add ReverseQueueRecursion.java: Reverse a Queue using recursion (#7281) * Create ReverseQueueRecursion Add ReverseQueueRecursion.java: - Reverses a Queue using recursion (generic ) - Includes unit tests in ReverseQueueRecursionTest.java - Follows repo style (final class, private constructor, Javadoc) * Create ReverseQueueRecursionTest.java Add ReverseQueueRecursionTest as required by CONTRIBUTING.md * Rename ReverseQueueRecursion to ReverseQueueRecursion.java * Update ReverseQueueRecursion.java * Update ReverseQueueRecursion.java * Update ReverseQueueRecursion.java * Update ReverseQueueRecursion.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursion.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursion.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursionTest.java * Update ReverseQueueRecursion.java --- .../queues/ReverseQueueRecursion.java | 28 ++++++++++ .../queues/ReverseQueueRecursionTest.java | 54 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java create mode 100644 src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java b/src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java new file mode 100644 index 000000000000..79275dcefe20 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java @@ -0,0 +1,28 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.Queue; + +/** + * Reverse a queue using recursion. + */ +public final class ReverseQueueRecursion { + private ReverseQueueRecursion() { + // private constructor to prevent instantiation + } + + /** + * Reverses the given queue recursively. + * + * @param queue the queue to reverse + * @param the type of elements in the queue + */ + public static void reverseQueue(final Queue queue) { + if (queue == null || queue.isEmpty()) { + return; + } + + final T front = queue.poll(); + reverseQueue(queue); + queue.add(front); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java b/src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java new file mode 100644 index 000000000000..e3abe15b6a46 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.LinkedList; +import java.util.Queue; +import org.junit.jupiter.api.Test; + +class ReverseQueueRecursionTest { + @Test + void testReverseMultipleElements() { + Queue queue = new LinkedList<>(); + queue.add(1); + queue.add(2); + queue.add(3); + queue.add(4); + ReverseQueueRecursion.reverseQueue(queue); + assertEquals(4, queue.poll()); + assertEquals(3, queue.poll()); + assertEquals(2, queue.poll()); + assertEquals(1, queue.poll()); + assertTrue(queue.isEmpty()); + } + + @Test + void testReverseSingleElement() { + Queue queue = new LinkedList<>(); + queue.add(42); + ReverseQueueRecursion.reverseQueue(queue); + assertEquals(42, queue.poll()); + assertTrue(queue.isEmpty()); + } + + @Test + void testReverseEmptyQueue() { + Queue queue = new LinkedList<>(); + ReverseQueueRecursion.reverseQueue(queue); + assertTrue(queue.isEmpty()); + } + + @Test + void testReverseStringQueue() { + Queue queue = new LinkedList<>(); + queue.add("A"); + queue.add("B"); + queue.add("C"); + ReverseQueueRecursion.reverseQueue(queue); + assertEquals("C", queue.poll()); + assertEquals("B", queue.poll()); + assertEquals("A", queue.poll()); + assertTrue(queue.isEmpty()); + } +} From 023f856a9bca320e5ab4324e3efb4766ae2f4f54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:55:11 +0100 Subject: [PATCH 108/122] chore(deps-dev): bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.4 to 3.5.5 (#7288) chore(deps-dev): bump org.apache.maven.plugins:maven-surefire-plugin Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.4 to 3.5.5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.4...surefire-3.5.5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2445a1e920a8..96deca50fea8 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ maven-surefire-plugin - 3.5.4 + 3.5.5 From 7e5d9d469d8d2deb7309a1003dafdeb884c7dd84 Mon Sep 17 00:00:00 2001 From: Chahat Sandhu Date: Thu, 26 Feb 2026 05:27:11 -0600 Subject: [PATCH 109/122] feat: add HuffmanCoding with fail-fast validation and immutable design (#7289) --- .../compression/HuffmanCoding.java | 253 ++++++++++++++++++ .../compression/HuffmanCodingTest.java | 110 ++++++++ 2 files changed, 363 insertions(+) create mode 100644 src/main/java/com/thealgorithms/compression/HuffmanCoding.java create mode 100644 src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java diff --git a/src/main/java/com/thealgorithms/compression/HuffmanCoding.java b/src/main/java/com/thealgorithms/compression/HuffmanCoding.java new file mode 100644 index 000000000000..d7f9d58d2429 --- /dev/null +++ b/src/main/java/com/thealgorithms/compression/HuffmanCoding.java @@ -0,0 +1,253 @@ +package com.thealgorithms.compression; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * Huffman Coding Compression Algorithm Implementation. + *

+ * Huffman Coding is a popular greedy algorithm used for lossless data compression. + * It reduces the overall size of data by assigning variable-length, prefix-free + * binary codes to input characters, ensuring that more frequent characters receive + * the shortest possible codes. + *

+ *

+ * Key Features: + *

    + *
  • Uses a PriorityQueue (min-heap) to efficiently construct the optimal prefix tree.
  • + *
  • Fail-fast design throws exceptions for unsupported characters and malformed binary payloads.
  • + *
  • Immutable internal dictionary state prevents external tampering with generated codes.
  • + *
  • Robust handling of edge cases, including single-character strings and incomplete sequences.
  • + *
+ *

+ * @author Chahat Sandhu, singhc7 + * @see Huffman Coding (Wikipedia) + */ +public class HuffmanCoding { + + private Node root; + private final Map huffmanCodes; + + /** + * Represents a node within the Huffman Tree. + * Implements {@link Comparable} to allow sorting by frequency in a PriorityQueue. + */ + private static class Node implements Comparable { + final char ch; + final int freq; + final Node left; + final Node right; + + /** + * Constructs a leaf node containing a specific character and its frequency. + * + * @param ch The character stored in this leaf. + * @param freq The frequency of occurrence of the character. + */ + Node(char ch, int freq) { + this.ch = ch; + this.freq = freq; + this.left = null; + this.right = null; + } + + /** + * Constructs an internal node that merges two child nodes. + * The character is defaulted to the null character ('\0'). + * + * @param freq The combined frequency of the left and right child nodes. + * @param left The left child node. + * @param right The right child node. + */ + Node(int freq, Node left, Node right) { + this.ch = '\0'; + this.freq = freq; + this.left = left; + this.right = right; + } + + /** + * Determines if the current node is a leaf (contains no children). + * + * @return {@code true} if both left and right children are null, {@code false} otherwise. + */ + boolean isLeaf() { + return left == null && right == null; + } + + /** + * Compares this node with another node based on their frequencies. + * Used by the PriorityQueue to maintain the min-heap property. + * + * @param other The other Node to compare against. + * @return A negative integer, zero, or a positive integer as this node's frequency + * is less than, equal to, or greater than the specified node's frequency. + */ + @Override + public int compareTo(Node other) { + return Integer.compare(this.freq, other.freq); + } + } + + /** + * Initializes the Huffman Tree and generates immutable prefix-free codes + * based on the character frequencies in the provided text. + * + * @param text The input string used to calculate frequencies and build the optimal tree. + * If null or empty, an empty tree and dictionary are created. + */ + public HuffmanCoding(String text) { + if (text == null || text.isEmpty()) { + this.huffmanCodes = Collections.emptyMap(); + return; + } + + Map tempCodes = new HashMap<>(); + buildTree(text); + generateCodes(root, "", tempCodes); + + if (tempCodes.size() == 1) { + tempCodes.put(root.ch, "0"); + } + + this.huffmanCodes = Collections.unmodifiableMap(tempCodes); + } + + /** + * Computes character frequencies and constructs the Huffman Tree using a min-heap. + * The optimal tree is built by repeatedly extracting the two lowest-frequency nodes + * and merging them until a single root node remains. + * + * @param text The input text to analyze. + */ + private void buildTree(String text) { + Map freqMap = new HashMap<>(); + for (char c : text.toCharArray()) { + freqMap.put(c, freqMap.getOrDefault(c, 0) + 1); + } + + PriorityQueue pq = new PriorityQueue<>(); + for (Map.Entry entry : freqMap.entrySet()) { + pq.add(new Node(entry.getKey(), entry.getValue())); + } + + while (pq.size() > 1) { + Node left = pq.poll(); + Node right = pq.poll(); + pq.add(new Node(left.freq + right.freq, left, right)); + } + + root = pq.poll(); + } + + /** + * Recursively traverses the Huffman Tree to generate prefix-free binary codes. + * Left traversals append a '0' to the code, while right traversals append a '1'. + * + * @param node The current node in the traversal. + * @param code The accumulated binary string for the current path. + * @param map The temporary dictionary to populate with the final character-to-code mappings. + */ + private void generateCodes(Node node, String code, Map map) { + if (node == null) { + return; + } + if (node.isLeaf()) { + map.put(node.ch, code); + return; + } + generateCodes(node.left, code + "0", map); + generateCodes(node.right, code + "1", map); + } + + /** + * Encodes the given plaintext string into a binary string using the generated Huffman dictionary. + * + * @param text The plaintext string to compress. + * @return A string of '0's and '1's representing the compressed data. + * Returns an empty string if the input is null or empty. + * @throws IllegalStateException If attempting to encode when the Huffman tree is empty. + * @throws IllegalArgumentException If the input text contains a character not present + * in the original text used to build the tree. + */ + public String encode(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + if (root == null) { + throw new IllegalStateException("Huffman tree is empty."); + } + + StringBuilder sb = new StringBuilder(); + for (char c : text.toCharArray()) { + if (!huffmanCodes.containsKey(c)) { + throw new IllegalArgumentException(String.format("Character '%c' (U+%04X) not found in Huffman dictionary.", c, (int) c)); + } + sb.append(huffmanCodes.get(c)); + } + return sb.toString(); + } + + /** + * Decodes the given binary string back into the original plaintext using the Huffman Tree. + * Validates the integrity of the binary payload during traversal. + * + * @param encodedText The binary string of '0's and '1's to decompress. + * @return The reconstructed plaintext string. Returns an empty string if the input is null or empty. + * @throws IllegalStateException If attempting to decode when the Huffman tree is empty. + * @throws IllegalArgumentException If the binary string contains characters other than '0' or '1', + * or if the sequence ends abruptly without reaching a leaf node. + */ + public String decode(String encodedText) { + if (encodedText == null || encodedText.isEmpty()) { + return ""; + } + if (root == null) { + throw new IllegalStateException("Huffman tree is empty."); + } + + StringBuilder sb = new StringBuilder(); + + if (root.isLeaf()) { + for (char bit : encodedText.toCharArray()) { + if (bit != '0') { + throw new IllegalArgumentException("Invalid binary sequence for single-character tree."); + } + sb.append(root.ch); + } + return sb.toString(); + } + + Node current = root; + for (char bit : encodedText.toCharArray()) { + if (bit != '0' && bit != '1') { + throw new IllegalArgumentException("Encoded text contains invalid characters: " + bit); + } + + current = (bit == '0') ? current.left : current.right; + + if (current.isLeaf()) { + sb.append(current.ch); + current = root; + } + } + + if (current != root) { + throw new IllegalArgumentException("Malformed encoded string: incomplete sequence ending."); + } + + return sb.toString(); + } + + /** + * Retrieves the generated Huffman dictionary mapping characters to their binary codes. + * + * @return An unmodifiable map containing the character-to-binary-code mappings to prevent + * external mutation of the algorithm's state. + */ + public Map getHuffmanCodes() { + return huffmanCodes; + } +} diff --git a/src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java b/src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java new file mode 100644 index 000000000000..f919417899db --- /dev/null +++ b/src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java @@ -0,0 +1,110 @@ +package com.thealgorithms.compression; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class HuffmanCodingTest { + + @Test + void testStandardLifecycle() { + String input = "efficiency is key"; + HuffmanCoding huffman = new HuffmanCoding(input); + + String encoded = huffman.encode(input); + assertNotNull(encoded); + assertTrue(encoded.matches("[01]+")); + assertEquals(input, huffman.decode(encoded)); + } + + @Test + void testNullAndEmptyHandling() { + HuffmanCoding huffman = new HuffmanCoding(""); + assertEquals("", huffman.encode("")); + assertEquals("", huffman.decode("")); + + HuffmanCoding huffmanNull = new HuffmanCoding(null); + assertEquals("", huffmanNull.encode(null)); + assertEquals("", huffmanNull.decode(null)); + } + + @Test + void testSingleCharacterEdgeCase() { + String input = "aaaaa"; + HuffmanCoding huffman = new HuffmanCoding(input); + + String encoded = huffman.encode(input); + assertEquals("00000", encoded); + assertEquals(input, huffman.decode(encoded)); + } + + @Test + void testUnicodeAndSpecialCharacters() { + // Tests spacing, symbols, non-latin alphabets, and surrogate pairs (emojis) + String input = "Hello, World! πŸš€\nLine 2: こんにけは"; + HuffmanCoding huffman = new HuffmanCoding(input); + + String encoded = huffman.encode(input); + assertEquals(input, huffman.decode(encoded)); + } + + @Test + void testFailFastOnUnseenCharacter() { + HuffmanCoding huffman = new HuffmanCoding("abc"); + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> huffman.encode("abcd") // 'd' was not in the original tree + ); + assertTrue(exception.getMessage().contains("not found in Huffman dictionary")); + } + + @Test + void testFailFastOnInvalidBinaryCharacter() { + HuffmanCoding huffman = new HuffmanCoding("abc"); + String encoded = huffman.encode("abc"); + + // Inject a '2' into the binary stream + String corruptedEncoded = encoded + "2"; + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> huffman.decode(corruptedEncoded)); + assertTrue(exception.getMessage().contains("contains invalid characters")); + } + + @Test + void testFailFastOnIncompleteSequence() { + HuffmanCoding huffman = new HuffmanCoding("abcd"); + String encoded = huffman.encode("abc"); + + // Truncate the last bit to simulate an incomplete byte/sequence transfer + String truncatedEncoded = encoded.substring(0, encoded.length() - 1); + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> huffman.decode(truncatedEncoded)); + assertTrue(exception.getMessage().contains("incomplete sequence")); + } + + @Test + void testImmutabilityOfDictionary() { + HuffmanCoding huffman = new HuffmanCoding("abc"); + var codes = huffman.getHuffmanCodes(); + + assertThrows(UnsupportedOperationException.class, () -> codes.put('z', "0101")); + } + + @Test + void testStressVolume() { + StringBuilder sb = new StringBuilder(); + // Generate a 100,000 character string + for (int i = 0; i < 100000; i++) { + sb.append((char) ('a' + (i % 26))); + } + String largeInput = sb.toString(); + + HuffmanCoding huffman = new HuffmanCoding(largeInput); + String encoded = huffman.encode(largeInput); + + assertEquals(largeInput, huffman.decode(encoded)); + } +} From 705eb52833f50e88b1f92d309ac40f770fa56b10 Mon Sep 17 00:00:00 2001 From: kvadrik <41710943+kvadrik@users.noreply.github.com> Date: Fri, 27 Feb 2026 23:35:42 +0200 Subject: [PATCH 110/122] Added volume of a pyramid frustum (#7291) * Added volume of a pyramid frustum Added a function calculating volume of a pyramid frustum, V=(S1+S2+sqrt(S1*S2))*h/3 * compiler error fixed * Added pyramid frustum test case * extra space removed --- src/main/java/com/thealgorithms/maths/Volume.java | 12 ++++++++++++ .../java/com/thealgorithms/maths/VolumeTest.java | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/main/java/com/thealgorithms/maths/Volume.java b/src/main/java/com/thealgorithms/maths/Volume.java index 0f282b2abae2..89b0595912b9 100644 --- a/src/main/java/com/thealgorithms/maths/Volume.java +++ b/src/main/java/com/thealgorithms/maths/Volume.java @@ -102,4 +102,16 @@ public static double volumePyramid(double baseArea, double height) { public static double volumeFrustumOfCone(double r1, double r2, double height) { return (Math.PI * height / 3) * (r1 * r1 + r2 * r2 + r1 * r2); } + + /** + * Calculate the volume of a frustum of a pyramid. + * + * @param upperBaseArea area of the upper base + * @param lowerBaseArea area of the lower base + * @param height height of the frustum + * @return volume of the frustum + */ + public static double volumeFrustumOfPyramid(double upperBaseArea, double lowerBaseArea, double height) { + return (upperBaseArea + lowerBaseArea + Math.sqrt(upperBaseArea * lowerBaseArea)) * height / 3; + } } diff --git a/src/test/java/com/thealgorithms/maths/VolumeTest.java b/src/test/java/com/thealgorithms/maths/VolumeTest.java index af882eef7563..cf72d7084e75 100644 --- a/src/test/java/com/thealgorithms/maths/VolumeTest.java +++ b/src/test/java/com/thealgorithms/maths/VolumeTest.java @@ -35,5 +35,8 @@ public void volume() { /* test frustum */ assertEquals(359.188760060433, Volume.volumeFrustumOfCone(3, 5, 7)); + + /* test pyramid frustum */ + assertEquals(140.0, Volume.volumeFrustumOfPyramid(6, 24, 10)); } } From ba286b24d66972150547fc6683eaa1925743e9d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 22:52:50 +0100 Subject: [PATCH 111/122] chore(deps-dev): bump org.mockito:mockito-core from 5.21.0 to 5.22.0 (#7292) Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.21.0 to 5.22.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.21.0...v5.22.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-version: 5.22.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 96deca50fea8..7c614f31e52e 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ org.mockito mockito-core - 5.21.0 + 5.22.0 test From 0d2a98e9f87dc7bd5dde732a8d34a6bcaef43400 Mon Sep 17 00:00:00 2001 From: kvadrik <41710943+kvadrik@users.noreply.github.com> Date: Sat, 28 Feb 2026 21:08:50 +0200 Subject: [PATCH 112/122] Added volume of a torus (#7294) * Added volume of a torus Added function calculation the volume of a torus according to the formula: V = 2 * pi^2 * R * r^2 where R is the major radius and r is the minor radius of the torus. * Added test for torus volume --- src/main/java/com/thealgorithms/maths/Volume.java | 11 +++++++++++ src/test/java/com/thealgorithms/maths/VolumeTest.java | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/main/java/com/thealgorithms/maths/Volume.java b/src/main/java/com/thealgorithms/maths/Volume.java index 89b0595912b9..c0898c5424a0 100644 --- a/src/main/java/com/thealgorithms/maths/Volume.java +++ b/src/main/java/com/thealgorithms/maths/Volume.java @@ -114,4 +114,15 @@ public static double volumeFrustumOfCone(double r1, double r2, double height) { public static double volumeFrustumOfPyramid(double upperBaseArea, double lowerBaseArea, double height) { return (upperBaseArea + lowerBaseArea + Math.sqrt(upperBaseArea * lowerBaseArea)) * height / 3; } + + /** + * Calculate the volume of a torus. + * + * @param majorRadius major radius of a torus + * @param minorRadius minor radius of a torus + * @return volume of the torus + */ + public static double volumeTorus(double majorRadius, double minorRadius) { + return 2 * Math.PI * Math.PI * majorRadius * minorRadius * minorRadius; + } } diff --git a/src/test/java/com/thealgorithms/maths/VolumeTest.java b/src/test/java/com/thealgorithms/maths/VolumeTest.java index cf72d7084e75..c159d7566b46 100644 --- a/src/test/java/com/thealgorithms/maths/VolumeTest.java +++ b/src/test/java/com/thealgorithms/maths/VolumeTest.java @@ -38,5 +38,8 @@ public void volume() { /* test pyramid frustum */ assertEquals(140.0, Volume.volumeFrustumOfPyramid(6, 24, 10)); + + /* test torus */ + assertEquals(39.47841760435743, Volume.volumeTorus(2, 1)); } } From d8672882bfebc8cf85f184da461880c84aa7cb92 Mon Sep 17 00:00:00 2001 From: kvadrik <41710943+kvadrik@users.noreply.github.com> Date: Sat, 28 Feb 2026 21:13:19 +0200 Subject: [PATCH 113/122] Added surface area of a cuboid (#7293) * Added surface area of a cuboid Added surface area of a cuboid according to the formula: S = 2 * (ab + ac + bc) * Removed extra white space * Added test for cuboid surface area * fixed syntax error * Removed extra space * Added tests for cuboid surface area that should fail I have added tests for cuboid surface area where one of the parameters is invalid. These should fail. --- .../java/com/thealgorithms/maths/Area.java | 21 +++++++++++++++++++ .../com/thealgorithms/maths/AreaTest.java | 11 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/main/java/com/thealgorithms/maths/Area.java b/src/main/java/com/thealgorithms/maths/Area.java index 08807580cb03..84fc67159379 100644 --- a/src/main/java/com/thealgorithms/maths/Area.java +++ b/src/main/java/com/thealgorithms/maths/Area.java @@ -35,6 +35,27 @@ public static double surfaceAreaCube(final double sideLength) { return 6 * sideLength * sideLength; } + /** + * Calculate the surface area of a cuboid. + * + * @param length length of the cuboid + * @param width width of the cuboid + * @param height height of the cuboid + * @return surface area of given cuboid + */ + public static double surfaceAreaCuboid(final double length, double width, double height) { + if (length <= 0) { + throw new IllegalArgumentException("Length must be greater than 0"); + } + if (width <= 0) { + throw new IllegalArgumentException("Width must be greater than 0"); + } + if (height <= 0) { + throw new IllegalArgumentException("Height must be greater than 0"); + } + return 2 * (length * width + length * height + width * height); + } + /** * Calculate the surface area of a sphere. * diff --git a/src/test/java/com/thealgorithms/maths/AreaTest.java b/src/test/java/com/thealgorithms/maths/AreaTest.java index b28afb85fbc3..1c2fe53ff3f3 100644 --- a/src/test/java/com/thealgorithms/maths/AreaTest.java +++ b/src/test/java/com/thealgorithms/maths/AreaTest.java @@ -16,6 +16,11 @@ void testSurfaceAreaCube() { assertEquals(6.0, Area.surfaceAreaCube(1)); } + @Test + void testSurfaceAreaCuboid() { + assertEquals(214.0, Area.surfaceAreaCuboid(5, 6, 7)); + } + @Test void testSurfaceAreaSphere() { assertEquals(12.566370614359172, Area.surfaceAreaSphere(1)); @@ -70,6 +75,12 @@ void surfaceAreaCone() { void testAllIllegalInput() { assertAll(() -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCube(0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCuboid(0, 1, 2)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCuboid(1, 0, 2)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCuboid(1, 2, 0)), () -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaSphere(0)), () From 4b04ad4a836ad87d6d4adf3bf395c0aade96bb07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 19:56:39 +0100 Subject: [PATCH 114/122] chore(deps): bump com.puppycrawl.tools:checkstyle from 13.2.0 to 13.3.0 (#7295) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 13.2.0 to 13.3.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-13.2.0...checkstyle-13.3.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: a <19151554+alxkm@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c614f31e52e..b2192fb9a64a 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 13.2.0 + 13.3.0
From 9875648a835ac49b9502c0b6c4816780b4f0462b Mon Sep 17 00:00:00 2001 From: Alex Tumanov Date: Thu, 5 Mar 2026 07:34:33 -0600 Subject: [PATCH 115/122] feat: add TopKFrequentWords with deterministic tie-breaking (#7298) (#7297) * feat: add TopKFrequentWords with deterministic tie-breaking * style: format TopKFrequentWords files with clang-format --- .../strings/TopKFrequentWords.java | 56 +++++++++++++++++++ .../strings/TopKFrequentWordsTest.java | 34 +++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/TopKFrequentWords.java create mode 100644 src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java diff --git a/src/main/java/com/thealgorithms/strings/TopKFrequentWords.java b/src/main/java/com/thealgorithms/strings/TopKFrequentWords.java new file mode 100644 index 000000000000..106de304cf40 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/TopKFrequentWords.java @@ -0,0 +1,56 @@ +package com.thealgorithms.strings; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility class to find the top-k most frequent words. + * + *

Words are ranked by frequency in descending order. For equal frequencies, + * words are ranked in lexicographical ascending order. + * + *

Reference: + * https://en.wikipedia.org/wiki/Top-k_problem + * + */ +public final class TopKFrequentWords { + private TopKFrequentWords() { + } + + /** + * Finds the k most frequent words. + * + * @param words input array of words + * @param k number of words to return + * @return list of top-k words ordered by frequency then lexicographical order + * @throws IllegalArgumentException if words is null, k is negative, or words contains null + */ + public static List findTopKFrequentWords(String[] words, int k) { + if (words == null) { + throw new IllegalArgumentException("Input words array cannot be null."); + } + if (k < 0) { + throw new IllegalArgumentException("k cannot be negative."); + } + if (k == 0 || words.length == 0) { + return List.of(); + } + + Map frequency = new HashMap<>(); + for (String word : words) { + if (word == null) { + throw new IllegalArgumentException("Input words cannot contain null values."); + } + frequency.put(word, frequency.getOrDefault(word, 0) + 1); + } + + List candidates = new ArrayList<>(frequency.keySet()); + candidates.sort(Comparator.comparingInt(frequency::get).reversed().thenComparing(Comparator.naturalOrder())); + + int limit = Math.min(k, candidates.size()); + return new ArrayList<>(candidates.subList(0, limit)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java b/src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java new file mode 100644 index 000000000000..42b2d04ff265 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +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; + +class TopKFrequentWordsTest { + + @ParameterizedTest + @MethodSource("validTestCases") + void testFindTopKFrequentWords(String[] words, int k, List expected) { + assertEquals(expected, TopKFrequentWords.findTopKFrequentWords(words, k)); + } + + static Stream validTestCases() { + return Stream.of(Arguments.of(new String[] {"i", "love", "leetcode", "i", "love", "coding"}, 2, List.of("i", "love")), Arguments.of(new String[] {"the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"}, 4, List.of("the", "is", "sunny", "day")), + Arguments.of(new String[] {"bbb", "aaa", "bbb", "aaa", "ccc"}, 2, List.of("aaa", "bbb")), Arguments.of(new String[] {"one", "two", "three"}, 10, List.of("one", "three", "two")), Arguments.of(new String[] {}, 3, List.of()), Arguments.of(new String[] {"x", "x", "y"}, 0, List.of())); + } + + @ParameterizedTest + @MethodSource("invalidTestCases") + void testFindTopKFrequentWordsInvalidInput(String[] words, int k) { + assertThrows(IllegalArgumentException.class, () -> TopKFrequentWords.findTopKFrequentWords(words, k)); + } + + static Stream invalidTestCases() { + return Stream.of(Arguments.of((String[]) null, 1), Arguments.of(new String[] {"a", null, "b"}, 2), Arguments.of(new String[] {"a"}, -1)); + } +} From 8b41533d004babdd641ad70f80e50cf6c1746ebe Mon Sep 17 00:00:00 2001 From: Kakkirala Reshma <24wh1a05q3@bvrithyderabad.edu.in> Date: Fri, 6 Mar 2026 19:41:57 +0530 Subject: [PATCH 116/122] Add Javadoc comments for AnyBaseToAnyBase class (#7301) Added Javadoc comments to describe the algorithm and its complexities. --- .../com/thealgorithms/conversions/AnyBaseToAnyBase.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java b/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java index 7a9448fd8fe7..7698cc832981 100644 --- a/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java +++ b/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java @@ -1,3 +1,10 @@ +/** + * [Brief description of what the algorithm does] + *

+ * Time Complexity: O(n) [or appropriate complexity] + * Space Complexity: O(n) + * * @author Reshma Kakkirala + */ package com.thealgorithms.conversions; import java.util.Arrays; From 9aa2544f61566c5e715d56e1a76e51692c26eb37 Mon Sep 17 00:00:00 2001 From: Debojeet Bhattacharya Date: Sun, 8 Mar 2026 09:27:33 -0500 Subject: [PATCH 117/122] Renamed method variables (#7296) * renamed method variables for improved readibility in the sort method of InsertionSort * Renamed method in PancackeSort. * Renamed array "aux" to "tempArray" --------- Co-authored-by: Deniz Altunkapan --- .../thealgorithms/sorts/InsertionSort.java | 30 +++++++++---------- .../com/thealgorithms/sorts/MergeSort.java | 16 +++++----- .../com/thealgorithms/sorts/PancakeSort.java | 4 +-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/InsertionSort.java b/src/main/java/com/thealgorithms/sorts/InsertionSort.java index fdbfd9cd1cfa..1e42f2a61271 100644 --- a/src/main/java/com/thealgorithms/sorts/InsertionSort.java +++ b/src/main/java/com/thealgorithms/sorts/InsertionSort.java @@ -33,30 +33,30 @@ public > T[] sort(T[] array) { } /** - * Sorts a subarray of the given array using the standard Insertion Sort algorithm. + * Sorts a subarray of the given items using the standard Insertion Sort algorithm. * - * @param array The array to be sorted - * @param lo The starting index of the subarray - * @param hi The ending index of the subarray (exclusive) - * @param The type of elements in the array, which must be comparable - * @return The sorted array + * @param items The items to be sorted + * @param startIndex The starting index of the subarray + * @param endIndex The ending index of the subarray (exclusive) + * @param The type of elements in the items, which must be comparable + * @return The sorted items */ - public > T[] sort(T[] array, final int lo, final int hi) { - if (array == null || lo >= hi) { - return array; + public > T[] sort(T[] items, final int startIndex, final int endIndex) { + if (items == null || startIndex >= endIndex) { + return items; } - for (int i = lo + 1; i < hi; i++) { - final T key = array[i]; + for (int i = startIndex + 1; i < endIndex; i++) { + final T key = items[i]; int j = i - 1; - while (j >= lo && SortUtils.less(key, array[j])) { - array[j + 1] = array[j]; + while (j >= startIndex && SortUtils.less(key, items[j])) { + items[j + 1] = items[j]; j--; } - array[j + 1] = key; + items[j + 1] = key; } - return array; + return items; } /** diff --git a/src/main/java/com/thealgorithms/sorts/MergeSort.java b/src/main/java/com/thealgorithms/sorts/MergeSort.java index f7a7c8da004d..5db9c48b4f61 100644 --- a/src/main/java/com/thealgorithms/sorts/MergeSort.java +++ b/src/main/java/com/thealgorithms/sorts/MergeSort.java @@ -10,7 +10,7 @@ @SuppressWarnings("rawtypes") class MergeSort implements SortAlgorithm { - private Comparable[] aux; + private Comparable[] tempArray; /** * Generic merge sort algorithm. @@ -26,7 +26,7 @@ class MergeSort implements SortAlgorithm { */ @Override public > T[] sort(T[] unsorted) { - aux = new Comparable[unsorted.length]; + tempArray = new Comparable[unsorted.length]; doSort(unsorted, 0, unsorted.length - 1); return unsorted; } @@ -58,17 +58,17 @@ private > void doSort(T[] arr, int left, int right) { private > void merge(T[] arr, int left, int mid, int right) { int i = left; int j = mid + 1; - System.arraycopy(arr, left, aux, left, right + 1 - left); + System.arraycopy(arr, left, tempArray, left, right + 1 - left); for (int k = left; k <= right; k++) { if (j > right) { - arr[k] = (T) aux[i++]; + arr[k] = (T) tempArray[i++]; } else if (i > mid) { - arr[k] = (T) aux[j++]; - } else if (less(aux[j], aux[i])) { - arr[k] = (T) aux[j++]; + arr[k] = (T) tempArray[j++]; + } else if (less(tempArray[j], tempArray[i])) { + arr[k] = (T) tempArray[j++]; } else { - arr[k] = (T) aux[i++]; + arr[k] = (T) tempArray[i++]; } } } diff --git a/src/main/java/com/thealgorithms/sorts/PancakeSort.java b/src/main/java/com/thealgorithms/sorts/PancakeSort.java index 6079672a1d77..6522aefd7ae3 100644 --- a/src/main/java/com/thealgorithms/sorts/PancakeSort.java +++ b/src/main/java/com/thealgorithms/sorts/PancakeSort.java @@ -15,7 +15,7 @@ public > T[] sort(T[] array) { } for (int currentSize = 0; currentSize < array.length; currentSize++) { - int maxIndex = findMaxIndex(array, currentSize); + int maxIndex = findIndexOfMax(array, currentSize); SortUtils.flip(array, maxIndex, array.length - 1 - currentSize); } @@ -30,7 +30,7 @@ public > T[] sort(T[] array) { * @param the type of elements in the array * @return the index of the maximum element */ - private > int findMaxIndex(T[] array, int currentSize) { + private > int findIndexOfMax(T[] array, int currentSize) { T max = array[0]; int maxIndex = 0; for (int i = 0; i < array.length - currentSize; i++) { From 8e1f12447c23b0ac9179e76e4adef6b75ecc10e7 Mon Sep 17 00:00:00 2001 From: lmj798 <2757400745@qq.com> Date: Wed, 11 Mar 2026 21:31:15 +0800 Subject: [PATCH 118/122] test: enhance GenericHashMapUsingArrayTest with comprehensive edge case coverage (#7300) * test: enhance GenericHashMapUsingArrayTest with additional edge case coverage * Removed unused assertion 'assertNotEquals' from imports. * Simplify import statements in GenericHashMapUsingArrayTest * Refactor assertions to use Assertions class * Refactor null key test to use variable --- .../hashing/GenericHashMapUsingArrayTest.java | 190 +++++++++++++++--- 1 file changed, 164 insertions(+), 26 deletions(-) diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java index 5d1733a3e97c..6b6e670a258b 100644 --- a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java @@ -1,11 +1,9 @@ package com.thealgorithms.datastructures.hashmap.hashing; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class GenericHashMapUsingArrayTest { @@ -16,10 +14,10 @@ void testGenericHashmapWhichUsesArrayAndBothKeyAndValueAreStrings() { map.put("Nepal", "Kathmandu"); map.put("India", "New Delhi"); map.put("Australia", "Sydney"); - assertNotNull(map); - assertEquals(4, map.size()); - assertEquals("Kathmandu", map.get("Nepal")); - assertEquals("Sydney", map.get("Australia")); + Assertions.assertNotNull(map); + Assertions.assertEquals(4, map.size()); + Assertions.assertEquals("Kathmandu", map.get("Nepal")); + Assertions.assertEquals("Sydney", map.get("Australia")); } @Test @@ -29,12 +27,12 @@ void testGenericHashmapWhichUsesArrayAndKeyIsStringValueIsInteger() { map.put("Nepal", 25); map.put("India", 101); map.put("Australia", 99); - assertNotNull(map); - assertEquals(4, map.size()); - assertEquals(25, map.get("Nepal")); - assertEquals(99, map.get("Australia")); + Assertions.assertNotNull(map); + Assertions.assertEquals(4, map.size()); + Assertions.assertEquals(25, map.get("Nepal")); + Assertions.assertEquals(99, map.get("Australia")); map.remove("Nepal"); - assertFalse(map.containsKey("Nepal")); + Assertions.assertFalse(map.containsKey("Nepal")); } @Test @@ -44,11 +42,11 @@ void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() { map.put(34, "Kathmandu"); map.put(46, "New Delhi"); map.put(89, "Sydney"); - assertNotNull(map); - assertEquals(4, map.size()); - assertEquals("Sydney", map.get(89)); - assertEquals("Washington DC", map.get(101)); - assertTrue(map.containsKey(46)); + Assertions.assertNotNull(map); + Assertions.assertEquals(4, map.size()); + Assertions.assertEquals("Sydney", map.get(89)); + Assertions.assertEquals("Washington DC", map.get(101)); + Assertions.assertTrue(map.containsKey(46)); } @Test @@ -56,7 +54,7 @@ void testRemoveNonExistentKey() { GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); map.put("USA", "Washington DC"); map.remove("Nepal"); // Attempting to remove a non-existent key - assertEquals(1, map.size()); // Size should remain the same + Assertions.assertEquals(1, map.size()); // Size should remain the same } @Test @@ -65,8 +63,8 @@ void testRehashing() { for (int i = 0; i < 20; i++) { map.put("Key" + i, "Value" + i); } - assertEquals(20, map.size()); // Ensure all items were added - assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash + Assertions.assertEquals(20, map.size()); // Ensure all items were added + Assertions.assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash } @Test @@ -74,7 +72,7 @@ void testUpdateValueForExistingKey() { GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); map.put("USA", "Washington DC"); map.put("USA", "New Washington DC"); // Updating value for existing key - assertEquals("New Washington DC", map.get("USA")); + Assertions.assertEquals("New Washington DC", map.get("USA")); } @Test @@ -83,14 +81,154 @@ void testToStringMethod() { map.put("USA", "Washington DC"); map.put("Nepal", "Kathmandu"); String expected = "{USA : Washington DC, Nepal : Kathmandu}"; - assertEquals(expected, map.toString()); + Assertions.assertEquals(expected, map.toString()); } @Test void testContainsKey() { GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); map.put("USA", "Washington DC"); - assertTrue(map.containsKey("USA")); - assertFalse(map.containsKey("Nepal")); + Assertions.assertTrue(map.containsKey("USA")); + Assertions.assertFalse(map.containsKey("Nepal")); + } + + // ======= Added tests from the new version ======= + + @Test + void shouldThrowNullPointerExceptionForNullKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + String nullKey = null; // Use variable to avoid static analysis false positive + Assertions.assertThrows(NullPointerException.class, () -> map.put(nullKey, "value")); + } + + @Test + void shouldStoreNullValueForKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("keyWithNullValue", null); + Assertions.assertEquals(1, map.size()); + Assertions.assertNull(map.get("keyWithNullValue")); + // Note: containsKey returns false for null values due to implementation + Assertions.assertFalse(map.containsKey("keyWithNullValue")); + } + + @Test + void shouldHandleCollisionWhenKeysHashToSameBucket() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + Integer key1 = 1; + Integer key2 = 17; + map.put(key1, 100); + map.put(key2, 200); + Assertions.assertEquals(2, map.size()); + Assertions.assertEquals(100, map.get(key1)); + Assertions.assertEquals(200, map.get(key2)); + Assertions.assertTrue(map.containsKey(key1)); + Assertions.assertTrue(map.containsKey(key2)); + } + + @Test + void shouldHandleEmptyStringAsKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("", "valueForEmptyKey"); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("valueForEmptyKey", map.get("")); + Assertions.assertTrue(map.containsKey("")); + } + + @Test + void shouldHandleEmptyStringAsValue() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("keyForEmptyValue", ""); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("", map.get("keyForEmptyValue")); + Assertions.assertTrue(map.containsKey("keyForEmptyValue")); + } + + @Test + void shouldHandleNegativeIntegerKeys() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put(-1, 100); + map.put(-100, 200); + Assertions.assertEquals(2, map.size()); + Assertions.assertEquals(100, map.get(-1)); + Assertions.assertEquals(200, map.get(-100)); + Assertions.assertTrue(map.containsKey(-1)); + Assertions.assertTrue(map.containsKey(-100)); + } + + @Test + void shouldHandleZeroAsKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put(0, 100); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals(100, map.get(0)); + Assertions.assertTrue(map.containsKey(0)); + } + + @Test + void shouldHandleStringWithSpecialCharacters() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("key!@#$%^&*()", "value<>?/\\|"); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("value<>?/\\|", map.get("key!@#$%^&*()")); + Assertions.assertTrue(map.containsKey("key!@#$%^&*()")); + } + + @Test + void shouldHandleLongStrings() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + StringBuilder longKey = new StringBuilder(); + StringBuilder longValue = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + longKey.append("a"); + longValue.append("b"); + } + String key = longKey.toString(); + String value = longValue.toString(); + map.put(key, value); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals(value, map.get(key)); + Assertions.assertTrue(map.containsKey(key)); + } + + @ParameterizedTest + @ValueSource(strings = {"a", "ab", "abc", "test", "longerString"}) + void shouldHandleKeysOfDifferentLengths(String key) { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put(key, "value"); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("value", map.get(key)); + Assertions.assertTrue(map.containsKey(key)); + } + + @Test + void shouldHandleUpdateOnExistingKeyInCollisionBucket() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + Integer key1 = 1; + Integer key2 = 17; + map.put(key1, 100); + map.put(key2, 200); + Assertions.assertEquals(2, map.size()); + map.put(key2, 999); + Assertions.assertEquals(2, map.size()); + Assertions.assertEquals(100, map.get(key1)); + Assertions.assertEquals(999, map.get(key2)); + Assertions.assertTrue(map.containsKey(key1)); + Assertions.assertTrue(map.containsKey(key2)); + } + + @Test + void shouldHandleExactlyLoadFactorBoundary() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + // Fill exactly to load factor (12 items with capacity 16 and 0.75 load factor) + for (int i = 0; i < 12; i++) { + map.put(i, i * 10); + } + Assertions.assertEquals(12, map.size()); + // Act - This should trigger rehash on 13th item + map.put(12, 120); + // Assert - Rehash should have happened + Assertions.assertEquals(13, map.size()); + Assertions.assertEquals(120, map.get(12)); + Assertions.assertTrue(map.containsKey(12)); } } From 5e06b1592638ac0826258341398f92537717eac3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 23:12:37 +0100 Subject: [PATCH 119/122] chore(deps-dev): bump org.mockito:mockito-core from 5.22.0 to 5.23.0 (#7305) Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.22.0 to 5.23.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.22.0...v5.23.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-version: 5.23.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b2192fb9a64a..dab7447430e5 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ org.mockito mockito-core - 5.22.0 + 5.23.0 test From 8bbd090e0cc7cf0dc9b2e3e74f9a33ca8dc297e6 Mon Sep 17 00:00:00 2001 From: kvadrik <41710943+kvadrik@users.noreply.github.com> Date: Sat, 14 Mar 2026 23:09:19 +0200 Subject: [PATCH 120/122] Overlapping condition changed (#7314) Overlapping happens not when centers of circular bodies are in the same point, but when the distance between them is smaller than the sum of their radii. --- src/main/java/com/thealgorithms/physics/ElasticCollision2D.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java index 399c3f1e041f..d096e0a8d7cd 100644 --- a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java +++ b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java @@ -41,7 +41,7 @@ public static void resolveCollision(Body a, Body b) { double dy = b.y - a.y; double dist = Math.hypot(dx, dy); - if (dist == 0) { + if (dist < a.radius + b.radius) { return; // overlapping } From 24c2beae463b7b1f8c2616110911449589af482b Mon Sep 17 00:00:00 2001 From: Maryam Hazrati <117775713+Maryamh12@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:15:50 +0000 Subject: [PATCH 121/122] Improve space complexity to O(1) in WordSearch. (#7308) * Modifying space complexity to O(1). * Fix formatting using clang-format. * Fix checkstyle violations. * Fix checkstyle violations and code formatting. * Remove unused fields reported by SpotBugs. * Remove unused fields and comments. * Remove unused field reported by SpotBugs. * Fix PMD collapsible if statement. * Fix indentation to satisfy clang-format. --------- Co-authored-by: Deniz Altunkapan --- .../backtracking/WordSearch.java | 66 +++++++------------ 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/thealgorithms/backtracking/WordSearch.java b/src/main/java/com/thealgorithms/backtracking/WordSearch.java index 174ca90ccaab..452f17b6ace6 100644 --- a/src/main/java/com/thealgorithms/backtracking/WordSearch.java +++ b/src/main/java/com/thealgorithms/backtracking/WordSearch.java @@ -35,22 +35,6 @@ * - Stack space for the recursive DFS function, where L is the maximum depth of recursion (length of the word). */ public class WordSearch { - private final int[] dx = {0, 0, 1, -1}; - private final int[] dy = {1, -1, 0, 0}; - private boolean[][] visited; - private char[][] board; - private String word; - - /** - * Checks if the given (x, y) coordinates are valid positions in the board. - * - * @param x The row index. - * @param y The column index. - * @return True if the coordinates are within the bounds of the board; false otherwise. - */ - private boolean isValid(int x, int y) { - return x >= 0 && x < board.length && y >= 0 && y < board[0].length; - } /** * Performs Depth First Search (DFS) from the cell (x, y) @@ -58,28 +42,27 @@ private boolean isValid(int x, int y) { * * @param x The current row index. * @param y The current column index. - * @param nextIdx The index of the next character in the word to be matched. + * @param idx The index of the next character in the word to be matched. * @return True if a valid path is found to match the remaining characters of the word; false otherwise. */ - private boolean doDFS(int x, int y, int nextIdx) { - visited[x][y] = true; - if (nextIdx == word.length()) { + + private boolean dfs(char[][] board, int x, int y, String word, int idx) { + if (idx == word.length()) { return true; } - for (int i = 0; i < 4; ++i) { - int xi = x + dx[i]; - int yi = y + dy[i]; - if (isValid(xi, yi) && board[xi][yi] == word.charAt(nextIdx) && !visited[xi][yi]) { - boolean exists = doDFS(xi, yi, nextIdx + 1); - if (exists) { - return true; - } - } + if (x < 0 || y < 0 || x >= board.length || y >= board[0].length || board[x][y] != word.charAt(idx)) { + return false; } - visited[x][y] = false; // Backtrack - return false; + char temp = board[x][y]; + board[x][y] = '#'; + + boolean found = dfs(board, x + 1, y, word, idx + 1) || dfs(board, x - 1, y, word, idx + 1) || dfs(board, x, y + 1, word, idx + 1) || dfs(board, x, y - 1, word, idx + 1); + + board[x][y] = temp; + + return found; } /** @@ -90,20 +73,21 @@ private boolean doDFS(int x, int y, int nextIdx) { * @param word The target word to search for in the board. * @return True if the word exists in the board; false otherwise. */ + public boolean exist(char[][] board, String word) { - this.board = board; - this.word = word; - for (int i = 0; i < board.length; ++i) { - for (int j = 0; j < board[0].length; ++j) { - if (board[i][j] == word.charAt(0)) { - visited = new boolean[board.length][board[0].length]; - boolean exists = doDFS(i, j, 1); - if (exists) { - return true; - } + + int m = board.length; + int n = board[0].length; + + // DFS search + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == word.charAt(0) && dfs(board, i, j, word, 0)) { + return true; } } } + return false; } } From 7d57c5720670dc0fd6e37d7bdc41ad053d925f5b Mon Sep 17 00:00:00 2001 From: kvadrik <41710943+kvadrik@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:44:31 +0200 Subject: [PATCH 122/122] Added quadratic mean (#7315) * Added quadratic mean Added a quadratic mean of the given numbers, sqrt ((n1^2+n2^2+...+nk^2)/k). * Added tests for quadratic mean * Corrected quadratic mean * Added comment to quadratic mean * Corrected quadratic mean tests * Replaced sqrt by pow * Error fixed * Extra whitespace removed * Extra whitespace removed * Removed extra white space * Removed extra white space --- .../java/com/thealgorithms/maths/Means.java | 22 ++++++++ .../com/thealgorithms/maths/MeansTest.java | 53 ++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/maths/Means.java b/src/main/java/com/thealgorithms/maths/Means.java index 5445a3caebc7..d77eb1d3f661 100644 --- a/src/main/java/com/thealgorithms/maths/Means.java +++ b/src/main/java/com/thealgorithms/maths/Means.java @@ -107,6 +107,28 @@ public static Double harmonic(final Iterable numbers) { return size / sumOfReciprocals; } + /** + * Computes the quadratic mean (root mean square) of the given numbers. + *

+ * The quadratic mean is calculated as: √[(x₁^2 Γ— xβ‚‚^2 Γ— ... Γ— xβ‚™^2)/n] + *

+ *

+ * Example: For numbers [1, 7], the quadratic mean is √[(1^2+7^2)/2] = √25 = 5.0 + *

+ * + * @param numbers the input numbers (must not be empty) + * @return the quadratic mean of the input numbers + * @throws IllegalArgumentException if the input is empty + * @see Quadratic + * Mean + */ + public static Double quadratic(final Iterable numbers) { + checkIfNotEmpty(numbers); + double sumOfSquares = StreamSupport.stream(numbers.spliterator(), false).reduce(0d, (x, y) -> x + y * y); + int size = IterableUtils.size(numbers); + return Math.pow(sumOfSquares / size, 0.5); + } + /** * Validates that the input iterable is not empty. * diff --git a/src/test/java/com/thealgorithms/maths/MeansTest.java b/src/test/java/com/thealgorithms/maths/MeansTest.java index deee0a931910..853fdbea3963 100644 --- a/src/test/java/com/thealgorithms/maths/MeansTest.java +++ b/src/test/java/com/thealgorithms/maths/MeansTest.java @@ -172,6 +172,53 @@ void testHarmonicMeanWithLinkedList() { assertEquals(expected, Means.harmonic(numbers), EPSILON); } + // ========== Quadratic Mean Tests ========== + + @Test + void testQuadraticMeanThrowsExceptionForEmptyList() { + List numbers = new ArrayList<>(); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> Means.quadratic(numbers)); + assertTrue(exception.getMessage().contains("Empty list")); + } + + @Test + void testQuadraticMeanSingleNumber() { + LinkedHashSet numbers = new LinkedHashSet<>(Arrays.asList(2.5)); + assertEquals(2.5, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanTwoNumbers() { + List numbers = Arrays.asList(1.0, 7.0); + assertEquals(5.0, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanMultipleNumbers() { + Vector numbers = new Vector<>(Arrays.asList(1.0, 2.5, 3.0, 7.5, 10.0)); + double expected = Math.sqrt(34.5); + assertEquals(expected, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanThreeNumbers() { + List numbers = Arrays.asList(3.0, 6.0, 9.0); + double expected = Math.sqrt(42.0); + assertEquals(expected, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanIdenticalNumbers() { + List numbers = Arrays.asList(5.0, 5.0, 5.0); + assertEquals(5.0, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanWithLinkedList() { + LinkedList numbers = new LinkedList<>(Arrays.asList(1.0, 5.0, 11.0)); + assertEquals(7.0, Means.quadratic(numbers), EPSILON); + } + // ========== Additional Edge Case Tests ========== @Test @@ -198,21 +245,25 @@ void testAllMeansConsistencyForIdenticalValues() { double arithmetic = Means.arithmetic(numbers); double geometric = Means.geometric(numbers); double harmonic = Means.harmonic(numbers); + double quadratic = Means.quadratic(numbers); assertEquals(7.5, arithmetic, EPSILON); assertEquals(7.5, geometric, EPSILON); assertEquals(7.5, harmonic, EPSILON); + assertEquals(7.5, quadratic, EPSILON); } @Test void testMeansRelationship() { - // For positive numbers, harmonic mean ≀ geometric mean ≀ arithmetic mean + // For positive numbers, harmonic mean ≀ geometric mean ≀ arithmetic mean ≀ quadratic mean List numbers = Arrays.asList(2.0, 4.0, 8.0); double arithmetic = Means.arithmetic(numbers); double geometric = Means.geometric(numbers); double harmonic = Means.harmonic(numbers); + double quadratic = Means.quadratic(numbers); assertTrue(harmonic <= geometric, "Harmonic mean should be ≀ geometric mean"); assertTrue(geometric <= arithmetic, "Geometric mean should be ≀ arithmetic mean"); + assertTrue(arithmetic <= quadratic, "Arithmetic mean should be ≀ quadratic mean"); } }