diff --git a/.github/workflows/close-failed-prs.yml b/.github/workflows/close-failed-prs.yml
new file mode 100644
index 000000000000..f1bf3690f13f
--- /dev/null
+++ b/.github/workflows/close-failed-prs.yml
@@ -0,0 +1,154 @@
+name: Close stale PRs with failed workflows
+
+on:
+ schedule:
+ - cron: '0 3 * * *' # runs daily at 03:00 UTC
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ issues: write
+ pull-requests: write
+
+jobs:
+ close-stale:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Close stale PRs
+ uses: actions/github-script@v8
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const mainBranches = ['main', 'master'];
+ const cutoffDays = 14;
+ const cutoff = new Date();
+ cutoff.setDate(cutoff.getDate() - cutoffDays);
+
+ console.log(`Checking PRs older than: ${cutoff.toISOString()}`);
+
+ try {
+ const { data: prs } = await github.rest.pulls.list({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'open',
+ sort: 'updated',
+ direction: 'asc',
+ per_page: 100
+ });
+
+ console.log(`Found ${prs.length} open PRs to check`);
+
+ for (const pr of prs) {
+ try {
+ const updated = new Date(pr.updated_at);
+
+ if (updated > cutoff) {
+ console.log(`⏩ Skipping PR #${pr.number} - updated recently`);
+ continue;
+ }
+
+ console.log(`🔍 Checking PR #${pr.number}: "${pr.title}"`);
+
+ // Get commits
+ const commits = await github.paginate(github.rest.pulls.listCommits, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: pr.number,
+ per_page: 100
+ });
+
+ const meaningfulCommits = commits.filter(c => {
+ const msg = c.commit.message.toLowerCase();
+ const isMergeFromMain = mainBranches.some(branch =>
+ msg.startsWith(`merge branch '${branch}'`) ||
+ msg.includes(`merge remote-tracking branch '${branch}'`)
+ );
+ return !isMergeFromMain;
+ });
+
+ // Get checks with error handling
+ let hasFailedChecks = false;
+ let allChecksCompleted = false;
+ let hasChecks = false;
+
+ try {
+ const { data: checks } = await github.rest.checks.listForRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: pr.head.sha
+ });
+
+ hasChecks = checks.check_runs.length > 0;
+ hasFailedChecks = checks.check_runs.some(c => c.conclusion === 'failure');
+ allChecksCompleted = checks.check_runs.every(c =>
+ c.status === 'completed' || c.status === 'skipped'
+ );
+ } catch (error) {
+ console.log(`⚠️ Could not fetch checks for PR #${pr.number}: ${error.message}`);
+ }
+
+ // Get workflow runs with error handling
+ let hasFailedWorkflows = false;
+ let allWorkflowsCompleted = false;
+ let hasWorkflows = false;
+
+ try {
+ const { data: runs } = await github.rest.actions.listWorkflowRuns({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ head_sha: pr.head.sha,
+ per_page: 50
+ });
+
+ hasWorkflows = runs.workflow_runs.length > 0;
+ hasFailedWorkflows = runs.workflow_runs.some(r => r.conclusion === 'failure');
+ allWorkflowsCompleted = runs.workflow_runs.every(r =>
+ ['completed', 'skipped', 'cancelled'].includes(r.status)
+ );
+
+ console.log(`PR #${pr.number}: ${runs.workflow_runs.length} workflow runs found`);
+
+ } catch (error) {
+ console.log(`⚠️ Could not fetch workflow runs for PR #${pr.number}: ${error.message}`);
+ }
+
+ console.log(`PR #${pr.number}: ${meaningfulCommits.length} meaningful commits`);
+ console.log(`Checks - has: ${hasChecks}, failed: ${hasFailedChecks}, completed: ${allChecksCompleted}`);
+ console.log(`Workflows - has: ${hasWorkflows}, failed: ${hasFailedWorkflows}, completed: ${allWorkflowsCompleted}`);
+
+ // Combine conditions - only consider if we actually have checks/workflows
+ const hasAnyFailure = (hasChecks && hasFailedChecks) || (hasWorkflows && hasFailedWorkflows);
+ const allCompleted = (!hasChecks || allChecksCompleted) && (!hasWorkflows || allWorkflowsCompleted);
+
+ if (meaningfulCommits.length === 0 && hasAnyFailure && allCompleted) {
+ console.log(`✅ Closing PR #${pr.number} (${pr.title})`);
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ body: `This pull request has been automatically closed because its workflows or checks failed and it has been inactive for more than ${cutoffDays} days. Please fix the workflows and reopen if you'd like to continue. Merging from main/master alone does not count as activity.`
+ });
+
+ await github.rest.pulls.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: pr.number,
+ state: 'closed'
+ });
+
+ console.log(`✅ Successfully closed PR #${pr.number}`);
+ } else {
+ console.log(`⏩ Not closing PR #${pr.number} - conditions not met`);
+ }
+
+ } catch (prError) {
+ console.error(`❌ Error processing PR #${pr.number}: ${prError.message}`);
+ continue;
+ }
+ }
+
+ } catch (error) {
+ console.error(`❌ Fatal error: ${error.message}`);
+ throw error;
+ }
\ No newline at end of file
diff --git a/DIRECTORY.md b/DIRECTORY.md
index b311b10fa177..47833a3f59f2 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -29,8 +29,10 @@
- 📄 [BcdConversion](src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java)
- 📄 [BinaryPalindromeCheck](src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.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)
- 📄 [ClearLeftmostSetBit](src/main/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBit.java)
+ - 📄 [CountBitsFlip](src/main/java/com/thealgorithms/bitmanipulation/CountBitsFlip.java)
- 📄 [CountLeadingZeros](src/main/java/com/thealgorithms/bitmanipulation/CountLeadingZeros.java)
- 📄 [CountSetBits](src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java)
- 📄 [FindNthBit](src/main/java/com/thealgorithms/bitmanipulation/FindNthBit.java)
@@ -74,6 +76,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)
+ - 📄 [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)
- 📄 [ProductCipher](src/main/java/com/thealgorithms/ciphers/ProductCipher.java)
@@ -89,6 +92,9 @@
- 📄 [CompositeLFSR](src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java)
- 📄 [LFSR](src/main/java/com/thealgorithms/ciphers/a5/LFSR.java)
- 📄 [Utils](src/main/java/com/thealgorithms/ciphers/a5/Utils.java)
+ - 📁 **compression**
+ - 📄 [RunLengthEncoding](src/main/java/com/thealgorithms/compression/RunLengthEncoding.java)
+ - 📄 [ShannonFano](src/main/java/com/thealgorithms/compression/ShannonFano.java)
- 📁 **conversions**
- 📄 [AffineConverter](src/main/java/com/thealgorithms/conversions/AffineConverter.java)
- 📄 [AnyBaseToAnyBase](src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java)
@@ -98,6 +104,7 @@
- 📄 [BinaryToDecimal](src/main/java/com/thealgorithms/conversions/BinaryToDecimal.java)
- 📄 [BinaryToHexadecimal](src/main/java/com/thealgorithms/conversions/BinaryToHexadecimal.java)
- 📄 [BinaryToOctal](src/main/java/com/thealgorithms/conversions/BinaryToOctal.java)
+ - 📄 [CoordinateConverter](src/main/java/com/thealgorithms/conversions/CoordinateConverter.java)
- 📄 [DecimalToAnyBase](src/main/java/com/thealgorithms/conversions/DecimalToAnyBase.java)
- 📄 [DecimalToBinary](src/main/java/com/thealgorithms/conversions/DecimalToBinary.java)
- 📄 [DecimalToHexadecimal](src/main/java/com/thealgorithms/conversions/DecimalToHexadecimal.java)
@@ -118,6 +125,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)
+ - 📄 [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)
- 📄 [UnitsConverter](src/main/java/com/thealgorithms/conversions/UnitsConverter.java)
@@ -157,6 +165,7 @@
- 📄 [BoruvkaAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java)
- 📄 [ConnectedComponent](src/main/java/com/thealgorithms/datastructures/graphs/ConnectedComponent.java)
- 📄 [Cycles](src/main/java/com/thealgorithms/datastructures/graphs/Cycles.java)
+ - 📄 [DialsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/DialsAlgorithm.java)
- 📄 [DijkstraAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithm.java)
- 📄 [DijkstraOptimizedAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithm.java)
- 📄 [EdmondsBlossomAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java)
@@ -171,6 +180,7 @@
- 📄 [MatrixGraphs](src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java)
- 📄 [PrimMST](src/main/java/com/thealgorithms/datastructures/graphs/PrimMST.java)
- 📄 [TarjansAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java)
+ - 📄 [TwoSat](src/main/java/com/thealgorithms/datastructures/graphs/TwoSat.java)
- 📄 [UndirectedAdjacencyListGraph](src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java)
- 📄 [WelshPowell](src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java)
- 📁 **hashmap**
@@ -199,10 +209,12 @@
- 📄 [MinPriorityQueue](src/main/java/com/thealgorithms/datastructures/heaps/MinPriorityQueue.java)
- 📁 **lists**
- 📄 [CircleLinkedList](src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java)
+ - 📄 [CircularDoublyLinkedList](src/main/java/com/thealgorithms/datastructures/lists/CircularDoublyLinkedList.java)
- 📄 [CountSinglyLinkedListRecursion](src/main/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursion.java)
- 📄 [CreateAndDetectLoop](src/main/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoop.java)
- 📄 [CursorLinkedList](src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java)
- 📄 [DoublyLinkedList](src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java)
+ - 📄 [FlattenMultilevelLinkedList](src/main/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedList.java)
- 📄 [MergeKSortedLinkedList](src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java)
- 📄 [MergeSortedArrayList](src/main/java/com/thealgorithms/datastructures/lists/MergeSortedArrayList.java)
- 📄 [MergeSortedSinglyLinkedList](src/main/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedList.java)
@@ -215,6 +227,7 @@
- 📄 [SinglyLinkedListNode](src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedListNode.java)
- 📄 [SkipList](src/main/java/com/thealgorithms/datastructures/lists/SkipList.java)
- 📄 [SortedLinkedList](src/main/java/com/thealgorithms/datastructures/lists/SortedLinkedList.java)
+ - 📄 [TortoiseHareAlgo](src/main/java/com/thealgorithms/datastructures/lists/TortoiseHareAlgo.java)
- 📁 **queues**
- 📄 [CircularQueue](src/main/java/com/thealgorithms/datastructures/queues/CircularQueue.java)
- 📄 [Deque](src/main/java/com/thealgorithms/datastructures/queues/Deque.java)
@@ -299,6 +312,7 @@
- 📄 [ClimbingStairs](src/main/java/com/thealgorithms/dynamicprogramming/ClimbingStairs.java)
- 📄 [CoinChange](src/main/java/com/thealgorithms/dynamicprogramming/CoinChange.java)
- 📄 [CountFriendsPairing](src/main/java/com/thealgorithms/dynamicprogramming/CountFriendsPairing.java)
+ - 📄 [DamerauLevenshteinDistance](src/main/java/com/thealgorithms/dynamicprogramming/DamerauLevenshteinDistance.java)
- 📄 [DiceThrow](src/main/java/com/thealgorithms/dynamicprogramming/DiceThrow.java)
- 📄 [EditDistance](src/main/java/com/thealgorithms/dynamicprogramming/EditDistance.java)
- 📄 [EggDropping](src/main/java/com/thealgorithms/dynamicprogramming/EggDropping.java)
@@ -319,9 +333,11 @@
- 📄 [LongestValidParentheses](src/main/java/com/thealgorithms/dynamicprogramming/LongestValidParentheses.java)
- 📄 [MatrixChainMultiplication](src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplication.java)
- 📄 [MatrixChainRecursiveTopDownMemoisation](src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisation.java)
+ - 📄 [MaximumProductSubarray](src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java)
- 📄 [MaximumSumOfNonAdjacentElements](src/main/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElements.java)
- 📄 [MinimumPathSum](src/main/java/com/thealgorithms/dynamicprogramming/MinimumPathSum.java)
- 📄 [MinimumSumPartition](src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java)
+ - 📄 [NeedlemanWunsch](src/main/java/com/thealgorithms/dynamicprogramming/NeedlemanWunsch.java)
- 📄 [NewManShanksPrime](src/main/java/com/thealgorithms/dynamicprogramming/NewManShanksPrime.java)
- 📄 [OptimalJobScheduling](src/main/java/com/thealgorithms/dynamicprogramming/OptimalJobScheduling.java)
- 📄 [PalindromicPartitioning](src/main/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioning.java)
@@ -329,6 +345,7 @@
- 📄 [RegexMatching](src/main/java/com/thealgorithms/dynamicprogramming/RegexMatching.java)
- 📄 [RodCutting](src/main/java/com/thealgorithms/dynamicprogramming/RodCutting.java)
- 📄 [ShortestCommonSupersequenceLength](src/main/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLength.java)
+ - 📄 [SmithWaterman](src/main/java/com/thealgorithms/dynamicprogramming/SmithWaterman.java)
- 📄 [SubsetCount](src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java)
- 📄 [SubsetSum](src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java)
- 📄 [SubsetSumSpaceOptimized](src/main/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimized.java)
@@ -343,15 +360,25 @@
- 📄 [BresenhamLine](src/main/java/com/thealgorithms/geometry/BresenhamLine.java)
- 📄 [ConvexHull](src/main/java/com/thealgorithms/geometry/ConvexHull.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)
- 📄 [MidpointEllipse](src/main/java/com/thealgorithms/geometry/MidpointEllipse.java)
- 📄 [Point](src/main/java/com/thealgorithms/geometry/Point.java)
+ - 📄 [WusLine](src/main/java/com/thealgorithms/geometry/WusLine.java)
- 📁 **graph**
+ - 📄 [BronKerbosch](src/main/java/com/thealgorithms/graph/BronKerbosch.java)
- 📄 [ConstrainedShortestPath](src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java)
+ - 📄 [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)
- 📄 [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)
- 📄 [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)
+ - 📄 [ZeroOneBfs](src/main/java/com/thealgorithms/graph/ZeroOneBfs.java)
- 📁 **greedyalgorithms**
- 📄 [ActivitySelection](src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java)
- 📄 [BandwidthAllocation](src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java)
@@ -404,6 +431,7 @@
- 📄 [DistanceFormula](src/main/java/com/thealgorithms/maths/DistanceFormula.java)
- 📄 [DudeneyNumber](src/main/java/com/thealgorithms/maths/DudeneyNumber.java)
- 📄 [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)
- 📄 [FFT](src/main/java/com/thealgorithms/maths/FFT.java)
- 📄 [FFTBluestein](src/main/java/com/thealgorithms/maths/FFTBluestein.java)
@@ -426,7 +454,9 @@
- 📄 [GCDRecursion](src/main/java/com/thealgorithms/maths/GCDRecursion.java)
- 📄 [Gaussian](src/main/java/com/thealgorithms/maths/Gaussian.java)
- 📄 [GenericRoot](src/main/java/com/thealgorithms/maths/GenericRoot.java)
+ - 📄 [GermainPrimeAndSafePrime](src/main/java/com/thealgorithms/maths/GermainPrimeAndSafePrime.java)
- 📄 [GoldbachConjecture](src/main/java/com/thealgorithms/maths/GoldbachConjecture.java)
+ - 📄 [HappyNumber](src/main/java/com/thealgorithms/maths/HappyNumber.java)
- 📄 [HarshadNumber](src/main/java/com/thealgorithms/maths/HarshadNumber.java)
- 📄 [HeronsFormula](src/main/java/com/thealgorithms/maths/HeronsFormula.java)
- 📄 [JosephusProblem](src/main/java/com/thealgorithms/maths/JosephusProblem.java)
@@ -450,6 +480,7 @@
- 📄 [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)
+ - 📄 [NumberPersistence](src/main/java/com/thealgorithms/maths/NumberPersistence.java)
- 📄 [PalindromeNumber](src/main/java/com/thealgorithms/maths/PalindromeNumber.java)
- 📄 [ParseInteger](src/main/java/com/thealgorithms/maths/ParseInteger.java)
- 📄 [PascalTriangle](src/main/java/com/thealgorithms/maths/PascalTriangle.java)
@@ -457,6 +488,7 @@
- 📄 [PerfectNumber](src/main/java/com/thealgorithms/maths/PerfectNumber.java)
- 📄 [PerfectSquare](src/main/java/com/thealgorithms/maths/PerfectSquare.java)
- 📄 [Perimeter](src/main/java/com/thealgorithms/maths/Perimeter.java)
+ - 📄 [PiApproximation](src/main/java/com/thealgorithms/maths/PiApproximation.java)
- 📄 [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)
@@ -475,6 +507,7 @@
- 📄 [ReverseNumber](src/main/java/com/thealgorithms/maths/ReverseNumber.java)
- 📄 [RomanNumeralUtil](src/main/java/com/thealgorithms/maths/RomanNumeralUtil.java)
- 📄 [SecondMinMax](src/main/java/com/thealgorithms/maths/SecondMinMax.java)
+ - 📄 [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)
- 📄 [SolovayStrassenPrimalityTest](src/main/java/com/thealgorithms/maths/SolovayStrassenPrimalityTest.java)
@@ -486,6 +519,7 @@
- 📄 [SumOfArithmeticSeries](src/main/java/com/thealgorithms/maths/SumOfArithmeticSeries.java)
- 📄 [SumOfDigits](src/main/java/com/thealgorithms/maths/SumOfDigits.java)
- 📄 [SumOfOddNumbers](src/main/java/com/thealgorithms/maths/SumOfOddNumbers.java)
+ - 📄 [SumOfSquares](src/main/java/com/thealgorithms/maths/SumOfSquares.java)
- 📄 [SumWithoutArithmeticOperators](src/main/java/com/thealgorithms/maths/SumWithoutArithmeticOperators.java)
- 📄 [TrinomialTriangle](src/main/java/com/thealgorithms/maths/TrinomialTriangle.java)
- 📄 [TwinPrime](src/main/java/com/thealgorithms/maths/TwinPrime.java)
@@ -493,6 +527,7 @@
- 📄 [VampireNumber](src/main/java/com/thealgorithms/maths/VampireNumber.java)
- 📄 [VectorCrossProduct](src/main/java/com/thealgorithms/maths/VectorCrossProduct.java)
- 📄 [Volume](src/main/java/com/thealgorithms/maths/Volume.java)
+ - 📄 [ZellersCongruence](src/main/java/com/thealgorithms/maths/ZellersCongruence.java)
- 📁 **matrix**
- 📄 [InverseOfMatrix](src/main/java/com/thealgorithms/matrix/InverseOfMatrix.java)
- 📄 [MatrixMultiplication](src/main/java/com/thealgorithms/matrix/MatrixMultiplication.java)
@@ -538,12 +573,11 @@
- 📄 [Dijkstra](src/main/java/com/thealgorithms/others/Dijkstra.java)
- 📄 [FloydTriangle](src/main/java/com/thealgorithms/others/FloydTriangle.java)
- 📄 [GaussLegendre](src/main/java/com/thealgorithms/others/GaussLegendre.java)
- - 📄 [HappyNumbersSeq](src/main/java/com/thealgorithms/others/HappyNumbersSeq.java)
- 📄 [Huffman](src/main/java/com/thealgorithms/others/Huffman.java)
- 📄 [Implementing_auto_completing_features_using_trie](src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java)
- 📄 [InsertDeleteInArray](src/main/java/com/thealgorithms/others/InsertDeleteInArray.java)
+ - 📄 [IterativeFloodFill](src/main/java/com/thealgorithms/others/IterativeFloodFill.java)
- 📄 [KochSnowflake](src/main/java/com/thealgorithms/others/KochSnowflake.java)
- - 📄 [Krishnamurthy](src/main/java/com/thealgorithms/others/Krishnamurthy.java)
- 📄 [LineSweep](src/main/java/com/thealgorithms/others/LineSweep.java)
- 📄 [LinearCongruentialGenerator](src/main/java/com/thealgorithms/others/LinearCongruentialGenerator.java)
- 📄 [LowestBasePalindrome](src/main/java/com/thealgorithms/others/LowestBasePalindrome.java)
@@ -552,6 +586,7 @@
- 📄 [MaximumSumOfDistinctSubarraysWithLengthK](src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java)
- 📄 [MemoryManagementAlgorithms](src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java)
- 📄 [MiniMaxAlgorithm](src/main/java/com/thealgorithms/others/MiniMaxAlgorithm.java)
+ - 📄 [MosAlgorithm](src/main/java/com/thealgorithms/others/MosAlgorithm.java)
- 📄 [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)
@@ -562,6 +597,8 @@
- 📄 [Verhoeff](src/main/java/com/thealgorithms/others/Verhoeff.java)
- 📁 **cn**
- 📄 [HammingDistance](src/main/java/com/thealgorithms/others/cn/HammingDistance.java)
+ - 📁 **physics**
+ - 📄 [GroundToGroundProjectileMotion](src/main/java/com/thealgorithms/physics/GroundToGroundProjectileMotion.java)
- 📁 **puzzlesandgames**
- 📄 [Sudoku](src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java)
- 📄 [TowerOfHanoi](src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java)
@@ -574,8 +611,10 @@
- 📄 [RandomizedQuickSort](src/main/java/com/thealgorithms/randomized/RandomizedQuickSort.java)
- 📄 [ReservoirSampling](src/main/java/com/thealgorithms/randomized/ReservoirSampling.java)
- 📁 **recursion**
+ - 📄 [DiceThrower](src/main/java/com/thealgorithms/recursion/DiceThrower.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)
- 📁 **scheduling**
- 📄 [AgingScheduling](src/main/java/com/thealgorithms/scheduling/AgingScheduling.java)
- 📄 [EDFScheduling](src/main/java/com/thealgorithms/scheduling/EDFScheduling.java)
@@ -630,6 +669,7 @@
- 📄 [RowColumnWiseSorted2dArrayBinarySearch](src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java)
- 📄 [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)
@@ -641,6 +681,7 @@
- 📄 [MaxSumKSizeSubarray](src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java)
- 📄 [MaximumSlidingWindow](src/main/java/com/thealgorithms/slidingwindow/MaximumSlidingWindow.java)
- 📄 [MinSumKSizeSubarray](src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java)
+ - 📄 [MinimumWindowSubstring](src/main/java/com/thealgorithms/slidingwindow/MinimumWindowSubstring.java)
- 📄 [ShortestCoprimeSegment](src/main/java/com/thealgorithms/slidingwindow/ShortestCoprimeSegment.java)
- 📁 **sorts**
- 📄 [AdaptiveMergeSort](src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java)
@@ -719,6 +760,7 @@
- 📁 **strings**
- 📄 [AhoCorasick](src/main/java/com/thealgorithms/strings/AhoCorasick.java)
- 📄 [Alphabetical](src/main/java/com/thealgorithms/strings/Alphabetical.java)
+ - 📄 [AlternativeStringArrange](src/main/java/com/thealgorithms/strings/AlternativeStringArrange.java)
- 📄 [Anagrams](src/main/java/com/thealgorithms/strings/Anagrams.java)
- 📄 [CharactersSame](src/main/java/com/thealgorithms/strings/CharactersSame.java)
- 📄 [CheckVowels](src/main/java/com/thealgorithms/strings/CheckVowels.java)
@@ -726,6 +768,7 @@
- 📄 [CountWords](src/main/java/com/thealgorithms/strings/CountWords.java)
- 📄 [HammingDistance](src/main/java/com/thealgorithms/strings/HammingDistance.java)
- 📄 [HorspoolSearch](src/main/java/com/thealgorithms/strings/HorspoolSearch.java)
+ - 📄 [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)
- 📄 [LetterCombinationsOfPhoneNumber](src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java)
@@ -781,8 +824,10 @@
- 📄 [BcdConversionTest](src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java)
- 📄 [BinaryPalindromeCheckTest](src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.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)
- 📄 [ClearLeftmostSetBitTest](src/test/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBitTest.java)
+ - 📄 [CountBitsFlipTest](src/test/java/com/thealgorithms/bitmanipulation/CountBitsFlipTest.java)
- 📄 [CountLeadingZerosTest](src/test/java/com/thealgorithms/bitmanipulation/CountLeadingZerosTest.java)
- 📄 [CountSetBitsTest](src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java)
- 📄 [FindNthBitTest](src/test/java/com/thealgorithms/bitmanipulation/FindNthBitTest.java)
@@ -825,6 +870,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)
+ - 📄 [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)
- 📄 [RSATest](src/test/java/com/thealgorithms/ciphers/RSATest.java)
@@ -836,6 +882,9 @@
- 📄 [A5CipherTest](src/test/java/com/thealgorithms/ciphers/a5/A5CipherTest.java)
- 📄 [A5KeyStreamGeneratorTest](src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java)
- 📄 [LFSRTest](src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java)
+ - 📁 **compression**
+ - 📄 [RunLengthEncodingTest](src/test/java/com/thealgorithms/compression/RunLengthEncodingTest.java)
+ - 📄 [ShannonFanoTest](src/test/java/com/thealgorithms/compression/ShannonFanoTest.java)
- 📁 **conversions**
- 📄 [AffineConverterTest](src/test/java/com/thealgorithms/conversions/AffineConverterTest.java)
- 📄 [AnyBaseToDecimalTest](src/test/java/com/thealgorithms/conversions/AnyBaseToDecimalTest.java)
@@ -844,6 +893,7 @@
- 📄 [BinaryToDecimalTest](src/test/java/com/thealgorithms/conversions/BinaryToDecimalTest.java)
- 📄 [BinaryToHexadecimalTest](src/test/java/com/thealgorithms/conversions/BinaryToHexadecimalTest.java)
- 📄 [BinaryToOctalTest](src/test/java/com/thealgorithms/conversions/BinaryToOctalTest.java)
+ - 📄 [CoordinateConverterTest](src/test/java/com/thealgorithms/conversions/CoordinateConverterTest.java)
- 📄 [DecimalToAnyBaseTest](src/test/java/com/thealgorithms/conversions/DecimalToAnyBaseTest.java)
- 📄 [DecimalToBinaryTest](src/test/java/com/thealgorithms/conversions/DecimalToBinaryTest.java)
- 📄 [DecimalToHexadecimalTest](src/test/java/com/thealgorithms/conversions/DecimalToHexadecimalTest.java)
@@ -863,6 +913,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)
+ - 📄 [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)
- 📄 [UnitsConverterTest](src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java)
@@ -897,6 +948,7 @@
- 📄 [AStarTest](src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java)
- 📄 [BipartiteGraphDFSTest](src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java)
- 📄 [BoruvkaAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.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)
- 📄 [EdmondsBlossomAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java)
@@ -910,6 +962,7 @@
- 📄 [MatrixGraphsTest](src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java)
- 📄 [PrimMSTTest](src/test/java/com/thealgorithms/datastructures/graphs/PrimMSTTest.java)
- 📄 [TarjansAlgorithmTest](src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java)
+ - 📄 [TwoSatTest](src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java)
- 📄 [WelshPowellTest](src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java)
- 📁 **hashmap**
- 📁 **hashing**
@@ -934,9 +987,11 @@
- 📄 [MinPriorityQueueTest](src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java)
- 📁 **lists**
- 📄 [CircleLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java)
+ - 📄 [CircularDoublyLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/CircularDoublyLinkedListTest.java)
- 📄 [CountSinglyLinkedListRecursionTest](src/test/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursionTest.java)
- 📄 [CreateAndDetectLoopTest](src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java)
- 📄 [CursorLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java)
+ - 📄 [FlattenMultilevelLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedListTest.java)
- 📄 [MergeKSortedLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java)
- 📄 [MergeSortedArrayListTest](src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java)
- 📄 [MergeSortedSinglyLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java)
@@ -947,6 +1002,7 @@
- 📄 [SinglyLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java)
- 📄 [SkipListTest](src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java)
- 📄 [SortedLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/SortedLinkedListTest.java)
+ - 📄 [TortoiseHareAlgoTest](src/test/java/com/thealgorithms/datastructures/lists/TortoiseHareAlgoTest.java)
- 📁 **queues**
- 📄 [CircularQueueTest](src/test/java/com/thealgorithms/datastructures/queues/CircularQueueTest.java)
- 📄 [DequeTest](src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java)
@@ -967,6 +1023,7 @@
- 📄 [AVLTreeTest](src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java)
- 📄 [BSTFromSortedArrayTest](src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java)
- 📄 [BSTIterativeTest](src/test/java/com/thealgorithms/datastructures/trees/BSTIterativeTest.java)
+ - 📄 [BSTRecursiveGenericTest](src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveGenericTest.java)
- 📄 [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)
@@ -990,6 +1047,9 @@
- 📄 [TrieTest](src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java)
- 📄 [VerticalOrderTraversalTest](src/test/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversalTest.java)
- 📄 [ZigzagTraversalTest](src/test/java/com/thealgorithms/datastructures/trees/ZigzagTraversalTest.java)
+ - 📁 **devutils**
+ - 📁 **entities**
+ - 📄 [ProcessDetailsTest](src/test/java/com/thealgorithms/devutils/entities/ProcessDetailsTest.java)
- 📁 **divideandconquer**
- 📄 [BinaryExponentiationTest](src/test/java/com/thealgorithms/divideandconquer/BinaryExponentiationTest.java)
- 📄 [ClosestPairTest](src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java)
@@ -1010,6 +1070,7 @@
- 📄 [CoinChangeTest](src/test/java/com/thealgorithms/dynamicprogramming/CoinChangeTest.java)
- 📄 [CountFriendsPairingTest](src/test/java/com/thealgorithms/dynamicprogramming/CountFriendsPairingTest.java)
- 📄 [DPTest](src/test/java/com/thealgorithms/dynamicprogramming/DPTest.java)
+ - 📄 [DamerauLevenshteinDistanceTest](src/test/java/com/thealgorithms/dynamicprogramming/DamerauLevenshteinDistanceTest.java)
- 📄 [EditDistanceTest](src/test/java/com/thealgorithms/dynamicprogramming/EditDistanceTest.java)
- 📄 [EggDroppingTest](src/test/java/com/thealgorithms/dynamicprogramming/EggDroppingTest.java)
- 📄 [FibonacciTest](src/test/java/com/thealgorithms/dynamicprogramming/FibonacciTest.java)
@@ -1028,9 +1089,11 @@
- 📄 [LongestValidParenthesesTest](src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java)
- 📄 [MatrixChainMultiplicationTest](src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplicationTest.java)
- 📄 [MatrixChainRecursiveTopDownMemoisationTest](src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisationTest.java)
+ - 📄 [MaximumProductSubarrayTest](src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java)
- 📄 [MaximumSumOfNonAdjacentElementsTest](src/test/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElementsTest.java)
- 📄 [MinimumPathSumTest](src/test/java/com/thealgorithms/dynamicprogramming/MinimumPathSumTest.java)
- 📄 [MinimumSumPartitionTest](src/test/java/com/thealgorithms/dynamicprogramming/MinimumSumPartitionTest.java)
+ - 📄 [NeedlemanWunschTest](src/test/java/com/thealgorithms/dynamicprogramming/NeedlemanWunschTest.java)
- 📄 [NewManShanksPrimeTest](src/test/java/com/thealgorithms/dynamicprogramming/NewManShanksPrimeTest.java)
- 📄 [OptimalJobSchedulingTest](src/test/java/com/thealgorithms/dynamicprogramming/OptimalJobSchedulingTest.java)
- 📄 [PalindromicPartitioningTest](src/test/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioningTest.java)
@@ -1038,6 +1101,7 @@
- 📄 [RegexMatchingTest](src/test/java/com/thealgorithms/dynamicprogramming/RegexMatchingTest.java)
- 📄 [RodCuttingTest](src/test/java/com/thealgorithms/dynamicprogramming/RodCuttingTest.java)
- 📄 [ShortestCommonSupersequenceLengthTest](src/test/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLengthTest.java)
+ - 📄 [SmithWatermanTest](src/test/java/com/thealgorithms/dynamicprogramming/SmithWatermanTest.java)
- 📄 [SubsetCountTest](src/test/java/com/thealgorithms/dynamicprogramming/SubsetCountTest.java)
- 📄 [SubsetSumSpaceOptimizedTest](src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimizedTest.java)
- 📄 [SubsetSumTest](src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumTest.java)
@@ -1052,15 +1116,24 @@
- 📄 [BresenhamLineTest](src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java)
- 📄 [ConvexHullTest](src/test/java/com/thealgorithms/geometry/ConvexHullTest.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)
- 📄 [MidpointEllipseTest](src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java)
- 📄 [PointTest](src/test/java/com/thealgorithms/geometry/PointTest.java)
+ - 📄 [WusLineTest](src/test/java/com/thealgorithms/geometry/WusLineTest.java)
- 📁 **graph**
+ - 📄 [BronKerboschTest](src/test/java/com/thealgorithms/graph/BronKerboschTest.java)
- 📄 [ConstrainedShortestPathTest](src/test/java/com/thealgorithms/graph/ConstrainedShortestPathTest.java)
+ - 📄 [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)
- 📄 [HopcroftKarpTest](src/test/java/com/thealgorithms/graph/HopcroftKarpTest.java)
- 📄 [PredecessorConstrainedDfsTest](src/test/java/com/thealgorithms/graph/PredecessorConstrainedDfsTest.java)
+ - 📄 [PushRelabelTest](src/test/java/com/thealgorithms/graph/PushRelabelTest.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)
+ - 📄 [ZeroOneBfsTest](src/test/java/com/thealgorithms/graph/ZeroOneBfsTest.java)
- 📁 **greedyalgorithms**
- 📄 [ActivitySelectionTest](src/test/java/com/thealgorithms/greedyalgorithms/ActivitySelectionTest.java)
- 📄 [BandwidthAllocationTest](src/test/java/com/thealgorithms/greedyalgorithms/BandwidthAllocationTest.java)
@@ -1109,6 +1182,7 @@
- 📄 [DistanceFormulaTest](src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java)
- 📄 [DudeneyNumberTest](src/test/java/com/thealgorithms/maths/DudeneyNumberTest.java)
- 📄 [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)
- 📄 [FFTTest](src/test/java/com/thealgorithms/maths/FFTTest.java)
- 📄 [FactorialRecursionTest](src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java)
@@ -1130,15 +1204,19 @@
- 📄 [GCDTest](src/test/java/com/thealgorithms/maths/GCDTest.java)
- 📄 [GaussianTest](src/test/java/com/thealgorithms/maths/GaussianTest.java)
- 📄 [GenericRootTest](src/test/java/com/thealgorithms/maths/GenericRootTest.java)
+ - 📄 [GermainPrimeAndSafePrimeTest](src/test/java/com/thealgorithms/maths/GermainPrimeAndSafePrimeTest.java)
- 📄 [GoldbachConjectureTest](src/test/java/com/thealgorithms/maths/GoldbachConjectureTest.java)
+ - 📄 [HappyNumberTest](src/test/java/com/thealgorithms/maths/HappyNumberTest.java)
- 📄 [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)
- 📄 [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)
- 📄 [KrishnamurthyNumberTest](src/test/java/com/thealgorithms/maths/KrishnamurthyNumberTest.java)
- 📄 [LeastCommonMultipleTest](src/test/java/com/thealgorithms/maths/LeastCommonMultipleTest.java)
- 📄 [LeonardoNumberTest](src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java)
+ - 📄 [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)
- 📄 [MathBuilderTest](src/test/java/com/thealgorithms/maths/MathBuilderTest.java)
@@ -1150,6 +1228,7 @@
- 📄 [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)
+ - 📄 [NumberPersistenceTest](src/test/java/com/thealgorithms/maths/NumberPersistenceTest.java)
- 📄 [PalindromeNumberTest](src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java)
- 📄 [ParseIntegerTest](src/test/java/com/thealgorithms/maths/ParseIntegerTest.java)
- 📄 [PascalTriangleTest](src/test/java/com/thealgorithms/maths/PascalTriangleTest.java)
@@ -1157,6 +1236,7 @@
- 📄 [PerfectNumberTest](src/test/java/com/thealgorithms/maths/PerfectNumberTest.java)
- 📄 [PerfectSquareTest](src/test/java/com/thealgorithms/maths/PerfectSquareTest.java)
- 📄 [PerimeterTest](src/test/java/com/thealgorithms/maths/PerimeterTest.java)
+ - 📄 [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)
- 📄 [PowerOfTwoOrNotTest](src/test/java/com/thealgorithms/maths/PowerOfTwoOrNotTest.java)
@@ -1166,6 +1246,7 @@
- 📄 [QuadraticEquationSolverTest](src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java)
- 📄 [ReverseNumberTest](src/test/java/com/thealgorithms/maths/ReverseNumberTest.java)
- 📄 [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)
- 📄 [SolovayStrassenPrimalityTestTest](src/test/java/com/thealgorithms/maths/SolovayStrassenPrimalityTestTest.java)
- 📄 [SquareFreeIntegerTest](src/test/java/com/thealgorithms/maths/SquareFreeIntegerTest.java)
@@ -1177,12 +1258,14 @@
- 📄 [SumOfArithmeticSeriesTest](src/test/java/com/thealgorithms/maths/SumOfArithmeticSeriesTest.java)
- 📄 [SumOfDigitsTest](src/test/java/com/thealgorithms/maths/SumOfDigitsTest.java)
- 📄 [SumOfOddNumbersTest](src/test/java/com/thealgorithms/maths/SumOfOddNumbersTest.java)
+ - 📄 [SumOfSquaresTest](src/test/java/com/thealgorithms/maths/SumOfSquaresTest.java)
- 📄 [SumWithoutArithmeticOperatorsTest](src/test/java/com/thealgorithms/maths/SumWithoutArithmeticOperatorsTest.java)
- 📄 [TestArmstrong](src/test/java/com/thealgorithms/maths/TestArmstrong.java)
- 📄 [TwinPrimeTest](src/test/java/com/thealgorithms/maths/TwinPrimeTest.java)
- 📄 [UniformNumbersTest](src/test/java/com/thealgorithms/maths/UniformNumbersTest.java)
- 📄 [VampireNumberTest](src/test/java/com/thealgorithms/maths/VampireNumberTest.java)
- 📄 [VolumeTest](src/test/java/com/thealgorithms/maths/VolumeTest.java)
+ - 📄 [ZellersCongruenceTest](src/test/java/com/thealgorithms/maths/ZellersCongruenceTest.java)
- 📁 **prime**
- 📄 [LiouvilleLambdaFunctionTest](src/test/java/com/thealgorithms/maths/prime/LiouvilleLambdaFunctionTest.java)
- 📄 [MillerRabinPrimalityCheckTest](src/test/java/com/thealgorithms/maths/prime/MillerRabinPrimalityCheckTest.java)
@@ -1222,14 +1305,21 @@
- 📄 [CountFriendsPairingTest](src/test/java/com/thealgorithms/others/CountFriendsPairingTest.java)
- 📄 [FirstFitCPUTest](src/test/java/com/thealgorithms/others/FirstFitCPUTest.java)
- 📄 [FloydTriangleTest](src/test/java/com/thealgorithms/others/FloydTriangleTest.java)
+ - 📄 [HuffmanTest](src/test/java/com/thealgorithms/others/HuffmanTest.java)
+ - 📄 [InsertDeleteInArrayTest](src/test/java/com/thealgorithms/others/InsertDeleteInArrayTest.java)
+ - 📄 [IterativeFloodFillTest](src/test/java/com/thealgorithms/others/IterativeFloodFillTest.java)
- 📄 [KadaneAlogrithmTest](src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java)
- 📄 [LineSweepTest](src/test/java/com/thealgorithms/others/LineSweepTest.java)
- 📄 [LinkListSortTest](src/test/java/com/thealgorithms/others/LinkListSortTest.java)
- 📄 [LowestBasePalindromeTest](src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java)
- 📄 [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)
+ - 📄 [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)
@@ -1237,6 +1327,8 @@
- 📄 [WorstFitCPUTest](src/test/java/com/thealgorithms/others/WorstFitCPUTest.java)
- 📁 **cn**
- 📄 [HammingDistanceTest](src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java)
+ - 📁 **physics**
+ - 📄 [GroundToGroundProjectileMotionTest](src/test/java/com/thealgorithms/physics/GroundToGroundProjectileMotionTest.java)
- 📁 **puzzlesandgames**
- 📄 [SudokuTest](src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java)
- 📄 [TowerOfHanoiTest](src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java)
@@ -1249,8 +1341,10 @@
- 📄 [RandomizedQuickSortTest](src/test/java/com/thealgorithms/randomized/RandomizedQuickSortTest.java)
- 📄 [ReservoirSamplingTest](src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java)
- 📁 **recursion**
+ - 📄 [DiceThrowerTest](src/test/java/com/thealgorithms/recursion/DiceThrowerTest.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)
- 📁 **scheduling**
- 📄 [AgingSchedulingTest](src/test/java/com/thealgorithms/scheduling/AgingSchedulingTest.java)
- 📄 [EDFSchedulingTest](src/test/java/com/thealgorithms/scheduling/EDFSchedulingTest.java)
@@ -1305,6 +1399,7 @@
- 📄 [RowColumnWiseSorted2dArrayBinarySearchTest](src/test/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearchTest.java)
- 📄 [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)
@@ -1317,6 +1412,7 @@
- 📄 [MaxSumKSizeSubarrayTest](src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java)
- 📄 [MaximumSlidingWindowTest](src/test/java/com/thealgorithms/slidingwindow/MaximumSlidingWindowTest.java)
- 📄 [MinSumKSizeSubarrayTest](src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java)
+ - 📄 [MinimumWindowSubstringTest](src/test/java/com/thealgorithms/slidingwindow/MinimumWindowSubstringTest.java)
- 📄 [ShortestCoprimeSegmentTest](src/test/java/com/thealgorithms/slidingwindow/ShortestCoprimeSegmentTest.java)
- 📁 **sorts**
- 📄 [AdaptiveMergeSortTest](src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java)
@@ -1393,6 +1489,7 @@
- 📁 **strings**
- 📄 [AhoCorasickTest](src/test/java/com/thealgorithms/strings/AhoCorasickTest.java)
- 📄 [AlphabeticalTest](src/test/java/com/thealgorithms/strings/AlphabeticalTest.java)
+ - 📄 [AlternativeStringArrangeTest](src/test/java/com/thealgorithms/strings/AlternativeStringArrangeTest.java)
- 📄 [AnagramsTest](src/test/java/com/thealgorithms/strings/AnagramsTest.java)
- 📄 [CharactersSameTest](src/test/java/com/thealgorithms/strings/CharactersSameTest.java)
- 📄 [CheckVowelsTest](src/test/java/com/thealgorithms/strings/CheckVowelsTest.java)
@@ -1400,6 +1497,7 @@
- 📄 [CountWordsTest](src/test/java/com/thealgorithms/strings/CountWordsTest.java)
- 📄 [HammingDistanceTest](src/test/java/com/thealgorithms/strings/HammingDistanceTest.java)
- 📄 [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)
- 📄 [LetterCombinationsOfPhoneNumberTest](src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java)
- 📄 [LongestCommonPrefixTest](src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java)
diff --git a/pom.xml b/pom.xml
index e0e3516c08eb..b7ca85e1407c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,7 +82,7 @@
+ * Arithmetic coding is a form of entropy encoding used in lossless data
+ * compression. It encodes an entire message into a single number, a fraction n
+ * where (0.0 <= n < 1.0). Unlike Huffman coding, which assigns a specific
+ * bit sequence to each symbol, arithmetic coding represents the message as a
+ * sub-interval of the [0, 1) interval.
+ *
+ * This implementation uses BigDecimal for precision to handle the shrinking
+ * intervals, making it suitable for educational purposes to demonstrate the
+ * core logic.
+ *
+ * Time Complexity: O(n*m) for compression and decompression where n is the
+ * length of the input and m is the number of unique symbols, due to the need
+ * to calculate symbol probabilities.
+ *
+ * References:
+ *
+ *
+ *
+ * BWT is a reversible data transformation algorithm that rearranges a string into runs of + * similar characters. While not a compression algorithm itself, it significantly improves + * the compressibility of data for subsequent algorithms like Move-to-Front encoding and + * Run-Length Encoding. + *
+ * + *The transform works by: + *
Important: The input string should end with a unique end-of-string marker + * (typically '$') that: + *
Time Complexity: + *
Example:
+ *
+ * Input: "banana$"
+ * Output: BWTResult("annb$aa", 4)
+ * - "annb$aa" is the transformed string (groups similar characters)
+ * - 4 is the index of the original string in the sorted rotations
+ *
+ *
+ * @see Burrows–Wheeler transform (Wikipedia)
+ */
+public final class BurrowsWheelerTransform {
+
+ private BurrowsWheelerTransform() {
+ }
+
+ /**
+ * A container for the result of the forward BWT.
+ * + * Contains the transformed string and the index of the original string + * in the sorted rotations matrix, both of which are required for the + * inverse transformation. + *
+ */ + public static class BWTResult { + /** The transformed string (last column of the sorted rotation matrix) */ + public final String transformed; + + /** The index of the original string in the sorted rotations matrix */ + public final int originalIndex; + + /** + * Constructs a BWTResult with the transformed string and original index. + * + * @param transformed the transformed string (L-column) + * @param originalIndex the index of the original string in sorted rotations + */ + public BWTResult(String transformed, int originalIndex) { + this.transformed = transformed; + this.originalIndex = originalIndex; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + BWTResult bwtResult = (BWTResult) obj; + return originalIndex == bwtResult.originalIndex && transformed.equals(bwtResult.transformed); + } + + @Override + public int hashCode() { + return 31 * transformed.hashCode() + originalIndex; + } + + @Override + public String toString() { + return "BWTResult[transformed=" + transformed + ", originalIndex=" + originalIndex + "]"; + } + } + + /** + * Performs the forward Burrows-Wheeler Transform on the input string. + *+ * The algorithm generates all cyclic rotations of the input, sorts them + * lexicographically, and returns the last column of this sorted matrix + * along with the position of the original string. + *
+ * + *Note: It is strongly recommended that the input string ends with + * a unique end-of-string marker (e.g., '$') that is lexicographically smaller + * than any other character in the string. This ensures correct inversion.
+ * + * @param text the input string to transform; must not be {@code null} + * @return a {@link BWTResult} object containing the transformed string (L-column) + * and the index of the original string in the sorted rotations matrix; + * returns {@code BWTResult("", -1)} for empty input + * @throws NullPointerException if {@code text} is {@code null} + */ + public static BWTResult transform(String text) { + if (text == null || text.isEmpty()) { + return new BWTResult("", -1); + } + + int n = text.length(); + + // Generate all rotations of the input string + String[] rotations = new String[n]; + for (int i = 0; i < n; i++) { + rotations[i] = text.substring(i) + text.substring(0, i); + } + + // Sort rotations lexicographically + Arrays.sort(rotations); + int originalIndex = Arrays.binarySearch(rotations, text); + StringBuilder lastColumn = new StringBuilder(n); + for (int i = 0; i < n; i++) { + lastColumn.append(rotations[i].charAt(n - 1)); + } + + return new BWTResult(lastColumn.toString(), originalIndex); + } + + /** + * Performs the inverse Burrows-Wheeler Transform using the LF-mapping technique. + *+ * The LF-mapping (Last-First mapping) is an efficient method to reconstruct + * the original string from the BWT output without explicitly reconstructing + * the entire sorted rotations matrix. + *
+ * + *The algorithm works by: + *
+ * LZ77 is a lossless data compression algorithm that works by finding repeated + * occurrences of data in a sliding window. It replaces subsequent occurrences + * with references (offset, length) to the first occurrence within the window. + *
+ *+ * This implementation uses a simple sliding window and lookahead buffer approach. + * Output format is a sequence of tuples (offset, length, next_character). + *
+ *+ * Time Complexity: O(n*W) in this naive implementation, where n is the input length + * and W is the window size, due to the search for the longest match. More advanced + * data structures (like suffix trees) can improve this. + *
+ *+ * References: + *
+ * LZ78 is a dictionary-based lossless data compression algorithm. It processes + * input data sequentially, building a dictionary of phrases encountered so far. + * It outputs pairs (dictionary_index, next_character), representing + * the longest match found in the dictionary plus the character that follows it. + *
+ *+ * This implementation builds the dictionary dynamically during compression. + * The dictionary index 0 represents the empty string (no prefix). + *
+ *+ * Time Complexity: O(n) on average for compression and decompression, assuming + * efficient dictionary lookups (using a HashMap), where n is the + * length of the input string. + *
+ *+ * References: + *
+ * LZW is a universal lossless data compression algorithm created by Abraham + * Lempel, Jacob Ziv, and Terry Welch. It works by building a dictionary of + * strings encountered during compression and replacing occurrences of those + * strings with a shorter code. + *
+ * + *+ * This implementation handles standard ASCII characters and provides methods for + * both compression and decompression. + *
+ * Time Complexity: O(n) for both compression and decompression, where n is the + * length of the input string. + *
+ * + *+ * References: + *
+ * MTF is a data transformation algorithm that encodes each symbol in the input + * as its current position in a dynamically-maintained list, then moves that symbol + * to the front of the list. This transformation is particularly effective when used + * after the Burrows-Wheeler Transform (BWT), as BWT groups similar characters together. + *
+ * + *The transform converts runs of repeated characters into sequences of small integers + * (often zeros), which are highly compressible by subsequent entropy encoding algorithms + * like Run-Length Encoding (RLE) or Huffman coding. This technique is used in the + * bzip2 compression algorithm. + *
+ * + *How it works: + *
Time Complexity: + *
Example:
+ *+ * Input: "annb$aa" + * Alphabet: "$abn" (initial order) + * Output: [1, 3, 0, 3, 3, 3, 0] + * + * Step-by-step: + * - 'a': index 1 in [$,a,b,n] → output 1, list becomes [a,$,b,n] + * - 'n': index 3 in [a,$,b,n] → output 3, list becomes [n,a,$,b] + * - 'n': index 0 in [n,a,$,b] → output 0, list stays [n,a,$,b] + * - 'b': index 3 in [n,a,$,b] → output 3, list becomes [b,n,a,$] + * - etc. + * + * Notice how repeated 'n' characters produce zeros after the first occurrence! + *+ * + * @see Move-to-front transform (Wikipedia) + */ +public final class MoveToFront { + + private MoveToFront() { + } + + /** + * Performs the forward Move-to-Front transform. + *
+ * Converts the input string into a list of integers, where each integer represents + * the position of the corresponding character in a dynamically-maintained alphabet list. + *
+ * + *Note: All characters in the input text must exist in the provided alphabet, + * otherwise an {@link IllegalArgumentException} is thrown. The alphabet should contain + * all unique characters that may appear in the input.
+ * + * @param text the input string to transform; if empty, returns an empty list + * @param initialAlphabet a string containing the initial ordered set of symbols + * (e.g., "$abn" or the full ASCII set); must not be empty + * when {@code text} is non-empty + * @return a list of integers representing the transformed data, where each integer + * is the index of the corresponding input character in the current alphabet state + * @throws IllegalArgumentException if {@code text} is non-empty and {@code initialAlphabet} + * is {@code null} or empty + * @throws IllegalArgumentException if any character in {@code text} is not found in + * {@code initialAlphabet} + */ + public static List+ * Reconstructs the original string from the list of indices produced by the + * forward transform. This requires the exact same initial alphabet that was + * used in the forward transform. + *
+ * + *Important: The {@code initialAlphabet} parameter must be identical + * to the one used in the forward transform, including character order, or the + * output will be incorrect.
+ * + * @param indices The list of integers from the forward transform. + * @param initialAlphabet the exact same initial alphabet string used for the forward transform; + * if {@code null} or empty, returns an empty string + * @return the original, untransformed string + * @throws IllegalArgumentException if any index in {@code indices} is negative or + * exceeds the current alphabet size + */ + public static String inverseTransform(CollectionRun-Length Encoding is a simple form of lossless data compression in which + * runs of data (sequences in which the same data value occurs in many + * consecutive data elements) are stored as a single data value and count, + * rather than as the original run. + * + *
This implementation provides methods for both compressing and decompressing + * a string. For example: + *
Time Complexity: O(n) for both compression and decompression, where n is the + * length of the input string. + * + *
References: + *
Shannon-Fano coding is an entropy encoding technique for lossless data + * compression. It assigns variable-length codes to symbols based on their + * frequencies of occurrence. It is a precursor to Huffman coding and works by + * recursively partitioning a sorted list of symbols into two sub-lists with + * nearly equal total frequencies. + * + *
The algorithm works as follows: + *
Time Complexity: O(n^2) in this implementation due to the partitioning logic, + * or O(n log n) if a more optimized partitioning strategy is used. + * Sorting takes O(n log n), where n is the number of unique symbols. + * + *
References: + *
+ */ +public final class ShannonFano { + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private ShannonFano() { + } + + /** + * A private inner class to represent a symbol and its frequency. + * Implements Comparable to allow sorting based on frequency. + */ + private static class Symbol implements Comparable- * Bloom filters are space-efficient data structures that provide a fast way to test whether an - * element is a member of a set. They may produce false positives, indicating an element is + * Bloom filters are space-efficient data structures that provide a fast way to + * test whether an + * element is a member of a set. They may produce false positives, indicating an + * element is * in the set when it is not, but they will never produce false negatives. *
* @@ -20,11 +23,14 @@ public class BloomFilter- * This method hashes the element using all defined hash functions and sets the corresponding + * This method hashes the element using all defined hash functions and sets the + * corresponding * bits in the bit array. *
* @@ -65,13 +73,16 @@ public void insert(T key) { /** * Checks if an element might be in the Bloom filter. *- * This method checks the bits at the positions computed by each hash function. If any of these - * bits are not set, the element is definitely not in the filter. If all bits are set, the element + * This method checks the bits at the positions computed by each hash function. + * If any of these + * bits are not set, the element is definitely not in the filter. If all bits + * are set, the element * might be in the filter. *
* * @param key the element to check for membership in the Bloom filter - * @return {@code true} if the element might be in the Bloom filter, {@code false} if it is definitely not + * @return {@code true} if the element might be in the Bloom filter, + * {@code false} if it is definitely not */ public boolean contains(T key) { for (Hash- * Each instance of this class represents a different hash function based on its index. + * Each instance of this class represents a different hash function based on its + * index. *
* * @param+ * Brief Idea: + *
+ * + *+ * 1. From each clause (a ∨ b), we can derive implications: + * (¬a → b) and (¬b → a) + * + * 2. We construct an implication graph using these implications. + * + * 3. For each variable x, its negation ¬x is also represented as a node. + * If x and ¬x belong to the same SCC, the expression is unsatisfiable. + * + * 4. Otherwise, we assign truth values based on the SCC order: + * If SCC(x) > SCC(¬x), then x = true; otherwise, x = false. + *+ * + *
+ * Complexities: + *
+ *+ * Usage Example: + *
+ * + *
+ * TwoSat twoSat = new TwoSat(5); // Initialize with 5 variables: x1, x2, x3, x4, x5
+ *
+ * // Add clauses
+ * twoSat.addClause(1, false, 2, false); // (x1 ∨ x2)
+ * twoSat.addClause(3, true, 2, false); // (¬x3 ∨ x2)
+ * twoSat.addClause(4, false, 5, true); // (x4 ∨ ¬x5)
+ *
+ * twoSat.solve(); // Solve the problem
+ *
+ * if (twoSat.isSolutionExists()) {
+ * boolean[] solution = twoSat.getSolutions();
+ * for (int i = 1; i <= 5; i++) {
+ * System.out.println("x" + i + " = " + solution[i]);
+ * }
+ * }
+ *
+ * Reference
+ * CP Algorithm+ * Example: To add (¬x₁ ∨ x₂), call: + *
+ * + *{@code
+ * addClause(1, true, 2, false);
+ * }
+ *
+ * @param a the first variable (1 ≤ a ≤ numberOfVariables)
+ * @param isNegateA {@code true} if variable {@code a} is negated
+ * @param b the second variable (1 ≤ b ≤ numberOfVariables)
+ * @param isNegateB {@code true} if variable {@code b} is negated
+ * @throws IllegalArgumentException if {@code a} or {@code b} are out of range
+ */
+ void addClause(int a, boolean isNegateA, int b, boolean isNegateB) {
+ if (a <= 0 || a > numberOfVariables) {
+ throw new IllegalArgumentException("Variable number must be between 1 and " + numberOfVariables);
+ }
+ if (b <= 0 || b > numberOfVariables) {
+ throw new IllegalArgumentException("Variable number must be between 1 and " + numberOfVariables);
+ }
+
+ a = isNegateA ? negate(a) : a;
+ b = isNegateB ? negate(b) : b;
+ int notA = negate(a);
+ int notB = negate(b);
+
+ // Add implications: (¬a → b) and (¬b → a)
+ graph[notA].add(b);
+ graph[notB].add(a);
+
+ // Build transpose graph
+ graphTranspose[b].add(notA);
+ graphTranspose[a].add(notB);
+ }
+
+ /**
+ * Solves the 2-SAT problem using Kosaraju's algorithm to find SCCs
+ * and determines whether a satisfying assignment exists.
+ */
+ void solve() {
+ isSolved = true;
+ int n = 2 * numberOfVariables + 1;
+
+ boolean[] visited = new boolean[n];
+ int[] component = new int[n];
+ Stack+ * Mapping rule: + *
+ * + *+ * For a variable i: + * negate(i) = i + n + * For a negated variable (i + n): + * negate(i + n) = i + * where n = numberOfVariables + *+ * + * @param a the variable index + * @return the index representing its negation + */ + private int negate(int a) { + return a <= numberOfVariables ? a + numberOfVariables : a - numberOfVariables; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedList.java new file mode 100644 index 000000000000..3c4106f178b9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedList.java @@ -0,0 +1,92 @@ +package com.thealgorithms.datastructures.lists; +/** + * Implements an algorithm to flatten a multilevel linked list. + * + * In this specific problem structure, each node has a `next` pointer (to the + * next node at the same level) and a `child` pointer (which points to the head + * of another sorted linked list). The goal is to merge all these lists into a + * single, vertically sorted linked list using the `child` pointer. + * + * The approach is a recursive one that leverages a merge utility, similar to + * the merge step in Merge Sort. It recursively flattens the list starting from + * the rightmost node and merges each node's child list with the already + * flattened list to its right. + * @see GeeksforGeeks: Flattening a Linked List + */ +public final class FlattenMultilevelLinkedList { + /** + * Private constructor to prevent instantiation of this utility class. + */ + private FlattenMultilevelLinkedList() { + } + /** + * Node represents an element in the multilevel linked list. It contains the + * integer data, a reference to the next node at the same level, and a + * reference to the head of a child list. + */ + static class Node { + int data; + Node next; + Node child; + + Node(int data) { + this.data = data; + this.next = null; + this.child = null; + } + } + + /** + * Merges two sorted linked lists (connected via the `child` pointer). + * This is a helper function for the main flatten algorithm. + * + * @param a The head of the first sorted list. + * @param b The head of the second sorted list. + * @return The head of the merged sorted list. + */ + private static Node merge(Node a, Node b) { + // If one of the lists is empty, return the other. + if (a == null) { + return b; + } + if (b == null) { + return a; + } + + Node result; + + // Choose the smaller value as the new head. + if (a.data < b.data) { + result = a; + result.child = merge(a.child, b); + } else { + result = b; + result.child = merge(a, b.child); + } + result.next = null; // Ensure the merged list has no `next` pointers. + return result; + } + + /** + * Flattens a multilevel linked list into a single sorted list. + * The flattened list is connected using the `child` pointers. + * + * @param head The head of the top-level list (connected via `next` pointers). + * @return The head of the fully flattened and sorted list. + */ + public static Node flatten(Node head) { + // Base case: if the list is empty or has only one node, it's already flattened. + if (head == null || head.next == null) { + return head; + } + + // Recursively flatten the list starting from the next node. + head.next = flatten(head.next); + + // Now, merge the current list (head's child list) with the flattened rest of the list. + head = merge(head, head.next); + + // Return the head of the fully merged list. + return head; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/README.md b/src/main/java/com/thealgorithms/datastructures/lists/README.md index 6aefa4c98e6d..5a19c3bfa990 100644 --- a/src/main/java/com/thealgorithms/datastructures/lists/README.md +++ b/src/main/java/com/thealgorithms/datastructures/lists/README.md @@ -30,3 +30,4 @@ The `next` variable points to the next node in the data structure and value stor 6. `MergeKSortedLinkedlist.java` : Merges K sorted linked list with mergesort (mergesort is also the most efficient sorting algorithm for linked list). 7. `RandomNode.java` : Selects a random node from given linked list and diplays it. 8. `SkipList.java` : Data Structure used for storing a sorted list of elements with help of a Linked list hierarchy that connects to subsequences of elements. +9. `TortoiseHareAlgo.java` : Finds the middle element of a linked list using the fast and slow pointer (Tortoise-Hare) algorithm. diff --git a/src/main/java/com/thealgorithms/datastructures/lists/TortoiseHareAlgo.java b/src/main/java/com/thealgorithms/datastructures/lists/TortoiseHareAlgo.java new file mode 100644 index 000000000000..9d803003c658 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/TortoiseHareAlgo.java @@ -0,0 +1,63 @@ +package com.thealgorithms.datastructures.lists; + +public class TortoiseHareAlgo
Uses a sweep-line approach with an event queue and status structure to + * efficiently detect intersections in 2D plane geometry.
+ * + * @see + * Bentley–Ottmann algorithm + */ +public final class BentleyOttmann { + + private BentleyOttmann() { + } + + private static final double EPS = 1e-9; + private static double currentSweepX; + + /** + * Represents a line segment with two endpoints. + */ + public static class Segment { + final Point2D.Double p1; + final Point2D.Double p2; + final int id; // Unique identifier for each segment + + Segment(Point2D.Double p1, Point2D.Double p2) { + this.p1 = p1; + this.p2 = p2; + this.id = segmentCounter++; + } + + private static int segmentCounter = 0; + + /** + * Computes the y-coordinate of this segment at a given x value. + */ + double getY(double x) { + if (Math.abs(p2.x - p1.x) < EPS) { + // Vertical segment: return midpoint y + return (p1.y + p2.y) / 2.0; + } + double t = (x - p1.x) / (p2.x - p1.x); + return p1.y + t * (p2.y - p1.y); + } + + Point2D.Double leftPoint() { + return p1.x < p2.x ? p1 : p1.x > p2.x ? p2 : p1.y < p2.y ? p1 : p2; + } + + Point2D.Double rightPoint() { + return p1.x > p2.x ? p1 : p1.x < p2.x ? p2 : p1.y > p2.y ? p1 : p2; + } + + @Override + public String toString() { + return String.format("S%d[(%.2f, %.2f), (%.2f, %.2f)]", id, p1.x, p1.y, p2.x, p2.y); + } + } + + /** + * Event types for the sweep line algorithm. + */ + private enum EventType { START, END, INTERSECTION } + + /** + * Represents an event in the event queue. + */ + private static class Event implements ComparableAn intersection point is reported when two or more segments cross or touch. + * For overlapping segments, only actual crossing/touching points are reported, + * not all points along the overlap.
+ * + * @param segments list of line segments represented as pairs of points + * @return a set of intersection points where segments meet or cross + * @throws IllegalArgumentException if the list is null or contains null points + */ + public static SetTime complexity: O(E * V^2) in the worst case, but typically faster in practice + * and near O(E * sqrt(V)) for unit networks.
+ * + *The graph is represented using a capacity matrix where capacity[u][v] is the + * capacity of the directed edge u -> v. Capacities must be non-negative. + * The algorithm builds level graphs using BFS and finds blocking flows using DFS + * with current-edge optimization.
+ * + *This implementation mirrors the API and validation style of + * {@link EdmondsKarp#maxFlow(int[][], int, int)} for consistency.
+ * + * @see Wikipedia: Dinic's algorithm + */ +public final class Dinic { + private Dinic() { + } + + /** + * Computes the maximum flow from source to sink using Dinic's algorithm. + * + * @param capacity square capacity matrix (n x n); entries must be >= 0 + * @param source source vertex index in [0, n) + * @param sink sink vertex index in [0, n) + * @return the maximum flow value + * @throws IllegalArgumentException if the input matrix is null/non-square/has negatives or + * indices invalid + */ + public static int maxFlow(int[][] capacity, int source, int sink) { + if (capacity == null || capacity.length == 0) { + throw new IllegalArgumentException("Capacity matrix must not be null or empty"); + } + final int n = capacity.length; + for (int i = 0; i < n; i++) { + if (capacity[i] == null || capacity[i].length != n) { + throw new IllegalArgumentException("Capacity matrix must be square"); + } + for (int j = 0; j < n; j++) { + if (capacity[i][j] < 0) { + throw new IllegalArgumentException("Capacities must be non-negative"); + } + } + } + if (source < 0 || sink < 0 || source >= n || sink >= n) { + throw new IllegalArgumentException("Source and sink must be valid vertex indices"); + } + if (source == sink) { + return 0; + } + + // residual capacities + int[][] residual = new int[n][n]; + for (int i = 0; i < n; i++) { + residual[i] = Arrays.copyOf(capacity[i], n); + } + + int[] level = new int[n]; + int flow = 0; + while (bfsBuildLevelGraph(residual, source, sink, level)) { + int[] next = new int[n]; // current-edge optimization + int pushed; + do { + pushed = dfsBlocking(residual, level, next, source, sink, Integer.MAX_VALUE); + flow += pushed; + } while (pushed > 0); + } + return flow; + } + + private static boolean bfsBuildLevelGraph(int[][] residual, int source, int sink, int[] level) { + Arrays.fill(level, -1); + level[source] = 0; + QueueAn MSA is a directed graph equivalent of a Minimum Spanning Tree. It is a tree rooted + * at a specific vertex 'r' that reaches all other vertices, such that the sum of the + * weights of its edges is minimized. + * + *
The algorithm works recursively: + *
Time Complexity: O(E * V) where E is the number of edges and V is the number of vertices. + * + *
References: + *
+ * An Eulerian Circuit is a path that starts and ends at the same vertex + * and visits every edge exactly once. + *
+ * + *+ * An Eulerian Path visits every edge exactly once but may start and end + * at different vertices. + *
+ * + *
+ * Algorithm Summary:
+ * 1. Compute indegree and outdegree for all vertices.
+ * 2. Check if the graph satisfies Eulerian path or circuit conditions.
+ * 3. Verify that all vertices with non-zero degree are weakly connected (undirected connectivity).
+ * 4. Use Hierholzer’s algorithm to build the path by exploring unused edges iteratively.
+ *
+ * Time Complexity: O(E + V).
+ * Space Complexity: O(V + E).
+ *
Given an n x m cost matrix (n tasks, m workers), finds a minimum-cost + * one-to-one assignment. If the matrix is rectangular, the algorithm pads to a + * square internally. Costs must be finite non-negative integers. + * + *
Time complexity: O(n^3) with n = max(rows, cols). + * + *
API returns the assignment as an array where {@code assignment[i]} is the + * column chosen for row i (or -1 if unassigned when rows != cols), and a total + * minimal cost. + * + * @see Wikipedia: Hungarian algorithm + */ +public final class HungarianAlgorithm { + + private HungarianAlgorithm() { + } + + /** Result holder for the Hungarian algorithm. */ + public static final class Result { + public final int[] assignment; // assignment[row] = col or -1 + public final int minCost; + + public Result(int[] assignment, int minCost) { + this.assignment = assignment; + this.minCost = minCost; + } + } + + /** + * Solves the assignment problem for a non-negative cost matrix. + * + * @param cost an r x c matrix of non-negative costs + * @return Result with row-to-column assignment and minimal total cost + * @throws IllegalArgumentException for null/empty or negative costs + */ + public static Result solve(int[][] cost) { + validate(cost); + int rows = cost.length; + int cols = cost[0].length; + int n = Math.max(rows, cols); + + // Build square matrix with padding 0 for missing cells + int[][] a = new int[n][n]; + for (int i = 0; i < n; i++) { + if (i < rows) { + for (int j = 0; j < n; j++) { + a[i][j] = (j < cols) ? cost[i][j] : 0; + } + } else { + Arrays.fill(a[i], 0); + } + } + + // Potentials and matching arrays + int[] u = new int[n + 1]; + int[] v = new int[n + 1]; + int[] p = new int[n + 1]; + int[] way = new int[n + 1]; + + for (int i = 1; i <= n; i++) { + p[0] = i; + int j0 = 0; + int[] minv = new int[n + 1]; + boolean[] used = new boolean[n + 1]; + Arrays.fill(minv, Integer.MAX_VALUE); + Arrays.fill(used, false); + do { + used[j0] = true; + int i0 = p[j0]; + int delta = Integer.MAX_VALUE; + int j1 = 0; + for (int j = 1; j <= n; j++) { + if (!used[j]) { + int cur = a[i0 - 1][j - 1] - u[i0] - v[j]; + if (cur < minv[j]) { + minv[j] = cur; + way[j] = j0; + } + if (minv[j] < delta) { + delta = minv[j]; + j1 = j; + } + } + } + for (int j = 0; j <= n; j++) { + if (used[j]) { + u[p[j]] += delta; + v[j] -= delta; + } else { + minv[j] -= delta; + } + } + j0 = j1; + } while (p[j0] != 0); + do { + int j1 = way[j0]; + p[j0] = p[j1]; + j0 = j1; + } while (j0 != 0); + } + + int[] matchColForRow = new int[n]; + Arrays.fill(matchColForRow, -1); + for (int j = 1; j <= n; j++) { + if (p[j] != 0) { + matchColForRow[p[j] - 1] = j - 1; + } + } + + // Build assignment for original rows only, ignore padded rows + int[] assignment = new int[rows]; + Arrays.fill(assignment, -1); + int total = 0; + for (int i = 0; i < rows; i++) { + int j = matchColForRow[i]; + if (j >= 0 && j < cols) { + assignment[i] = j; + total += cost[i][j]; + } + } + return new Result(assignment, total); + } + + private static void validate(int[][] cost) { + if (cost == null || cost.length == 0) { + throw new IllegalArgumentException("Cost matrix must not be null or empty"); + } + int c = cost[0].length; + if (c == 0) { + throw new IllegalArgumentException("Cost matrix must have at least 1 column"); + } + for (int i = 0; i < cost.length; i++) { + if (cost[i] == null || cost[i].length != c) { + throw new IllegalArgumentException("Cost matrix must be rectangular with equal row lengths"); + } + for (int j = 0; j < c; j++) { + if (cost[i][j] < 0) { + throw new IllegalArgumentException("Costs must be non-negative"); + } + } + } + } +} diff --git a/src/main/java/com/thealgorithms/graph/PushRelabel.java b/src/main/java/com/thealgorithms/graph/PushRelabel.java new file mode 100644 index 000000000000..1bfb5ceacce0 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/PushRelabel.java @@ -0,0 +1,162 @@ +package com.thealgorithms.graph; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Queue; + +/** + * Push–Relabel (Relabel-to-Front variant simplified to array scanning) for maximum flow. + * + *
Input graph is a capacity matrix where {@code capacity[u][v]} is the capacity of the edge + * {@code u -> v}. Capacities must be non-negative. Vertices are indexed in {@code [0, n)}. + * + *
Time complexity: O(V^3) in the worst case for the array-based variant; typically fast in + * practice. This implementation uses a residual network over an adjacency-matrix representation. + * + *
The API mirrors {@link EdmondsKarp#maxFlow(int[][], int, int)} and {@link Dinic#maxFlow(int[][], int, int)}.
+ *
+ * @see Wikipedia: Push–Relabel maximum flow algorithm
+ */
+public final class PushRelabel {
+
+ private PushRelabel() {
+ }
+
+ /**
+ * Computes the maximum flow from {@code source} to {@code sink} using Push–Relabel.
+ *
+ * @param capacity square capacity matrix (n x n); entries must be >= 0
+ * @param source source vertex index in [0, n)
+ * @param sink sink vertex index in [0, n)
+ * @return the maximum flow value
+ * @throws IllegalArgumentException if inputs are invalid
+ */
+ public static int maxFlow(int[][] capacity, int source, int sink) {
+ validate(capacity, source, sink);
+ final int n = capacity.length;
+ if (source == sink) {
+ return 0;
+ }
+
+ int[][] residual = new int[n][n];
+ for (int i = 0; i < n; i++) {
+ residual[i] = Arrays.copyOf(capacity[i], n);
+ }
+
+ int[] height = new int[n];
+ int[] excess = new int[n];
+ int[] nextNeighbor = new int[n];
+
+ // Preflow initialization
+ height[source] = n;
+ for (int v = 0; v < n; v++) {
+ int cap = residual[source][v];
+ if (cap > 0) {
+ residual[source][v] -= cap;
+ residual[v][source] += cap;
+ excess[v] += cap;
+ excess[source] -= cap;
+ }
+ }
+
+ // Active queue contains vertices (except source/sink) with positive excess
+ Queue Input is an adjacency matrix of edge weights. A value of -1 indicates no edge.
+ * All existing edge weights must be non-negative. Zero-weight edges are allowed. References:
+ * - Wikipedia: Yen's algorithm (https://en.wikipedia.org/wiki/Yen%27s_algorithm)
+ * - Dijkstra's algorithm for the base shortest path computation.
+ * This method is a "polynomial acceleration" method, meaning it finds the
+ * optimal polynomial to apply to the residual to accelerate convergence.
+ *
+ *
+ * It requires knowledge of the bounds of the eigenvalues of the matrix A:
+ * m(A) (smallest eigenvalue) and M(A) (largest eigenvalue).
+ *
+ *
+ * Wikipedia: https://en.wikipedia.org/wiki/Chebyshev_iteration
+ *
+ * @author Mitrajit Ghorui(KeyKyrios)
+ */
+public final class ChebyshevIteration {
+
+ private ChebyshevIteration() {
+ }
+
+ /**
+ * Solves the linear system Ax = b using the Chebyshev iteration method.
+ *
+ *
+ * NOTE: The matrix A *must* be symmetric positive-definite (SPD) for this
+ * algorithm to converge.
+ *
+ * @param a The matrix A (must be square, SPD).
+ * @param b The vector b.
+ * @param x0 The initial guess vector.
+ * @param minEigenvalue The smallest eigenvalue of A (m(A)).
+ * @param maxEigenvalue The largest eigenvalue of A (M(A)).
+ * @param maxIterations The maximum number of iterations to perform.
+ * @param tolerance The desired tolerance for the residual norm.
+ * @return The solution vector x.
+ * @throws IllegalArgumentException if matrix/vector dimensions are
+ * incompatible,
+ * if maxIterations <= 0, or if eigenvalues are invalid (e.g., minEigenvalue
+ * <= 0, maxEigenvalue <= minEigenvalue).
+ */
+ public static double[] solve(double[][] a, double[] b, double[] x0, double minEigenvalue, double maxEigenvalue, int maxIterations, double tolerance) {
+ validateInputs(a, b, x0, minEigenvalue, maxEigenvalue, maxIterations, tolerance);
+
+ int n = b.length;
+ double[] x = x0.clone();
+ double[] r = vectorSubtract(b, matrixVectorMultiply(a, x));
+ double[] p = new double[n];
+
+ double d = (maxEigenvalue + minEigenvalue) / 2.0;
+ double c = (maxEigenvalue - minEigenvalue) / 2.0;
+
+ double alpha = 0.0;
+ double alphaPrev = 0.0;
+
+ for (int k = 0; k < maxIterations; k++) {
+ double residualNorm = vectorNorm(r);
+ if (residualNorm < tolerance) {
+ return x; // Solution converged
+ }
+
+ if (k == 0) {
+ alpha = 1.0 / d;
+ System.arraycopy(r, 0, p, 0, n); // p = r
+ } else {
+ double beta = c * alphaPrev / 2.0 * (c * alphaPrev / 2.0);
+ alpha = 1.0 / (d - beta / alphaPrev);
+ double[] pUpdate = scalarMultiply(beta / alphaPrev, p);
+ p = vectorAdd(r, pUpdate); // p = r + (beta / alphaPrev) * p
+ }
+
+ double[] xUpdate = scalarMultiply(alpha, p);
+ x = vectorAdd(x, xUpdate); // x = x + alpha * p
+
+ // Recompute residual for accuracy
+ r = vectorSubtract(b, matrixVectorMultiply(a, x));
+ alphaPrev = alpha;
+ }
+
+ return x; // Return best guess after maxIterations
+ }
+
+ /**
+ * Validates the inputs for the Chebyshev solver.
+ */
+ private static void validateInputs(double[][] a, double[] b, double[] x0, double minEigenvalue, double maxEigenvalue, int maxIterations, double tolerance) {
+ int n = a.length;
+ if (n == 0) {
+ throw new IllegalArgumentException("Matrix A cannot be empty.");
+ }
+ if (n != a[0].length) {
+ throw new IllegalArgumentException("Matrix A must be square.");
+ }
+ if (n != b.length) {
+ throw new IllegalArgumentException("Matrix A and vector b dimensions do not match.");
+ }
+ if (n != x0.length) {
+ throw new IllegalArgumentException("Matrix A and vector x0 dimensions do not match.");
+ }
+ if (minEigenvalue <= 0) {
+ throw new IllegalArgumentException("Smallest eigenvalue must be positive (matrix must be positive-definite).");
+ }
+ if (maxEigenvalue <= minEigenvalue) {
+ throw new IllegalArgumentException("Max eigenvalue must be strictly greater than min eigenvalue.");
+ }
+ if (maxIterations <= 0) {
+ throw new IllegalArgumentException("Max iterations must be positive.");
+ }
+ if (tolerance <= 0) {
+ throw new IllegalArgumentException("Tolerance must be positive.");
+ }
+ }
+
+ // --- Vector/Matrix Helper Methods ---
+ /**
+ * Computes the product of a matrix A and a vector v (Av).
+ */
+ private static double[] matrixVectorMultiply(double[][] a, double[] v) {
+ int n = a.length;
+ double[] result = new double[n];
+ for (int i = 0; i < n; i++) {
+ double sum = 0;
+ for (int j = 0; j < n; j++) {
+ sum += a[i][j] * v[j];
+ }
+ result[i] = sum;
+ }
+ return result;
+ }
+
+ /**
+ * Computes the subtraction of two vectors (v1 - v2).
+ */
+ private static double[] vectorSubtract(double[] v1, double[] v2) {
+ int n = v1.length;
+ double[] result = new double[n];
+ for (int i = 0; i < n; i++) {
+ result[i] = v1[i] - v2[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes the addition of two vectors (v1 + v2).
+ */
+ private static double[] vectorAdd(double[] v1, double[] v2) {
+ int n = v1.length;
+ double[] result = new double[n];
+ for (int i = 0; i < n; i++) {
+ result[i] = v1[i] + v2[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes the product of a scalar and a vector (s * v).
+ */
+ private static double[] scalarMultiply(double scalar, double[] v) {
+ int n = v.length;
+ double[] result = new double[n];
+ for (int i = 0; i < n; i++) {
+ result[i] = scalar * v[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes the L2 norm (Euclidean norm) of a vector.
+ */
+ private static double vectorNorm(double[] v) {
+ double sumOfSquares = 0;
+ for (double val : v) {
+ sumOfSquares += val * val;
+ }
+ return Math.sqrt(sumOfSquares);
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/HarshadNumber.java b/src/main/java/com/thealgorithms/maths/HarshadNumber.java
index 5792e925a8aa..abe21cb045ae 100644
--- a/src/main/java/com/thealgorithms/maths/HarshadNumber.java
+++ b/src/main/java/com/thealgorithms/maths/HarshadNumber.java
@@ -1,49 +1,78 @@
package com.thealgorithms.maths;
-// Wikipedia for Harshad Number : https://en.wikipedia.org/wiki/Harshad_number
-
+/**
+ * A Harshad number (or Niven number) in a given number base is an integer that
+ * is divisible by the sum of its digits.
+ * For example, 18 is a Harshad number because 18 is divisible by (1 + 8) = 9.
+ * The name "Harshad" comes from the Sanskrit words "harṣa" (joy) and "da"
+ * (give), meaning "joy-giver".
+ *
+ * @author Hardvan
+ * @see Harshad Number -
+ * Wikipedia
+ */
public final class HarshadNumber {
private HarshadNumber() {
}
/**
- * A function to check if a number is Harshad number or not
+ * Checks if a number is a Harshad number.
+ * A Harshad number is a positive integer that is divisible by the sum of its
+ * digits.
*
- * @param n The number to be checked
- * @return {@code true} if {@code a} is Harshad number, otherwise
+ * @param n the number to be checked (must be positive)
+ * @return {@code true} if {@code n} is a Harshad number, otherwise
* {@code false}
+ * @throws IllegalArgumentException if {@code n} is less than or equal to 0
*/
public static boolean isHarshad(long n) {
if (n <= 0) {
- return false;
+ throw new IllegalArgumentException("Input must be a positive integer. Received: " + n);
}
- long t = n;
+ long temp = n;
long sumOfDigits = 0;
- while (t > 0) {
- sumOfDigits += t % 10;
- t /= 10;
+ while (temp > 0) {
+ sumOfDigits += temp % 10;
+ temp /= 10;
}
return n % sumOfDigits == 0;
}
/**
- * A function to check if a number is Harshad number or not
+ * Checks if a number represented as a string is a Harshad number.
+ * A Harshad number is a positive integer that is divisible by the sum of its
+ * digits.
*
- * @param s The number in String to be checked
- * @return {@code true} if {@code a} is Harshad number, otherwise
+ * @param s the string representation of the number to be checked
+ * @return {@code true} if the number is a Harshad number, otherwise
* {@code false}
+ * @throws IllegalArgumentException if {@code s} is null, empty, or represents a
+ * non-positive integer
+ * @throws NumberFormatException if {@code s} cannot be parsed as a long
*/
public static boolean isHarshad(String s) {
- final Long n = Long.valueOf(s);
+ if (s == null || s.isEmpty()) {
+ throw new IllegalArgumentException("Input string cannot be null or empty");
+ }
+
+ final long n;
+ try {
+ n = Long.parseLong(s);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Input string must be a valid integer: " + s, e);
+ }
+
if (n <= 0) {
- return false;
+ throw new IllegalArgumentException("Input must be a positive integer. Received: " + n);
}
int sumOfDigits = 0;
for (char ch : s.toCharArray()) {
- sumOfDigits += ch - '0';
+ if (Character.isDigit(ch)) {
+ sumOfDigits += ch - '0';
+ }
}
return n % sumOfDigits == 0;
diff --git a/src/main/java/com/thealgorithms/maths/HeronsFormula.java b/src/main/java/com/thealgorithms/maths/HeronsFormula.java
index 5baee715d1ec..10438e2888b9 100644
--- a/src/main/java/com/thealgorithms/maths/HeronsFormula.java
+++ b/src/main/java/com/thealgorithms/maths/HeronsFormula.java
@@ -1,33 +1,76 @@
package com.thealgorithms.maths;
/**
- * Wikipedia for HeronsFormula => https://en.wikipedia.org/wiki/Heron%27s_formula
- * Find the area of a triangle using only side lengths
+ * Heron's Formula implementation for calculating the area of a triangle given
+ * its three side lengths.
+ *
+ * Heron's Formula states that the area of a triangle whose sides have lengths
+ * a, b, and c is:
+ * Area = √(s(s - a)(s - b)(s - c))
+ * where s is the semi-perimeter of the triangle: s = (a + b + c) / 2
+ *
+ * The triangle inequality theorem states that the sum of any two sides
+ * of a triangle must be greater than the third side.
+ *
+ * Given three side lengths a, b, and c, the area is computed as:
+ * Area = √(s(s - a)(s - b)(s - c))
+ * where s is the semi-perimeter: s = (a + b + c) / 2
+ *
+ * A Kaprekar number is a positive integer with the following property:
+ * If you square it, then split the resulting number into two parts (right part
+ * has same number of
+ * digits as the original number, left part has the remaining digits), and
+ * finally add the two
+ * parts together, you get the original number.
+ *
+ * For example:
+ *
+ * Note: The right part can have leading zeros, but must not be all zeros.
+ *
+ * @see Kaprekar Number
+ * - Wikipedia
+ * @author TheAlgorithms (https://github.com/TheAlgorithms)
+ */
public final class KaprekarNumbers {
private KaprekarNumbers() {
}
- /* This program demonstrates if a given number is Kaprekar Number or not.
- Kaprekar Number: A Kaprekar number is an n-digit number which its square can be split into
- two parts where the right part has n digits and sum of these parts is equal to the original
- number. */
-
- // Provides a list of kaprekarNumber in a range
- public static List
+ * The algorithm works as follows:
+ *
+ * Special handling is required for numbers whose squares contain zeros.
+ *
+ * @param num the number to check
+ * @return true if the number is a Kaprekar number, false otherwise
+ * @throws IllegalArgumentException if num is negative
+ */
public static boolean isKaprekarNumber(long num) {
+ if (num < 0) {
+ throw new IllegalArgumentException("Number must be non-negative. Given: " + num);
+ }
+
+ if (num == 0 || num == 1) {
+ return true;
+ }
+
String number = Long.toString(num);
BigInteger originalNumber = BigInteger.valueOf(num);
BigInteger numberSquared = originalNumber.multiply(originalNumber);
- if (number.length() == numberSquared.toString().length()) {
- return number.equals(numberSquared.toString());
- } else {
- BigInteger leftDigits1 = BigInteger.ZERO;
- BigInteger leftDigits2;
- if (numberSquared.toString().contains("0")) {
- leftDigits1 = new BigInteger(numberSquared.toString().substring(0, numberSquared.toString().indexOf("0")));
- }
- leftDigits2 = new BigInteger(numberSquared.toString().substring(0, (numberSquared.toString().length() - number.length())));
- BigInteger rightDigits = new BigInteger(numberSquared.toString().substring(numberSquared.toString().length() - number.length()));
- String x = leftDigits1.add(rightDigits).toString();
- String y = leftDigits2.add(rightDigits).toString();
- return (number.equals(x)) || (number.equals(y));
+ String squaredStr = numberSquared.toString();
+
+ // Special case: if the squared number has the same length as the original
+ if (number.length() == squaredStr.length()) {
+ return number.equals(squaredStr);
+ }
+
+ // Calculate the split position
+ int splitPos = squaredStr.length() - number.length();
+
+ // Split the squared number into left and right parts
+ String leftPart = squaredStr.substring(0, splitPos);
+ String rightPart = squaredStr.substring(splitPos);
+
+ // Parse the parts as BigInteger (handles empty left part as zero)
+ BigInteger leftNum = leftPart.isEmpty() ? BigInteger.ZERO : new BigInteger(leftPart);
+ BigInteger rightNum = new BigInteger(rightPart);
+
+ // Check if right part is all zeros (invalid for Kaprekar numbers except 1)
+ if (rightNum.equals(BigInteger.ZERO)) {
+ return false;
}
+
+ // Check if the sum equals the original number
+ return leftNum.add(rightNum).equals(originalNumber);
}
}
diff --git a/src/main/java/com/thealgorithms/maths/KeithNumber.java b/src/main/java/com/thealgorithms/maths/KeithNumber.java
index 1756cfbae91b..eb7f800d378f 100644
--- a/src/main/java/com/thealgorithms/maths/KeithNumber.java
+++ b/src/main/java/com/thealgorithms/maths/KeithNumber.java
@@ -4,57 +4,98 @@
import java.util.Collections;
import java.util.Scanner;
-final class KeithNumber {
+/**
+ * A Keith number is an n-digit positive integer where the sequence formed by
+ * starting with its digits and repeatedly adding the previous n terms,
+ * eventually reaches the number itself.
+ *
+ *
+ * For example:
+ *
+ * The algorithm works as follows:
+ *
+ * A Krishnamurthy number (also known as a Strong number or Factorion) is a
+ * number
+ * whose sum of the factorials of its digits is equal to the number itself.
+ *
+ * For example, 145 is a Krishnamurthy number because 1! + 4! + 5! = 1 + 24 +
+ * 120 = 145.
+ *
+ * The only Krishnamurthy numbers in base 10 are: 1, 2, 145, and 40585.
+ *
* Example usage:
+ *
+ * A number is a Krishnamurthy number if the sum of the factorials of its digits
+ * equals the number itself.
+ *
+ * Leonardo numbers are a sequence of numbers defined by the recurrence:
+ * L(n) = L(n-1) + L(n-2) + 1, with L(0) = 1 and L(1) = 1
+ *
+ * The sequence begins: 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...
+ *
+ * This class provides both a recursive implementation and an optimized
+ * iterative
+ * implementation for calculating Leonardo numbers.
+ *
+ * @see Leonardo Number
+ * - Wikipedia
+ * @see OEIS A001595
*/
public final class LeonardoNumber {
private LeonardoNumber() {
}
/**
- * Calculate nth Leonardo Number (1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...)
+ * Calculates the nth Leonardo Number using recursion.
+ *
+ * Time Complexity: O(2^n) - exponential due to repeated calculations
+ * Space Complexity: O(n) - due to recursion stack
+ *
+ * Note: This method is not recommended for large values of n due to exponential
+ * time complexity.
+ * Consider using {@link #leonardoNumberIterative(int)} for better performance.
*
- * @param n the index of Leonardo Number to calculate
- * @return nth number of Leonardo sequences
+ * @param n the index of the Leonardo Number to calculate (must be non-negative)
+ * @return the nth Leonardo Number
+ * @throws IllegalArgumentException if n is negative
*/
public static int leonardoNumber(int n) {
if (n < 0) {
- throw new ArithmeticException();
+ throw new IllegalArgumentException("Input must be non-negative. Received: " + n);
}
if (n == 0 || n == 1) {
return 1;
}
- return (leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1);
+ return leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1;
+ }
+
+ /**
+ * Calculates the nth Leonardo Number using an iterative approach.
+ *
+ * This method provides better performance than the recursive version for large
+ * values of n.
+ *
+ * Time Complexity: O(n)
+ * Space Complexity: O(1)
+ *
+ * @param n the index of the Leonardo Number to calculate (must be non-negative)
+ * @return the nth Leonardo Number
+ * @throws IllegalArgumentException if n is negative
+ */
+ public static int leonardoNumberIterative(int n) {
+ if (n < 0) {
+ throw new IllegalArgumentException("Input must be non-negative. Received: " + n);
+ }
+ if (n == 0 || n == 1) {
+ return 1;
+ }
+
+ int previous = 1;
+ int current = 1;
+
+ for (int i = 2; i <= n; i++) {
+ int next = current + previous + 1;
+ previous = current;
+ current = next;
+ }
+
+ return current;
}
}
diff --git a/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java b/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java
index a50cfb218283..a95e7053e210 100644
--- a/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java
+++ b/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java
@@ -2,20 +2,77 @@
import java.util.Objects;
+/**
+ * A solver for linear Diophantine equations of the form ax + by = c.
+ *
+ * A linear Diophantine equation is an equation in which only integer solutions
+ * are allowed.
+ * This solver uses the Extended Euclidean Algorithm to find integer solutions
+ * (x, y)
+ * for equations of the form ax + by = c, where a, b, and c are integers.
+ *
+ * The equation has solutions if and only if gcd(a, b) divides c.
+ * If solutions exist, this solver finds one particular solution.
+ *
+ * The method returns one of three types of solutions:
+ *
+ * This method also finds coefficients x and y such that ax + by = gcd(a, b).
+ * The coefficients are stored in the 'previous' wrapper object.
+ *
+ * Special instances:
+ * > kShortestPaths(int[][] weights, int src, int dst, int k) {
+ validate(weights, src, dst, k);
+ final int n = weights.length;
+ // Make a defensive copy to avoid mutating caller's matrix
+ int[][] weightsCopy = new int[n][n];
+ for (int i = 0; i < n; i++) {
+ weightsCopy[i] = Arrays.copyOf(weights[i], n);
+ }
+
+ List
> result = new ArrayList<>(shortestPaths.size());
+ for (Path p : shortestPaths) {
+ result.add(new ArrayList<>(p.nodes));
+ }
+ return result;
+ }
+
+ private static void validate(int[][] weights, int src, int dst, int k) {
+ if (weights == null || weights.length == 0) {
+ throw new IllegalArgumentException("Weights matrix must not be null or empty");
+ }
+ int n = weights.length;
+ for (int i = 0; i < n; i++) {
+ if (weights[i] == null || weights[i].length != n) {
+ throw new IllegalArgumentException("Weights matrix must be square");
+ }
+ for (int j = 0; j < n; j++) {
+ int val = weights[i][j];
+ if (val < NO_EDGE) {
+ throw new IllegalArgumentException("Weights must be -1 (no edge) or >= 0");
+ }
+ }
+ }
+ if (src < 0 || dst < 0 || src >= n || dst >= n) {
+ throw new IllegalArgumentException("Invalid src/dst indices");
+ }
+ if (k < 1) {
+ throw new IllegalArgumentException("k must be >= 1");
+ }
+ }
+
+ private static boolean startsWith(List
+ *
+ *
+ *
+ *
+ *
+ *
+ * @see Keith Number -
+ * Wikipedia
+ * @see Keith Number -
+ * MathWorld
+ */
+public final class KeithNumber {
private KeithNumber() {
}
- // user-defined function that checks if the given number is Keith or not
- static boolean isKeith(int x) {
- // List stores all the digits of the X
+ /**
+ * Checks if a given number is a Keith number.
+ *
+ *
+ *
+ *
+ * @param number the number to check (must be positive)
+ * @return {@code true} if the number is a Keith number, {@code false} otherwise
+ * @throws IllegalArgumentException if the number is not positive
+ */
+ public static boolean isKeith(int number) {
+ if (number <= 0) {
+ throw new IllegalArgumentException("Number must be positive");
+ }
+
+ // Extract digits and store them in the list
ArrayList
* boolean isKrishnamurthy = KrishnamurthyNumber.isKrishnamurthy(145);
* System.out.println(isKrishnamurthy); // Output: true
@@ -14,40 +29,43 @@
* isKrishnamurthy = KrishnamurthyNumber.isKrishnamurthy(123);
* System.out.println(isKrishnamurthy); // Output: false
*
+ *
+ * @see Factorion
+ * (Wikipedia)
*/
public final class KrishnamurthyNumber {
+ // Pre-computed factorials for digits 0-9 to improve performance
+ private static final int[] FACTORIALS = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
+
private KrishnamurthyNumber() {
}
/**
* Checks if a number is a Krishnamurthy number.
*
- * @param n The number to check
+ *
+ *
+ *
+ *
+ *
+ * This class is used internally to pass results between recursive calls + * of the GCD computation. + *
+ */ public static final class GcdSolutionWrapper { private int gcd; private Solution solution; + /** + * Constructs a GcdSolutionWrapper with the given GCD and solution. + * + * @param gcd the greatest common divisor + * @param solution the solution coefficients + */ public GcdSolutionWrapper(int gcd, Solution solution) { this.gcd = gcd; this.solution = solution; @@ -120,18 +255,38 @@ public boolean equals(Object obj) { return (this.gcd == that.gcd && Objects.equals(this.solution, that.solution)); } + /** + * Gets the GCD value. + * + * @return the GCD + */ public int getGcd() { return gcd; } + /** + * Sets the GCD value. + * + * @param gcd the new GCD value + */ public void setGcd(int gcd) { this.gcd = gcd; } + /** + * Gets the solution coefficients. + * + * @return the solution + */ public Solution getSolution() { return solution; } + /** + * Sets the solution coefficients. + * + * @param solution the new solution + */ public void setSolution(Solution solution) { this.solution = solution; } diff --git a/src/main/java/com/thealgorithms/maths/LucasSeries.java b/src/main/java/com/thealgorithms/maths/LucasSeries.java index e277c511f317..90a35f0d6259 100644 --- a/src/main/java/com/thealgorithms/maths/LucasSeries.java +++ b/src/main/java/com/thealgorithms/maths/LucasSeries.java @@ -1,38 +1,69 @@ package com.thealgorithms.maths; /** - * https://en.wikipedia.org/wiki/Lucas_number + * Utility class for calculating Lucas numbers. + * The Lucas sequence is similar to the Fibonacci sequence but starts with 2 and + * 1. + * The sequence follows: L(n) = L(n-1) + L(n-2) + * Starting values: L(1) = 2, L(2) = 1 + * Sequence: 2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123, ... + * + * @see Lucas Number + * @author TheAlgorithms Contributors */ public final class LucasSeries { private LucasSeries() { } /** - * Calculate nth number of Lucas Series(2, 1, 3, 4, 7, 11, 18, 29, 47, 76, - * 123, ....) using recursion + * Calculate the nth Lucas number using recursion. + * Time Complexity: O(2^n) - exponential due to recursive calls + * Space Complexity: O(n) - recursion depth * - * @param n nth - * @return nth number of Lucas Series + * @param n the position in the Lucas sequence (1-indexed, must be positive) + * @return the nth Lucas number + * @throws IllegalArgumentException if n is less than 1 */ public static int lucasSeries(int n) { - return n == 1 ? 2 : n == 2 ? 1 : lucasSeries(n - 1) + lucasSeries(n - 2); + if (n < 1) { + throw new IllegalArgumentException("Input must be a positive integer. Provided: " + n); + } + if (n == 1) { + return 2; + } + if (n == 2) { + return 1; + } + return lucasSeries(n - 1) + lucasSeries(n - 2); } /** - * Calculate nth number of Lucas Series(2, 1, 3, 4, 7, 11, 18, 29, 47, 76, - * 123, ....) using iteration + * Calculate the nth Lucas number using iteration. + * Time Complexity: O(n) - single loop through n iterations + * Space Complexity: O(1) - constant space usage * - * @param n nth - * @return nth number of lucas series + * @param n the position in the Lucas sequence (1-indexed, must be positive) + * @return the nth Lucas number + * @throws IllegalArgumentException if n is less than 1 */ public static int lucasSeriesIteration(int n) { + if (n < 1) { + throw new IllegalArgumentException("Input must be a positive integer. Provided: " + n); + } + if (n == 1) { + return 2; + } + if (n == 2) { + return 1; + } + int previous = 2; int current = 1; - for (int i = 1; i < n; i++) { + for (int i = 2; i < n; i++) { int next = previous + current; previous = current; current = next; } - return previous; + return current; } } diff --git a/src/main/java/com/thealgorithms/maths/Means.java b/src/main/java/com/thealgorithms/maths/Means.java index dccc820b172e..5445a3caebc7 100644 --- a/src/main/java/com/thealgorithms/maths/Means.java +++ b/src/main/java/com/thealgorithms/maths/Means.java @@ -4,9 +4,27 @@ import org.apache.commons.collections4.IterableUtils; /** - * https://en.wikipedia.org/wiki/Mean + * Utility class for computing various types of statistical means. *- * by: Punit Patel + * This class provides static methods to calculate different types of means + * (averages) + * from a collection of numbers. All methods accept any {@link Iterable} + * collection of + * {@link Double} values and return the computed mean as a {@link Double}. + *
+ * + *+ * Supported means: + *
+ * The arithmetic mean is calculated as: (x₁ + x₂ + ... + xₙ) / n + *
+ *+ * Example: For numbers [2, 4, 6], the arithmetic mean is (2+4+6)/3 = 4.0 + *
+ * + * @param numbers the input numbers (must not be empty) * @return the arithmetic mean of the input numbers + * @throws IllegalArgumentException if the input is empty + * @see Arithmetic + * Mean */ public static Double arithmetic(final Iterable+ * The geometric mean is calculated as: ⁿ√(x₁ × x₂ × ... × xₙ) + *
+ *+ * Example: For numbers [2, 8], the geometric mean is √(2×8) = √16 = 4.0 + *
+ *+ * Note: This method may produce unexpected results for negative numbers, + * as it computes the real-valued nth root which may not exist for negative + * products. + *
+ * + * @param numbers the input numbers (must not be empty) * @return the geometric mean of the input numbers + * @throws IllegalArgumentException if the input is empty + * @see Geometric + * Mean */ public static Double geometric(final Iterable+ * The harmonic mean is calculated as: n / (1/x₁ + 1/x₂ + ... + 1/xₙ) + *
+ *+ * Example: For numbers [1, 2, 4], the harmonic mean is 3/(1/1 + 1/2 + 1/4) = + * 3/1.75 ≈ 1.714 + *
+ *+ * Note: This method will produce unexpected results if any input number is + * zero, + * as it involves computing reciprocals. + *
+ * + * @param numbers the input numbers (must not be empty) * @return the harmonic mean of the input numbers + * @throws IllegalArgumentException if the input is empty + * @see Harmonic Mean */ public static Double harmonic(final Iterable+ * Time Complexity: O(n log n) due to sorting + *
+ * Space Complexity: O(1) if sorting is done in-place + * + * @see Median (Wikipedia) + * @see Mean, + * Median, and Mode Review */ public final class Median { private Median() { } /** - * Calculate average median - * @param values sorted numbers to find median of - * @return median of given {@code values} - * @throws IllegalArgumentException If the input array is empty or null. + * Calculates the median of an array of integers. + * The array is sorted internally, so the original order is not preserved. + * For arrays with an odd number of elements, returns the middle element. + * For arrays with an even number of elements, returns the average of the two + * middle elements. + * + * @param values the array of integers to find the median of (can be unsorted) + * @return the median value as a double + * @throws IllegalArgumentException if the input array is empty or null */ public static double median(int[] values) { if (values == null || values.length == 0) { @@ -22,6 +40,10 @@ public static double median(int[] values) { Arrays.sort(values); int length = values.length; - return length % 2 == 0 ? (values[length / 2] + values[length / 2 - 1]) / 2.0 : values[length / 2]; + if (length % 2 == 0) { + return (values[length / 2] + values[length / 2 - 1]) / 2.0; + } else { + return values[length / 2]; + } } } diff --git a/src/main/java/com/thealgorithms/maths/Neville.java b/src/main/java/com/thealgorithms/maths/Neville.java new file mode 100644 index 000000000000..ca45f2e8a042 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Neville.java @@ -0,0 +1,61 @@ +package com.thealgorithms.maths; + +import java.util.HashSet; +import java.util.Set; + +/** + * In numerical analysis, Neville's algorithm is an algorithm used for + * polynomial interpolation. Given n+1 points, there is a unique polynomial of + * degree at most n that passes through all the points. Neville's algorithm + * computes the value of this polynomial at a given point. + * + *
+ * Wikipedia: https://en.wikipedia.org/wiki/Neville%27s_algorithm
+ *
+ * @author Mitrajit Ghorui(KeyKyrios)
+ */
+public final class Neville {
+
+ private Neville() {
+ }
+
+ /**
+ * Evaluates the polynomial that passes through the given points at a
+ * specific x-coordinate.
+ *
+ * @param x The x-coordinates of the points. Must be the same length as y.
+ * @param y The y-coordinates of the points. Must be the same length as x.
+ * @param target The x-coordinate at which to evaluate the polynomial.
+ * @return The interpolated y-value at the target x-coordinate.
+ * @throws IllegalArgumentException if the lengths of x and y arrays are
+ * different, if the arrays are empty, or if x-coordinates are not unique.
+ */
+ public static double interpolate(double[] x, double[] y, double target) {
+ if (x.length != y.length) {
+ throw new IllegalArgumentException("x and y arrays must have the same length.");
+ }
+ if (x.length == 0) {
+ throw new IllegalArgumentException("Input arrays cannot be empty.");
+ }
+
+ // Check for duplicate x-coordinates to prevent division by zero
+ Set
+ * Given a 2D array (matrix), this class provides a method to return the
+ * elements
+ * of the matrix in spiral order, starting from the top-left corner and moving
+ * clockwise.
+ *
+ * Example:
+ *
+ *
+ * int[][] matrix = {
+ * {1, 2, 3},
+ * {4, 5, 6},
+ * {7, 8, 9}
+ * };
+ * print(matrix, 3, 3) returns [1, 2, 3, 6, 9, 8, 7, 4, 5]
+ *
+ *
+ * Time Complexity: O(n log n) where n is the number of unique characters
+ * Space Complexity: O(n)
+ *
+ * @see Huffman
+ * Coding
+ */
public final class Huffman {
private Huffman() {
}
- // recursive function to print the
- // huffman-code through the tree traversal.
- // Here s is the huffman - code generated.
- public static void printCode(HuffmanNode root, String s) {
- // base case; if the left and right are null
- // then its a leaf node and we print
- // the code s generated by traversing the tree.
- if (root.left == null && root.right == null && Character.isLetter(root.c)) {
- // c is the character in the node
- System.out.println(root.c + ":" + s);
-
- return;
+ /**
+ * Builds a Huffman tree from the given character array and their frequencies.
+ *
+ * @param charArray array of characters
+ * @param charFreq array of frequencies corresponding to the characters
+ * @return root node of the Huffman tree
+ * @throws IllegalArgumentException if arrays are null, empty, or have different
+ * lengths
+ */
+ public static HuffmanNode buildHuffmanTree(char[] charArray, int[] charFreq) {
+ if (charArray == null || charFreq == null) {
+ throw new IllegalArgumentException("Character array and frequency array cannot be null");
+ }
+ if (charArray.length == 0 || charFreq.length == 0) {
+ throw new IllegalArgumentException("Character array and frequency array cannot be empty");
+ }
+ if (charArray.length != charFreq.length) {
+ throw new IllegalArgumentException("Character array and frequency array must have the same length");
}
- // if we go to left then add "0" to the code.
- // if we go to the right add"1" to the code.
- // recursive calls for left and
- // right sub-tree of the generated tree.
- printCode(root.left, s + "0");
- printCode(root.right, s + "1");
- }
+ int n = charArray.length;
+ PriorityQueue
+ * This class demonstrates how to insert an element at a specific position and
+ * delete an element from a specific position in an integer array. Since arrays
+ * in Java have fixed size, insertion creates a new array with increased size,
+ * and deletion shifts elements to fill the gap.
+ *
+ * Time Complexity:
+ *
+ * Space Complexity:
+ *
+ * Creates a new array with size = original array size + 1.
+ * Elements at positions <= insertPos retain their positions,
+ * while elements at positions > insertPos are shifted right by one position.
+ *
+ * Creates a new array with size = original array size - 1.
+ * Elements after the deletion position are shifted left by one position.
+ *
+ * This method interactively:
+ *
+ *
+ *
+ *
+ *
+ *
+ * @author TheAlgorithms community
+ * @see Array
+ * Data Structure
+ */
public final class InsertDeleteInArray {
private InsertDeleteInArray() {
}
+ /**
+ * Inserts an element at the specified position in the array.
+ *
+ *
+ *
+ * A number is a palindrome in a given base if its representation in that base + * reads the same + * forwards and backwards. For example, 15 in base 2 is 1111, which is + * palindromic. + * This class provides methods to check palindromic properties and find the + * smallest base + * where a number becomes palindromic. + *
+ *+ * Example: The number 15 in base 2 is represented as [1,1,1,1], which is + * palindromic. + * The number 10 in base 3 is represented as [1,0,1], which is also palindromic. + *
+ * + * @see OEIS A016026 - Smallest base in which + * n is palindromic + * @author TheAlgorithms Contributors */ public final class LowestBasePalindrome { private LowestBasePalindrome() { @@ -37,12 +55,18 @@ private static void checkNumber(int number) { /** * Computes the digits of a given number in a specified base. + *+ * The digits are returned in reverse order (least significant digit first). + * For example, the number 13 in base 2 produces [1,0,1,1] representing 1101 in + * binary. + *
* - * @param number the number to be converted - * @param base the base to be used for the conversion - * @return a list of digits representing the number in the given base, with the most - * significant digit at the end of the list - * @throws IllegalArgumentException if the number is negative or the base is less than 2 + * @param number the number to be converted (must be non-negative) + * @param base the base to be used for the conversion (must be greater than 1) + * @return a list of digits representing the number in the given base, with the + * least significant digit at the beginning of the list + * @throws IllegalArgumentException if the number is negative or the base is + * less than 2 */ public static List+ * A list is palindromic if it reads the same forwards and backwards. + * For example, [1,2,1] is palindromic, but [1,2,3] is not. + *
* * @param list the list of integers to be checked * @return {@code true} if the list is a palindrome, {@code false} otherwise @@ -73,12 +101,29 @@ public static boolean isPalindromic(List+ * This method first validates the input, then applies optimization: if the + * number + * ends with 0 in the given base (i.e., divisible by the base), it cannot be + * palindromic + * as palindromes cannot start with 0. + *
+ *+ * Examples: + * - 101 in base 10 is palindromic (101) + * - 15 in base 2 is palindromic (1111) + * - 10 in base 3 is palindromic (101) + *
* - * @param number the number to be checked - * @param base the base in which the number will be represented - * @return {@code true} if the number is palindromic in the specified base, {@code false} otherwise - * @throws IllegalArgumentException if the number is negative or the base is less than 2 + * @param number the number to be checked (must be non-negative) + * @param base the base in which the number will be represented (must be + * greater than 1) + * @return {@code true} if the number is palindromic in the specified base, + * {@code false} otherwise + * @throws IllegalArgumentException if the number is negative or the base is + * less than 2 */ public static boolean isPalindromicInBase(int number, int base) { checkNumber(number); @@ -89,7 +134,8 @@ public static boolean isPalindromicInBase(int number, int base) { } if (number % base == 0) { - // If the last digit of the number in the given base is 0, it can't be palindromic + // If the last digit of the number in the given base is 0, it can't be + // palindromic return false; } @@ -97,10 +143,29 @@ public static boolean isPalindromicInBase(int number, int base) { } /** - * Finds the smallest base in which the representation of a given number is palindromic. + * Finds the smallest base in which the representation of a given number is + * palindromic. + *+ * This method iteratively checks bases starting from 2 until it finds one where + * the number is palindromic. For any number n ≥ 2, the number is always + * palindromic + * in base n-1 (represented as [1, 1]), so this algorithm is guaranteed to + * terminate. + *
+ *+ * Time Complexity: O(n * log(n)) in the worst case, where we check each base + * and + * convert the number to that base. + *
+ *+ * Examples: + * - lowestBasePalindrome(15) returns 2 (15 in base 2 is 1111) + * - lowestBasePalindrome(10) returns 3 (10 in base 3 is 101) + * - lowestBasePalindrome(11) returns 10 (11 in base 10 is 11) + *
* - * @param number the number to be checked - * @return the smallest base in which the number is a palindrome + * @param number the number to be checked (must be non-negative) + * @return the smallest base in which the number is a palindrome (base ≥ 2) * @throws IllegalArgumentException if the number is negative */ public static int lowestBasePalindrome(int number) { diff --git a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java index c05f1af4e327..dec813dd3213 100644 --- a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java +++ b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java @@ -1,14 +1,26 @@ package com.thealgorithms.others; -import java.util.HashSet; -import java.util.Set; +import java.util.HashMap; +import java.util.Map; /** - * References: https://en.wikipedia.org/wiki/Streaming_algorithm + * Algorithm to find the maximum sum of a subarray of size K with all distinct + * elements. * - * This model involves computing the maximum sum of subarrays of a fixed size \( K \) from a stream of integers. - * As the stream progresses, elements from the end of the window are removed, and new elements from the stream are added. + * This implementation uses a sliding window approach with a hash map to + * efficiently + * track element frequencies within the current window. The algorithm maintains + * a window + * of size K and slides it across the array, ensuring all elements in the window + * are distinct. * + * Time Complexity: O(n) where n is the length of the input array + * Space Complexity: O(k) for storing elements in the hash map + * + * @see Streaming + * Algorithm + * @see Sliding + * Window * @author Swarga-codes (https://github.com/Swarga-codes) */ public final class MaximumSumOfDistinctSubarraysWithLengthK { @@ -16,54 +28,62 @@ private MaximumSumOfDistinctSubarraysWithLengthK() { } /** - * Finds the maximum sum of a subarray of size K consisting of distinct elements. + * Finds the maximum sum of a subarray of size K consisting of distinct + * elements. * - * @param k The size of the subarray. - * @param nums The array from which subarrays will be considered. + * The algorithm uses a sliding window technique with a frequency map to track + * the count of each element in the current window. A window is valid only if + * all K elements are distinct (frequency map size equals K). * - * @return The maximum sum of any distinct-element subarray of size K. If no such subarray exists, returns 0. + * @param k The size of the subarray. Must be non-negative. + * @param nums The array from which subarrays will be considered. + * @return The maximum sum of any distinct-element subarray of size K. + * Returns 0 if no such subarray exists or if k is 0 or negative. + * @throws IllegalArgumentException if k is negative */ public static long maximumSubarraySum(int k, int... nums) { - if (nums.length < k) { + if (k <= 0 || nums == null || nums.length < k) { return 0; } - long masSum = 0; // Variable to store the maximum sum of distinct subarrays - long currentSum = 0; // Variable to store the sum of the current subarray - Set+ * The algorithm simulates all possible moves in a game tree and chooses the + * move that minimizes the maximum possible loss. The algorithm assumes both + * players play optimally. + * + *
+ * Time Complexity: O(b^d) where b is the branching factor and d is the depth + *
+ * Space Complexity: O(d) for the recursive call stack + * + *
+ * See more: + *
* * @author aitofi (https://github.com/aitorfi) */ -public class MiniMaxAlgorithm { +public final class MiniMaxAlgorithm { + + private static final Random RANDOM = new Random(); /** * Game tree represented as an int array containing scores. Each array - * element is a leaf node. + * element is a leaf node. The array length must be a power of 2. */ private int[] scores; + + /** + * The height of the game tree, calculated as log2(scores.length). + */ private int height; /** - * Initializes the scores with 8 random leaf nodes + * Initializes the MiniMaxAlgorithm with 8 random leaf nodes (2^3 = 8). + * Each score is a random integer between 1 and 99 inclusive. */ public MiniMaxAlgorithm() { - scores = getRandomScores(3, 99); - height = log2(scores.length); + this(getRandomScores(3, 99)); + } + + /** + * Initializes the MiniMaxAlgorithm with the provided scores. + * + * @param scores An array of scores representing leaf nodes. The length must be + * a power of 2. + * @throws IllegalArgumentException if the scores array length is not a power of + * 2 + */ + public MiniMaxAlgorithm(int[] scores) { + if (!isPowerOfTwo(scores.length)) { + throw new IllegalArgumentException("The number of scores must be a power of 2."); + } + this.scores = Arrays.copyOf(scores, scores.length); + this.height = log2(scores.length); } + /** + * Demonstrates the MiniMax algorithm with a random game tree. + * + * @param args Command line arguments (not used) + */ public static void main(String[] args) { - MiniMaxAlgorithm miniMaxAlgorith = new MiniMaxAlgorithm(); + MiniMaxAlgorithm miniMaxAlgorithm = new MiniMaxAlgorithm(); boolean isMaximizer = true; // Specifies the player that goes first. - boolean verbose = true; // True to show each players choices. int bestScore; - bestScore = miniMaxAlgorith.miniMax(0, isMaximizer, 0, verbose); + bestScore = miniMaxAlgorithm.miniMax(0, isMaximizer, 0, true); - if (verbose) { - System.out.println(); - } - - System.out.println(Arrays.toString(miniMaxAlgorith.getScores())); + System.out.println(); + System.out.println(Arrays.toString(miniMaxAlgorithm.getScores())); System.out.println("The best score for " + (isMaximizer ? "Maximizer" : "Minimizer") + " is " + bestScore); } /** * Returns the optimal score assuming that both players play their best. * - * @param depth Indicates how deep we are into the game tree. - * @param isMaximizer True if it is maximizers turn; otherwise false. - * @param index Index of the leaf node that is being evaluated. - * @param verbose True to show each players choices. + *+ * This method recursively evaluates the game tree using the minimax algorithm. + * At each level, the maximizer tries to maximize the score while the minimizer + * tries to minimize it. + * + * @param depth The current depth in the game tree (0 at root). + * @param isMaximizer True if it is the maximizer's turn; false for minimizer. + * @param index Index of the current node in the game tree. + * @param verbose True to print each player's choice during evaluation. * @return The optimal score for the player that made the first move. */ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) { @@ -75,7 +120,7 @@ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) { } // Leaf nodes can be sequentially inspected by - // recurssively multiplying (0 * 2) and ((0 * 2) + 1): + // recursively multiplying (0 * 2) and ((0 * 2) + 1): // (0 x 2) = 0; ((0 x 2) + 1) = 1 // (1 x 2) = 2; ((1 x 2) + 1) = 3 // (2 x 2) = 4; ((2 x 2) + 1) = 5 ... @@ -87,46 +132,73 @@ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) { } /** - * Returns an array of random numbers which lenght is a power of 2. + * Returns an array of random numbers whose length is a power of 2. * - * @param size The power of 2 that will determine the lenght of the array. - * @param maxScore The maximum possible score. - * @return An array of random numbers. + * @param size The power of 2 that will determine the length of the array + * (array length = 2^size). + * @param maxScore The maximum possible score (scores will be between 1 and + * maxScore inclusive). + * @return An array of random numbers with length 2^size. */ public static int[] getRandomScores(int size, int maxScore) { int[] randomScores = new int[(int) Math.pow(2, size)]; - Random rand = new Random(); for (int i = 0; i < randomScores.length; i++) { - randomScores[i] = rand.nextInt(maxScore) + 1; + randomScores[i] = RANDOM.nextInt(maxScore) + 1; } return randomScores; } - // A utility function to find Log n in base 2 + /** + * Calculates the logarithm base 2 of a number. + * + * @param n The number to calculate log2 for (must be a power of 2). + * @return The log2 of n. + */ private int log2(int n) { return (n == 1) ? 0 : log2(n / 2) + 1; } - // A utility function to check if a number is a power of 2 + /** + * Checks if a number is a power of 2. + * + * @param n The number to check. + * @return True if n is a power of 2, false otherwise. + */ private boolean isPowerOfTwo(int n) { return n > 0 && (n & (n - 1)) == 0; } + /** + * Sets the scores array for the game tree. + * + * @param scores The array of scores. Length must be a power of 2. + * @throws IllegalArgumentException if the scores array length is not a power of + * 2 + */ public void setScores(int[] scores) { if (!isPowerOfTwo(scores.length)) { - System.out.println("The number of scores must be a power of 2."); - return; + throw new IllegalArgumentException("The number of scores must be a power of 2."); } - this.scores = scores; + this.scores = Arrays.copyOf(scores, scores.length); height = log2(this.scores.length); } + /** + * Returns a copy of the scores array. + * + * @return A copy of the scores array. + */ public int[] getScores() { - return scores; + return Arrays.copyOf(scores, scores.length); } + /** + * Returns the height of the game tree. + * + * @return The height of the game tree (log2 of the number of leaf nodes). + */ public int getHeight() { return height; } diff --git a/src/main/java/com/thealgorithms/others/PageRank.java b/src/main/java/com/thealgorithms/others/PageRank.java index c7be7a9882bc..2899b80bcee8 100644 --- a/src/main/java/com/thealgorithms/others/PageRank.java +++ b/src/main/java/com/thealgorithms/others/PageRank.java @@ -2,94 +2,306 @@ import java.util.Scanner; -class PageRank { +/** + * PageRank Algorithm Implementation + * + *
+ * The PageRank algorithm is used by Google Search to rank web pages in their + * search engine + * results. It was named after Larry Page, one of the founders of Google. + * PageRank is a way of + * measuring the importance of website pages. + * + *
+ * Algorithm: 1. Initialize PageRank values for all pages to 1/N (where N is the
+ * total number
+ * of pages) 2. For each iteration: - For each page, calculate the new PageRank
+ * by summing the
+ * contributions from all incoming links - Apply the damping factor: PR(page) =
+ * (1-d) + d *
+ * sum(PR(incoming_page) / outgoing_links(incoming_page)) 3. Repeat until
+ * convergence
+ *
+ * @see PageRank Algorithm
+ */
+public final class PageRank {
+ private static final int MAX_NODES = 10;
+ private static final double DEFAULT_DAMPING_FACTOR = 0.85;
+ private static final int DEFAULT_ITERATIONS = 2;
+
+ private int[][] adjacencyMatrix;
+ private double[] pageRankValues;
+ private int nodeCount;
+
+ /**
+ * Constructor to initialize PageRank with specified number of nodes
+ *
+ * @param numberOfNodes the number of nodes/pages in the graph
+ * @throws IllegalArgumentException if numberOfNodes is less than 1 or greater
+ * than MAX_NODES
+ */
+ public PageRank(int numberOfNodes) {
+ if (numberOfNodes < 1 || numberOfNodes > MAX_NODES) {
+ throw new IllegalArgumentException("Number of nodes must be between 1 and " + MAX_NODES);
+ }
+ this.nodeCount = numberOfNodes;
+ this.adjacencyMatrix = new int[MAX_NODES][MAX_NODES];
+ this.pageRankValues = new double[MAX_NODES];
+ }
+
+ /**
+ * Default constructor for interactive mode
+ */
+ public PageRank() {
+ this.adjacencyMatrix = new int[MAX_NODES][MAX_NODES];
+ this.pageRankValues = new double[MAX_NODES];
+ }
+
+ /**
+ * Main method for interactive PageRank calculation
+ *
+ * @param args command line arguments (not used)
+ */
public static void main(String[] args) {
- int nodes;
- int i;
- int j;
- Scanner in = new Scanner(System.in);
- System.out.print("Enter the Number of WebPages: ");
- nodes = in.nextInt();
- PageRank p = new PageRank();
- System.out.println("Enter the Adjacency Matrix with 1->PATH & 0->NO PATH Between two WebPages: ");
- for (i = 1; i <= nodes; i++) {
- for (j = 1; j <= nodes; j++) {
- p.path[i][j] = in.nextInt();
- if (j == i) {
- p.path[i][j] = 0;
+ try (Scanner scanner = new Scanner(System.in)) {
+ System.out.print("Enter the Number of WebPages: ");
+ int nodes = scanner.nextInt();
+
+ PageRank pageRank = new PageRank(nodes);
+ System.out.println("Enter the Adjacency Matrix with 1->PATH & 0->NO PATH Between two WebPages: ");
+
+ for (int i = 1; i <= nodes; i++) {
+ for (int j = 1; j <= nodes; j++) {
+ int value = scanner.nextInt();
+ pageRank.setEdge(i, j, value);
}
}
+
+ pageRank.calculatePageRank(nodes, DEFAULT_DAMPING_FACTOR, DEFAULT_ITERATIONS, true);
}
- p.calc(nodes);
}
- public int[][] path = new int[10][10];
- public double[] pagerank = new double[10];
+ /**
+ * Sets an edge in the adjacency matrix
+ *
+ * @param from source node (1-indexed)
+ * @param to destination node (1-indexed)
+ * @param value 1 if edge exists, 0 otherwise
+ */
+ public void setEdge(int from, int to, int value) {
+ if (from == to) {
+ adjacencyMatrix[from][to] = 0; // No self-loops
+ } else {
+ adjacencyMatrix[from][to] = value;
+ }
+ }
- public void calc(double totalNodes) {
- double initialPageRank;
- double outgoingLinks = 0;
- double dampingFactor = 0.85;
- double[] tempPageRank = new double[10];
- int externalNodeNumber;
- int internalNodeNumber;
- int k = 1; // For Traversing
- int iterationStep = 1;
- initialPageRank = 1 / totalNodes;
- System.out.printf(" Total Number of Nodes :" + totalNodes + "\t Initial PageRank of All Nodes :" + initialPageRank + "\n");
+ /**
+ * Sets the adjacency matrix for the graph
+ *
+ * @param matrix the adjacency matrix (1-indexed)
+ */
+ public void setAdjacencyMatrix(int[][] matrix) {
+ for (int i = 1; i <= nodeCount; i++) {
+ for (int j = 1; j <= nodeCount; j++) {
+ setEdge(i, j, matrix[i][j]);
+ }
+ }
+ }
- // 0th ITERATION _ OR _ INITIALIZATION PHASE //
- for (k = 1; k <= totalNodes; k++) {
- this.pagerank[k] = initialPageRank;
+ /**
+ * Gets the PageRank value for a specific node
+ *
+ * @param node the node index (1-indexed)
+ * @return the PageRank value
+ */
+ public double getPageRank(int node) {
+ if (node < 1 || node > nodeCount) {
+ throw new IllegalArgumentException("Node index out of bounds");
}
- System.out.print("\n Initial PageRank Values , 0th Step \n");
+ return pageRankValues[node];
+ }
+
+ /**
+ * Gets all PageRank values
+ *
+ * @return array of PageRank values (1-indexed)
+ */
+ public double[] getAllPageRanks() {
+ return pageRankValues.clone();
+ }
+
+ /**
+ * Calculates PageRank using the default damping factor and iterations
+ *
+ * @param totalNodes the total number of nodes
+ * @return array of PageRank values
+ */
+ public double[] calculatePageRank(int totalNodes) {
+ return calculatePageRank(totalNodes, DEFAULT_DAMPING_FACTOR, DEFAULT_ITERATIONS, false);
+ }
+
+ /**
+ * Calculates PageRank with custom parameters
+ *
+ * @param totalNodes the total number of nodes
+ * @param dampingFactor the damping factor (typically 0.85)
+ * @param iterations number of iterations to perform
+ * @param verbose whether to print detailed output
+ * @return array of PageRank values
+ */
+ public double[] calculatePageRank(int totalNodes, double dampingFactor, int iterations, boolean verbose) {
+ validateInputParameters(totalNodes, dampingFactor, iterations);
- for (k = 1; k <= totalNodes; k++) {
- System.out.printf(" Page Rank of " + k + " is :\t" + this.pagerank[k] + "\n");
+ this.nodeCount = totalNodes;
+ double initialPageRank = 1.0 / totalNodes;
+
+ if (verbose) {
+ System.out.printf("Total Number of Nodes: %d\tInitial PageRank of All Nodes: %.6f%n", totalNodes, initialPageRank);
}
- while (iterationStep <= 2) { // Iterations
- // Store the PageRank for All Nodes in Temporary Array
- for (k = 1; k <= totalNodes; k++) {
- tempPageRank[k] = this.pagerank[k];
- this.pagerank[k] = 0;
- }
+ initializePageRanks(totalNodes, initialPageRank, verbose);
+ performIterations(totalNodes, dampingFactor, iterations, verbose);
- for (internalNodeNumber = 1; internalNodeNumber <= totalNodes; internalNodeNumber++) {
- for (externalNodeNumber = 1; externalNodeNumber <= totalNodes; externalNodeNumber++) {
- if (this.path[externalNodeNumber][internalNodeNumber] == 1) {
- k = 1;
- outgoingLinks = 0; // Count the Number of Outgoing Links for each externalNodeNumber
- while (k <= totalNodes) {
- if (this.path[externalNodeNumber][k] == 1) {
- outgoingLinks = outgoingLinks + 1; // Counter for Outgoing Links
- }
- k = k + 1;
- }
- // Calculate PageRank
- this.pagerank[internalNodeNumber] += tempPageRank[externalNodeNumber] * (1 / outgoingLinks);
- }
- }
- System.out.printf("\n After " + iterationStep + "th Step \n");
+ if (verbose) {
+ System.out.println("\nFinal PageRank:");
+ printPageRanks(totalNodes);
+ }
- for (k = 1; k <= totalNodes; k++) {
- System.out.printf(" Page Rank of " + k + " is :\t" + this.pagerank[k] + "\n");
- }
+ return pageRankValues.clone();
+ }
+
+ /**
+ * Validates input parameters for PageRank calculation
+ *
+ * @param totalNodes the total number of nodes
+ * @param dampingFactor the damping factor
+ * @param iterations number of iterations
+ * @throws IllegalArgumentException if parameters are invalid
+ */
+ private void validateInputParameters(int totalNodes, double dampingFactor, int iterations) {
+ if (totalNodes < 1 || totalNodes > MAX_NODES) {
+ throw new IllegalArgumentException("Total nodes must be between 1 and " + MAX_NODES);
+ }
+ if (dampingFactor < 0 || dampingFactor > 1) {
+ throw new IllegalArgumentException("Damping factor must be between 0 and 1");
+ }
+ if (iterations < 1) {
+ throw new IllegalArgumentException("Iterations must be at least 1");
+ }
+ }
+
+ /**
+ * Initializes PageRank values for all nodes
+ *
+ * @param totalNodes the total number of nodes
+ * @param initialPageRank the initial PageRank value
+ * @param verbose whether to print output
+ */
+ private void initializePageRanks(int totalNodes, double initialPageRank, boolean verbose) {
+ for (int i = 1; i <= totalNodes; i++) {
+ pageRankValues[i] = initialPageRank;
+ }
+
+ if (verbose) {
+ System.out.println("\nInitial PageRank Values, 0th Step");
+ printPageRanks(totalNodes);
+ }
+ }
- iterationStep = iterationStep + 1;
+ /**
+ * Performs the iterative PageRank calculation
+ *
+ * @param totalNodes the total number of nodes
+ * @param dampingFactor the damping factor
+ * @param iterations number of iterations
+ * @param verbose whether to print output
+ */
+ private void performIterations(int totalNodes, double dampingFactor, int iterations, boolean verbose) {
+ for (int iteration = 1; iteration <= iterations; iteration++) {
+ double[] tempPageRank = storeCurrentPageRanks(totalNodes);
+ calculateNewPageRanks(totalNodes, tempPageRank);
+ applyDampingFactor(totalNodes, dampingFactor);
+
+ if (verbose) {
+ System.out.printf("%nAfter %d iteration(s)%n", iteration);
+ printPageRanks(totalNodes);
}
+ }
+ }
- // Add the Damping Factor to PageRank
- for (k = 1; k <= totalNodes; k++) {
- this.pagerank[k] = (1 - dampingFactor) + dampingFactor * this.pagerank[k];
+ /**
+ * Stores current PageRank values in a temporary array
+ *
+ * @param totalNodes the total number of nodes
+ * @return temporary array with current PageRank values
+ */
+ private double[] storeCurrentPageRanks(int totalNodes) {
+ double[] tempPageRank = new double[MAX_NODES];
+ for (int i = 1; i <= totalNodes; i++) {
+ tempPageRank[i] = pageRankValues[i];
+ pageRankValues[i] = 0;
+ }
+ return tempPageRank;
+ }
+
+ /**
+ * Calculates new PageRank values based on incoming links
+ *
+ * @param totalNodes the total number of nodes
+ * @param tempPageRank temporary array with previous PageRank values
+ */
+ private void calculateNewPageRanks(int totalNodes, double[] tempPageRank) {
+ for (int targetNode = 1; targetNode <= totalNodes; targetNode++) {
+ for (int sourceNode = 1; sourceNode <= totalNodes; sourceNode++) {
+ if (adjacencyMatrix[sourceNode][targetNode] == 1) {
+ int outgoingLinks = countOutgoingLinks(sourceNode, totalNodes);
+ if (outgoingLinks > 0) {
+ pageRankValues[targetNode] += tempPageRank[sourceNode] / outgoingLinks;
+ }
+ }
}
+ }
+ }
- // Display PageRank
- System.out.print("\n Final Page Rank : \n");
- for (k = 1; k <= totalNodes; k++) {
- System.out.printf(" Page Rank of " + k + " is :\t" + this.pagerank[k] + "\n");
+ /**
+ * Applies the damping factor to all PageRank values
+ *
+ * @param totalNodes the total number of nodes
+ * @param dampingFactor the damping factor
+ */
+ private void applyDampingFactor(int totalNodes, double dampingFactor) {
+ for (int i = 1; i <= totalNodes; i++) {
+ pageRankValues[i] = (1 - dampingFactor) + dampingFactor * pageRankValues[i];
+ }
+ }
+
+ /**
+ * Counts the number of outgoing links from a node
+ *
+ * @param node the source node (1-indexed)
+ * @param totalNodes total number of nodes
+ * @return the count of outgoing links
+ */
+ private int countOutgoingLinks(int node, int totalNodes) {
+ int count = 0;
+ for (int i = 1; i <= totalNodes; i++) {
+ if (adjacencyMatrix[node][i] == 1) {
+ count++;
}
}
+ return count;
+ }
+
+ /**
+ * Prints the PageRank values for all nodes
+ *
+ * @param totalNodes the total number of nodes
+ */
+ private void printPageRanks(int totalNodes) {
+ for (int i = 1; i <= totalNodes; i++) {
+ System.out.printf("PageRank of %d: %.6f%n", i, pageRankValues[i]);
+ }
}
}
diff --git a/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java b/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java
deleted file mode 100644
index abfdd006879e..000000000000
--- a/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.thealgorithms.others;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PrintAMatrixInSpiralOrder {
- /**
- * Search a key in row and column wise sorted matrix
- *
- * @param matrix matrix to be searched
- * @param row number of rows matrix has
- * @param col number of columns matrix has
- * @author Sadiul Hakim : https://github.com/sadiul-hakim
- */
- public List The system is defined by the second-order differential equation:
+ * x'' + 2 * gamma * x' + omega₀² * x = 0
+ * where:
+ * This implementation provides:
+ * Usage Example:
+ *
+ * The algorithm works by:
+ * 1. Storing the last element of the array
+ * 2. Placing the search key at the last position (sentinel)
+ * 3. Searching from the beginning without bound checking
+ * 4. If found before the last position, return the index
+ * 5. If found at the last position, check if it was originally there
+ *
+ *
+ * Time Complexity:
+ * - Best case: O(1) - when the element is at the first position
+ * - Average case: O(n) - when the element is in the middle
+ * - Worst case: O(n) - when the element is not present
+ *
+ *
+ * Space Complexity: O(1) - only uses constant extra space
+ *
+ *
+ * Advantages over regular linear search:
+ * - Reduces the number of comparisons by eliminating bound checking
+ * - Slightly more efficient in practice due to fewer conditional checks
+ *
+ * @author TheAlgorithms Contributors
+ * @see LinearSearch
+ * @see SearchAlgorithm
+ */
+public class SentinelLinearSearch implements SearchAlgorithm {
+ /**
+ * Performs sentinel linear search on the given array.
+ *
+ * @param array the array to search in
+ * @param key the element to search for
+ * @param This test suite validates the correctness of the Bentley–Ottmann algorithm
+ * implementation by checking intersection points between multiple line segment configurations. Test cases include typical, edge, degenerate geometrical setups, and performance tests.
+ *
+ *
+ *
+ *
+ *
+ * {@code
+ * DampedOscillator oscillator = new DampedOscillator(10.0, 0.5);
+ * double displacement = oscillator.displacementAnalytical(1.0, 0.0, 0.1);
+ * double[] nextState = oscillator.stepEuler(new double[]{1.0, 0.0}, 0.001);
+ * }
+ *
+ * @author [Yash Rajput](https://github.com/the-yash-rajput)
+ */
+public final class DampedOscillator {
+
+ /** Natural (undamped) angular frequency (rad/s). */
+ private final double omega0;
+
+ /** Damping coefficient (s⁻¹). */
+ private final double gamma;
+
+ private DampedOscillator() {
+ throw new AssertionError("No instances.");
+ }
+
+ /**
+ * Constructs a damped oscillator model.
+ *
+ * @param omega0 the natural frequency (rad/s), must be positive
+ * @param gamma the damping coefficient (s⁻¹), must be non-negative
+ * @throws IllegalArgumentException if parameters are invalid
+ */
+ public DampedOscillator(double omega0, double gamma) {
+ if (omega0 <= 0) {
+ throw new IllegalArgumentException("Natural frequency must be positive.");
+ }
+ if (gamma < 0) {
+ throw new IllegalArgumentException("Damping coefficient must be non-negative.");
+ }
+ this.omega0 = omega0;
+ this.gamma = gamma;
+ }
+
+ /**
+ * Computes the analytical displacement of an underdamped oscillator.
+ * Formula: x(t) = A * exp(-γt) * cos(ω_d t + φ)
+ *
+ * @param amplitude the initial amplitude A
+ * @param phase the initial phase φ (radians)
+ * @param time the time t (seconds)
+ * @return the displacement x(t)
+ */
+ public double displacementAnalytical(double amplitude, double phase, double time) {
+ double omegaD = Math.sqrt(Math.max(0.0, omega0 * omega0 - gamma * gamma));
+ return amplitude * Math.exp(-gamma * time) * Math.cos(omegaD * time + phase);
+ }
+
+ /**
+ * Performs a single integration step using the explicit Euler method.
+ * State vector format: [x, v], where v = dx/dt.
+ *
+ * @param state the current state [x, v]
+ * @param dt the time step (seconds)
+ * @return the next state [x_next, v_next]
+ * @throws IllegalArgumentException if the state array is invalid or dt is non-positive
+ */
+ public double[] stepEuler(double[] state, double dt) {
+ if (state == null || state.length != 2) {
+ throw new IllegalArgumentException("State must be a non-null array of length 2.");
+ }
+ if (dt <= 0) {
+ throw new IllegalArgumentException("Time step must be positive.");
+ }
+
+ double x = state[0];
+ double v = state[1];
+ double acceleration = -2.0 * gamma * v - omega0 * omega0 * x;
+
+ double xNext = x + dt * v;
+ double vNext = v + dt * acceleration;
+
+ return new double[] {xNext, vNext};
+ }
+
+ /** @return the natural (undamped) angular frequency (rad/s). */
+ public double getOmega0() {
+ return omega0;
+ }
+
+ /** @return the damping coefficient (s⁻¹). */
+ public double getGamma() {
+ return gamma;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java
new file mode 100644
index 000000000000..399c3f1e041f
--- /dev/null
+++ b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java
@@ -0,0 +1,73 @@
+package com.thealgorithms.physics;
+
+/**
+ * 2D Elastic collision between two circular bodies
+ * Based on principles of conservation of momentum and kinetic energy.
+ *
+ * @author [Yash Rajput](https://github.com/the-yash-rajput)
+ */
+public final class ElasticCollision2D {
+
+ private ElasticCollision2D() {
+ throw new AssertionError("No instances. Utility class");
+ }
+
+ public static class Body {
+ public double x;
+ public double y;
+ public double vx;
+ public double vy;
+ public double mass;
+ public double radius;
+
+ public Body(double x, double y, double vx, double vy, double mass, double radius) {
+ this.x = x;
+ this.y = y;
+ this.vx = vx;
+ this.vy = vy;
+ this.mass = mass;
+ this.radius = radius;
+ }
+ }
+
+ /**
+ * Resolve instantaneous elastic collision between two circular bodies.
+ *
+ * @param a first body
+ * @param b second body
+ */
+ public static void resolveCollision(Body a, Body b) {
+ double dx = b.x - a.x;
+ double dy = b.y - a.y;
+ double dist = Math.hypot(dx, dy);
+
+ if (dist == 0) {
+ return; // overlapping
+ }
+
+ double nx = dx / dist;
+ double ny = dy / dist;
+
+ // relative velocity along normal
+ double rv = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny;
+
+ if (rv > 0) {
+ return; // moving apart
+ }
+
+ // impulse with masses
+ double m1 = a.mass;
+ double m2 = b.mass;
+
+ double j = -(1 + 1.0) * rv / (1.0 / m1 + 1.0 / m2);
+
+ // impulse vector
+ double impulseX = j * nx;
+ double impulseY = j * ny;
+
+ a.vx -= impulseX / m1;
+ a.vy -= impulseY / m1;
+ b.vx += impulseX / m2;
+ b.vy += impulseY / m2;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/physics/Gravitation.java b/src/main/java/com/thealgorithms/physics/Gravitation.java
new file mode 100644
index 000000000000..292fdc195f85
--- /dev/null
+++ b/src/main/java/com/thealgorithms/physics/Gravitation.java
@@ -0,0 +1,66 @@
+package com.thealgorithms.physics;
+
+/**
+ * Implements Newton's Law of Universal Gravitation.
+ * Provides simple static methods to calculate gravitational force and circular orbit velocity.
+ *
+ * @author [Priyanshu Kumar Singh](https://github.com/Priyanshu1303d)
+ * @see Wikipedia
+ */
+public final class Gravitation {
+
+ /** Gravitational constant in m^3 kg^-1 s^-2 */
+ public static final double GRAVITATIONAL_CONSTANT = 6.67430e-11;
+
+ /**
+ * Private constructor to prevent instantiation of this utility class.
+ */
+ private Gravitation() {
+ }
+
+ /**
+ * Calculates the gravitational force vector exerted by one body on another.
+ *
+ * @param m1 Mass of the first body (kg).
+ * @param x1 X-position of the first body (m).
+ * @param y1 Y-position of the first body (m).
+ * @param m2 Mass of the second body (kg).
+ * @param x2 X-position of the second body (m).
+ * @param y2 Y-position of the second body (m).
+ * @return A double array `[fx, fy]` representing the force vector on the second body.
+ */
+ public static double[] calculateGravitationalForce(double m1, double x1, double y1, double m2, double x2, double y2) {
+ double dx = x1 - x2;
+ double dy = y1 - y2;
+ double distanceSq = dx * dx + dy * dy;
+
+ // If bodies are at the same position, force is zero to avoid division by zero.
+ if (distanceSq == 0) {
+ return new double[] {0, 0};
+ }
+
+ double distance = Math.sqrt(distanceSq);
+ double forceMagnitude = GRAVITATIONAL_CONSTANT * m1 * m2 / distanceSq;
+
+ // Calculate the components of the force vector
+ double fx = forceMagnitude * (dx / distance);
+ double fy = forceMagnitude * (dy / distance);
+
+ return new double[] {fx, fy};
+ }
+
+ /**
+ * Calculates the speed required for a stable circular orbit.
+ *
+ * @param centralMass The mass of the central body (kg).
+ * @param radius The radius of the orbit (m).
+ * @return The orbital speed (m/s).
+ * @throws IllegalArgumentException if mass or radius are not positive.
+ */
+ public static double calculateCircularOrbitVelocity(double centralMass, double radius) {
+ if (centralMass <= 0 || radius <= 0) {
+ throw new IllegalArgumentException("Mass and radius must be positive.");
+ }
+ return Math.sqrt(GRAVITATIONAL_CONSTANT * centralMass / radius);
+ }
+}
diff --git a/src/main/java/com/thealgorithms/physics/ProjectileMotion.java b/src/main/java/com/thealgorithms/physics/ProjectileMotion.java
new file mode 100644
index 000000000000..cfc79547922c
--- /dev/null
+++ b/src/main/java/com/thealgorithms/physics/ProjectileMotion.java
@@ -0,0 +1,96 @@
+package com.thealgorithms.physics;
+
+/**
+ *
+ * This implementation calculates the flight path of a projectile launched from any INITIAL HEIGHT.
+ * It is a more flexible version of the ground-to-ground model.
+ *
+ * @see Wikipedia - Projectile Motion
+ * @author [Priyanshu Kumar Singh](https://github.com/Priyanshu1303d)
+ */
+public final class ProjectileMotion {
+
+ private ProjectileMotion() {
+ }
+
+ /** Standard Earth gravity constant*/
+ private static final double GRAVITY = 9.80665;
+
+ /**
+ * A simple container for the results of a projectile motion calculation.
+ */
+ public static final class Result {
+ private final double timeOfFlight;
+ private final double horizontalRange;
+ private final double maxHeight;
+
+ public Result(double timeOfFlight, double horizontalRange, double maxHeight) {
+ this.timeOfFlight = timeOfFlight;
+ this.horizontalRange = horizontalRange;
+ this.maxHeight = maxHeight;
+ }
+
+ /** @return The total time the projectile is in the air (seconds). */
+ public double getTimeOfFlight() {
+ return timeOfFlight;
+ }
+
+ /** @return The total horizontal distance traveled (meters). */
+ public double getHorizontalRange() {
+ return horizontalRange;
+ }
+
+ /** @return The maximum vertical height from the ground (meters). */
+ public double getMaxHeight() {
+ return maxHeight;
+ }
+ }
+
+ /**
+ * Calculates projectile trajectory using standard Earth gravity.
+ *
+ * @param initialVelocity Initial speed of the projectile (m/s).
+ * @param launchAngleDegrees Launch angle from the horizontal (degrees).
+ * @param initialHeight Starting height of the projectile (m).
+ * @return A {@link Result} object with the trajectory data.
+ */
+ public static Result calculateTrajectory(double initialVelocity, double launchAngleDegrees, double initialHeight) {
+ return calculateTrajectory(initialVelocity, launchAngleDegrees, initialHeight, GRAVITY);
+ }
+
+ /**
+ * Calculates projectile trajectory with a custom gravity value.
+ *
+ * @param initialVelocity Initial speed (m/s). Must be non-negative.
+ * @param launchAngleDegrees Launch angle (degrees).
+ * @param initialHeight Starting height (m). Must be non-negative.
+ * @param gravity Acceleration due to gravity (m/s^2). Must be positive.
+ * @return A {@link Result} object with the trajectory data.
+ */
+ public static Result calculateTrajectory(double initialVelocity, double launchAngleDegrees, double initialHeight, double gravity) {
+ if (initialVelocity < 0 || initialHeight < 0 || gravity <= 0) {
+ throw new IllegalArgumentException("Velocity, height, and gravity must be non-negative, and gravity must be positive.");
+ }
+
+ double launchAngleRadians = Math.toRadians(launchAngleDegrees);
+ double initialVerticalVelocity = initialVelocity * Math.sin(launchAngleRadians); // Initial vertical velocity
+ double initialHorizontalVelocity = initialVelocity * Math.cos(launchAngleRadians); // Initial horizontal velocity
+
+ // Correctly calculate total time of flight using the quadratic formula for vertical motion.
+ // y(t) = y0 + initialVerticalVelocity*t - 0.5*g*t^2. We solve for t when y(t) = 0.
+ double totalTimeOfFlight = (initialVerticalVelocity + Math.sqrt(initialVerticalVelocity * initialVerticalVelocity + 2 * gravity * initialHeight)) / gravity;
+
+ // Calculate max height. If launched downwards, max height is the initial height.
+ double maxHeight;
+ if (initialVerticalVelocity > 0) {
+ double heightGained = initialVerticalVelocity * initialVerticalVelocity / (2 * gravity);
+ maxHeight = initialHeight + heightGained;
+ } else {
+ maxHeight = initialHeight;
+ }
+
+ double horizontalRange = initialHorizontalVelocity * totalTimeOfFlight;
+
+ return new Result(totalTimeOfFlight, horizontalRange, maxHeight);
+ }
+}
diff --git a/src/main/java/com/thealgorithms/physics/SimplePendulumRK4.java b/src/main/java/com/thealgorithms/physics/SimplePendulumRK4.java
new file mode 100644
index 000000000000..6de69c103b5a
--- /dev/null
+++ b/src/main/java/com/thealgorithms/physics/SimplePendulumRK4.java
@@ -0,0 +1,126 @@
+package com.thealgorithms.physics;
+
+/**
+ * Simulates a simple pendulum using the Runge-Kutta 4th order method.
+ * The pendulum is modeled with the nonlinear differential equation.
+ *
+ * @author [Yash Rajput](https://github.com/the-yash-rajput)
+ */
+public final class SimplePendulumRK4 {
+
+ private SimplePendulumRK4() {
+ throw new AssertionError("No instances.");
+ }
+
+ private final double length; // meters
+ private final double g; // acceleration due to gravity (m/s^2)
+
+ /**
+ * Constructs a simple pendulum simulator.
+ *
+ * @param length the length of the pendulum in meters
+ * @param g the acceleration due to gravity in m/s^2
+ */
+ public SimplePendulumRK4(double length, double g) {
+ if (length <= 0) {
+ throw new IllegalArgumentException("Length must be positive");
+ }
+ if (g <= 0) {
+ throw new IllegalArgumentException("Gravity must be positive");
+ }
+ this.length = length;
+ this.g = g;
+ }
+
+ /**
+ * Computes the derivatives of the state vector.
+ * State: [theta, omega] where theta is angle and omega is angular velocity.
+ *
+ * @param state the current state [theta, omega]
+ * @return the derivatives [dtheta/dt, domega/dt]
+ */
+ private double[] derivatives(double[] state) {
+ double theta = state[0];
+ double omega = state[1];
+ double dtheta = omega;
+ double domega = -(g / length) * Math.sin(theta);
+ return new double[] {dtheta, domega};
+ }
+
+ /**
+ * Performs one time step using the RK4 method.
+ *
+ * @param state the current state [theta, omega]
+ * @param dt the time step size
+ * @return the new state after time dt
+ */
+ public double[] stepRK4(double[] state, double dt) {
+ if (state == null || state.length != 2) {
+ throw new IllegalArgumentException("State must be array of length 2");
+ }
+ if (dt <= 0) {
+ throw new IllegalArgumentException("Time step must be positive");
+ }
+
+ double[] k1 = derivatives(state);
+ double[] s2 = new double[] {state[0] + 0.5 * dt * k1[0], state[1] + 0.5 * dt * k1[1]};
+
+ double[] k2 = derivatives(s2);
+ double[] s3 = new double[] {state[0] + 0.5 * dt * k2[0], state[1] + 0.5 * dt * k2[1]};
+
+ double[] k3 = derivatives(s3);
+ double[] s4 = new double[] {state[0] + dt * k3[0], state[1] + dt * k3[1]};
+
+ double[] k4 = derivatives(s4);
+
+ double thetaNext = state[0] + dt / 6.0 * (k1[0] + 2 * k2[0] + 2 * k3[0] + k4[0]);
+ double omegaNext = state[1] + dt / 6.0 * (k1[1] + 2 * k2[1] + 2 * k3[1] + k4[1]);
+
+ return new double[] {thetaNext, omegaNext};
+ }
+
+ /**
+ * Simulates the pendulum for a given duration.
+ *
+ * @param initialState the initial state [theta, omega]
+ * @param dt the time step size
+ * @param steps the number of steps to simulate
+ * @return array of states at each step
+ */
+ public double[][] simulate(double[] initialState, double dt, int steps) {
+ double[][] trajectory = new double[steps + 1][2];
+ trajectory[0] = initialState.clone();
+
+ double[] currentState = initialState.clone();
+ for (int i = 1; i <= steps; i++) {
+ currentState = stepRK4(currentState, dt);
+ trajectory[i] = currentState.clone();
+ }
+
+ return trajectory;
+ }
+
+ /**
+ * Calculates the total energy of the pendulum.
+ * E = (1/2) * m * L^2 * omega^2 + m * g * L * (1 - cos(theta))
+ * We use m = 1 for simplicity.
+ *
+ * @param state the current state [theta, omega]
+ * @return the total energy
+ */
+ public double calculateEnergy(double[] state) {
+ double theta = state[0];
+ double omega = state[1];
+ double kineticEnergy = 0.5 * length * length * omega * omega;
+ double potentialEnergy = g * length * (1 - Math.cos(theta));
+ return kineticEnergy + potentialEnergy;
+ }
+
+ public double getLength() {
+ return length;
+ }
+
+ public double getGravity() {
+ return g;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java b/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java
new file mode 100644
index 000000000000..1a5903a5d134
--- /dev/null
+++ b/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java
@@ -0,0 +1,99 @@
+package com.thealgorithms.searches;
+
+import com.thealgorithms.devutils.searches.SearchAlgorithm;
+
+/**
+ * Sentinel Linear Search is a variation of linear search that eliminates the
+ * need to check the array bounds in each iteration by placing the search key
+ * at the end of the array as a sentinel value.
+ *
+ * > graph;
+ private static final int NUM_VERTICES = 6;
+ private static final int MAX_EDGE_WEIGHT = 10;
+
+ @BeforeEach
+ void setUp() {
+ graph = new ArrayList<>();
+ for (int i = 0; i < NUM_VERTICES; i++) {
+ graph.add(new ArrayList<>());
+ }
+ }
+
+ private void addEdge(int u, int v, int weight) {
+ graph.get(u).add(new DialsAlgorithm.Edge(v, weight));
+ }
+
+ @Test
+ @DisplayName("Test with a simple connected graph")
+ void testSimpleGraph() {
+ // Build graph from a standard example
+ addEdge(0, 1, 2);
+ addEdge(0, 2, 4);
+ addEdge(1, 2, 1);
+ addEdge(1, 3, 7);
+ addEdge(2, 4, 3);
+ addEdge(3, 5, 1);
+ addEdge(4, 3, 2);
+ addEdge(4, 5, 5);
+
+ int[] expectedDistances = {0, 2, 3, 8, 6, 9};
+ int[] actualDistances = DialsAlgorithm.run(graph, 0, MAX_EDGE_WEIGHT);
+ assertArrayEquals(expectedDistances, actualDistances);
+ }
+
+ @Test
+ @DisplayName("Test with a disconnected node")
+ void testDisconnectedNode() {
+ addEdge(0, 1, 5);
+ addEdge(1, 2, 5);
+ // Node 3, 4, 5 are disconnected
+
+ int[] expectedDistances = {0, 5, 10, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
+ int[] actualDistances = DialsAlgorithm.run(graph, 0, MAX_EDGE_WEIGHT);
+ assertArrayEquals(expectedDistances, actualDistances);
+ }
+
+ @Test
+ @DisplayName("Test with source as destination")
+ void testSourceIsDestination() {
+ addEdge(0, 1, 10);
+ int[] expectedDistances = {0, 10, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
+ // Run with source 0
+ int[] actualDistances = DialsAlgorithm.run(graph, 0, MAX_EDGE_WEIGHT);
+ assertArrayEquals(expectedDistances, actualDistances);
+ }
+
+ @Test
+ @DisplayName("Test graph with multiple paths to a node")
+ void testMultiplePaths() {
+ addEdge(0, 1, 10);
+ addEdge(0, 2, 3);
+ addEdge(2, 1, 2); // Shorter path to 1 is via 2 (3+2=5)
+
+ int[] expectedDistances = {0, 5, 3, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
+ int[] actualDistances = DialsAlgorithm.run(graph, 0, MAX_EDGE_WEIGHT);
+ assertArrayEquals(expectedDistances, actualDistances);
+ }
+
+ @Test
+ @DisplayName("Test with an invalid source vertex")
+ void testInvalidSource() {
+ assertThrows(IllegalArgumentException.class, () -> DialsAlgorithm.run(graph, -1, MAX_EDGE_WEIGHT));
+ assertThrows(IllegalArgumentException.class, () -> DialsAlgorithm.run(graph, NUM_VERTICES, MAX_EDGE_WEIGHT));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java
new file mode 100644
index 000000000000..15e77b357f83
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java
@@ -0,0 +1,125 @@
+package com.thealgorithms.datastructures.graphs;
+
+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;
+
+/**
+ * Testcases for 2-SAT.
+ * Please note thea whlie checking for boolean assignments always keep n + 1 elements and the first element should be always false.
+ */
+public class TwoSatTest {
+ private TwoSat twoSat;
+
+ /**
+ * Case 1: Basic satisfiable case.
+ * Simple 3 clauses with consistent assignments.
+ */
+ @Test
+ public void testSatisfiableBasicCase() {
+ twoSat = new TwoSat(5);
+
+ twoSat.addClause(1, false, 2, false); // (x1 ∨ x2)
+ twoSat.addClause(3, true, 2, false); // (¬x3 ∨ x2)
+ twoSat.addClause(4, false, 5, true); // (x4 ∨ ¬x5)
+
+ twoSat.solve();
+
+ assertTrue(twoSat.isSolutionExists(), "Expected solution to exist");
+ boolean[] expected = {false, true, true, true, true, true};
+ assertArrayEquals(expected, twoSat.getSolutions());
+ }
+
+ /**
+ * Case 2: Unsatisfiable due to direct contradiction.
+ * (x1 ∨ x1) ∧ (¬x1 ∨ ¬x1) makes x1 and ¬x1 both required.
+ */
+ @Test
+ public void testUnsatisfiableContradiction() {
+ twoSat = new TwoSat(1);
+
+ twoSat.addClause(1, false, 1, false); // (x1 ∨ x1)
+ twoSat.addClause(1, true, 1, true); // (¬x1 ∨ ¬x1)
+
+ twoSat.solve();
+
+ assertFalse(twoSat.isSolutionExists(), "Expected no solution (contradiction)");
+ }
+
+ /**
+ * Case 3: Single variable, trivially satisfiable.
+ * Only (x1 ∨ x1) exists.
+ */
+ @Test
+ public void testSingleVariableTrivialSatisfiable() {
+ twoSat = new TwoSat(1);
+
+ twoSat.addClause(1, false, 1, false); // (x1 ∨ x1)
+
+ twoSat.solve();
+
+ assertTrue(twoSat.isSolutionExists(), "Expected solution to exist");
+ boolean[] expected = {false, true};
+ assertArrayEquals(expected, twoSat.getSolutions());
+ }
+
+ /**
+ * Case 4: Larger satisfiable system with dependencies.
+ * (x1 ∨ x2), (¬x2 ∨ x3), (¬x3 ∨ x4), (¬x4 ∨ x5)
+ */
+ @Test
+ public void testChainedDependenciesSatisfiable() {
+ twoSat = new TwoSat(5);
+
+ twoSat.addClause(1, false, 2, false);
+ twoSat.addClause(2, true, 3, false);
+ twoSat.addClause(3, true, 4, false);
+ twoSat.addClause(4, true, 5, false);
+
+ twoSat.solve();
+
+ assertTrue(twoSat.isSolutionExists(), "Expected solution to exist");
+ boolean[] solution = twoSat.getSolutions();
+ for (int i = 1; i <= 5; i++) {
+ assertTrue(solution[i], "Expected x" + i + " to be true");
+ }
+ }
+
+ /**
+ * Case 5: Contradiction due to dependency cycle.
+ * (x1 ∨ x2), (¬x1 ∨ ¬x2), (x1 ∨ ¬x2), (¬x1 ∨ x2)
+ * These clauses form a circular dependency making it impossible.
+ */
+ @Test
+ public void testUnsatisfiableCycle() {
+ twoSat = new TwoSat(2);
+
+ twoSat.addClause(1, false, 2, false);
+ twoSat.addClause(1, true, 2, true);
+ twoSat.addClause(1, false, 2, true);
+ twoSat.addClause(1, true, 2, false);
+
+ twoSat.solve();
+
+ assertFalse(twoSat.isSolutionExists(), "Expected no solution due to contradictory cycle");
+ }
+
+ /**
+ * Testcase from CSES
+ */
+ @Test
+ public void test6() {
+ twoSat = new TwoSat(2);
+
+ twoSat.addClause(1, true, 2, false);
+ twoSat.addClause(2, true, 1, false);
+ twoSat.addClause(1, true, 1, true);
+ twoSat.addClause(2, false, 2, false);
+
+ twoSat.solve();
+
+ assertFalse(twoSat.isSolutionExists(), "Expected no solution.");
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedListTest.java
new file mode 100644
index 000000000000..667f9fcf5700
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/lists/FlattenMultilevelLinkedListTest.java
@@ -0,0 +1,112 @@
+package com.thealgorithms.datastructures.lists;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+/**
+ * Unit tests for the FlattenMultilevelLinkedList class.
+ * This class tests the flattening logic with various list structures,
+ * including null lists, simple lists, and complex multilevel lists.
+ */
+final class FlattenMultilevelLinkedListTest {
+
+ // A helper function to convert a flattened list (connected by child pointers)
+ // into a standard Java List for easy comparison.
+ private List