diff --git a/.github/workflows/update-directorymd.yml b/.github/workflows/update-directorymd.yml index aa553b46a23b..1cfee6e36e4e 100644 --- a/.github/workflows/update-directorymd.yml +++ b/.github/workflows/update-directorymd.yml @@ -1,4 +1,4 @@ -name: Generate Directory Markdown +ο»Ώname: Generate Directory Markdown on: push: diff --git a/DIRECTORY.md b/DIRECTORY.md index deaf59636fa4..585c634c3429 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -626,8 +626,6 @@ - πŸ“„ [SkylineProblem](src/main/java/com/thealgorithms/others/SkylineProblem.java) - πŸ“„ [TwoPointers](src/main/java/com/thealgorithms/others/TwoPointers.java) - πŸ“„ [Verhoeff](src/main/java/com/thealgorithms/others/Verhoeff.java) - - πŸ“ **cn** - - πŸ“„ [HammingDistance](src/main/java/com/thealgorithms/others/cn/HammingDistance.java) - πŸ“ **physics** - πŸ“„ [CoulombsLaw](src/main/java/com/thealgorithms/physics/CoulombsLaw.java) - πŸ“„ [DampedOscillator](src/main/java/com/thealgorithms/physics/DampedOscillator.java) @@ -701,7 +699,6 @@ - πŸ“„ [LowerBound](src/main/java/com/thealgorithms/searches/LowerBound.java) - πŸ“„ [MonteCarloTreeSearch](src/main/java/com/thealgorithms/searches/MonteCarloTreeSearch.java) - πŸ“„ [OrderAgnosticBinarySearch](src/main/java/com/thealgorithms/searches/OrderAgnosticBinarySearch.java) - - πŸ“„ [PerfectBinarySearch](src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java) - πŸ“„ [QuickSelect](src/main/java/com/thealgorithms/searches/QuickSelect.java) - πŸ“„ [RabinKarpAlgorithm](src/main/java/com/thealgorithms/searches/RabinKarpAlgorithm.java) - πŸ“„ [RandomSearch](src/main/java/com/thealgorithms/searches/RandomSearch.java) @@ -710,7 +707,6 @@ - πŸ“„ [SaddlebackSearch](src/main/java/com/thealgorithms/searches/SaddlebackSearch.java) - πŸ“„ [SearchInARowAndColWiseSortedMatrix](src/main/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrix.java) - πŸ“„ [SentinelLinearSearch](src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java) - - πŸ“„ [SortOrderAgnosticBinarySearch](src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java) - πŸ“„ [SquareRootBinarySearch](src/main/java/com/thealgorithms/searches/SquareRootBinarySearch.java) - πŸ“„ [TernarySearch](src/main/java/com/thealgorithms/searches/TernarySearch.java) - πŸ“„ [UnionFind](src/main/java/com/thealgorithms/searches/UnionFind.java) @@ -817,7 +813,6 @@ - πŸ“„ [LetterCombinationsOfPhoneNumber](src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java) - πŸ“„ [LongestCommonPrefix](src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java) - πŸ“„ [LongestNonRepetitiveSubstring](src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java) - - πŸ“„ [LongestPalindromicSubstring](src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java) - πŸ“„ [Lower](src/main/java/com/thealgorithms/strings/Lower.java) - πŸ“„ [Manacher](src/main/java/com/thealgorithms/strings/Manacher.java) - πŸ“„ [MyAtoi](src/main/java/com/thealgorithms/strings/MyAtoi.java) @@ -834,7 +829,6 @@ - πŸ“„ [StringMatchFiniteAutomata](src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java) - πŸ“„ [SuffixArray](src/main/java/com/thealgorithms/strings/SuffixArray.java) - πŸ“„ [Upper](src/main/java/com/thealgorithms/strings/Upper.java) - - πŸ“„ [ValidParentheses](src/main/java/com/thealgorithms/strings/ValidParentheses.java) - πŸ“„ [WordLadder](src/main/java/com/thealgorithms/strings/WordLadder.java) - πŸ“„ [ZAlgorithm](src/main/java/com/thealgorithms/strings/ZAlgorithm.java) - πŸ“ **zigZagPattern** @@ -1395,7 +1389,6 @@ - πŸ“„ [MaximumSumOfDistinctSubarraysWithLengthKTest](src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java) - πŸ“„ [MiniMaxAlgorithmTest](src/test/java/com/thealgorithms/others/MiniMaxAlgorithmTest.java) - πŸ“„ [MosAlgorithmTest](src/test/java/com/thealgorithms/others/MosAlgorithmTest.java) - - πŸ“„ [NewManShanksPrimeTest](src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java) - πŸ“„ [NextFitTest](src/test/java/com/thealgorithms/others/NextFitTest.java) - πŸ“„ [PageRankTest](src/test/java/com/thealgorithms/others/PageRankTest.java) - πŸ“„ [PasswordGenTest](src/test/java/com/thealgorithms/others/PasswordGenTest.java) @@ -1404,8 +1397,6 @@ - πŸ“„ [SkylineProblemTest](src/test/java/com/thealgorithms/others/SkylineProblemTest.java) - πŸ“„ [TwoPointersTest](src/test/java/com/thealgorithms/others/TwoPointersTest.java) - πŸ“„ [WorstFitCPUTest](src/test/java/com/thealgorithms/others/WorstFitCPUTest.java) - - πŸ“ **cn** - - πŸ“„ [HammingDistanceTest](src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java) - πŸ“ **physics** - πŸ“„ [CoulombsLawTest](src/test/java/com/thealgorithms/physics/CoulombsLawTest.java) - πŸ“„ [DampedOscillatorTest](src/test/java/com/thealgorithms/physics/DampedOscillatorTest.java) @@ -1479,7 +1470,6 @@ - πŸ“„ [LowerBoundTest](src/test/java/com/thealgorithms/searches/LowerBoundTest.java) - πŸ“„ [MonteCarloTreeSearchTest](src/test/java/com/thealgorithms/searches/MonteCarloTreeSearchTest.java) - πŸ“„ [OrderAgnosticBinarySearchTest](src/test/java/com/thealgorithms/searches/OrderAgnosticBinarySearchTest.java) - - πŸ“„ [PerfectBinarySearchTest](src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java) - πŸ“„ [QuickSelectTest](src/test/java/com/thealgorithms/searches/QuickSelectTest.java) - πŸ“„ [RabinKarpAlgorithmTest](src/test/java/com/thealgorithms/searches/RabinKarpAlgorithmTest.java) - πŸ“„ [RandomSearchTest](src/test/java/com/thealgorithms/searches/RandomSearchTest.java) @@ -1488,7 +1478,6 @@ - πŸ“„ [SaddlebackSearchTest](src/test/java/com/thealgorithms/searches/SaddlebackSearchTest.java) - πŸ“„ [SearchInARowAndColWiseSortedMatrixTest](src/test/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrixTest.java) - πŸ“„ [SentinelLinearSearchTest](src/test/java/com/thealgorithms/searches/SentinelLinearSearchTest.java) - - πŸ“„ [SortOrderAgnosticBinarySearchTest](src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java) - πŸ“„ [SquareRootBinarySearchTest](src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java) - πŸ“„ [TernarySearchTest](src/test/java/com/thealgorithms/searches/TernarySearchTest.java) - πŸ“„ [TestSearchInARowAndColWiseSortedMatrix](src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java) @@ -1593,7 +1582,6 @@ - πŸ“„ [LetterCombinationsOfPhoneNumberTest](src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java) - πŸ“„ [LongestCommonPrefixTest](src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java) - πŸ“„ [LongestNonRepetitiveSubstringTest](src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java) - - πŸ“„ [LongestPalindromicSubstringTest](src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java) - πŸ“„ [LowerTest](src/test/java/com/thealgorithms/strings/LowerTest.java) - πŸ“„ [ManacherTest](src/test/java/com/thealgorithms/strings/ManacherTest.java) - πŸ“„ [MyAtoiTest](src/test/java/com/thealgorithms/strings/MyAtoiTest.java) @@ -1609,7 +1597,6 @@ - πŸ“„ [StringMatchFiniteAutomataTest](src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java) - πŸ“„ [SuffixArrayTest](src/test/java/com/thealgorithms/strings/SuffixArrayTest.java) - πŸ“„ [UpperTest](src/test/java/com/thealgorithms/strings/UpperTest.java) - - πŸ“„ [ValidParenthesesTest](src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java) - πŸ“„ [WordLadderTest](src/test/java/com/thealgorithms/strings/WordLadderTest.java) - πŸ“„ [ZAlgorithmTest](src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java) - πŸ“ **zigZagPattern** diff --git a/README-ko.md b/README-ko.md deleted file mode 100644 index 4f8cab92fc42..000000000000 --- a/README-ko.md +++ /dev/null @@ -1,191 +0,0 @@ -# μ•Œκ³ λ¦¬μ¦˜ - μžλ°” - -## 이 [개발브런치](https://github.com/TheAlgorithms/Java/tree/Development)λŠ” κΈ°μ‘΄ ν”„λ‘œμ νŠΈλ₯Ό Java ν”„λ‘œμ νŠΈ ꡬ쑰둜 μž¬κ°œλ°œν•˜κΈ° μœ„ν•΄ μž‘μ„±λ˜μ—ˆλ‹€. 기여도λ₯Ό μœ„ν•΄ 개발 μ§€μ‚¬λ‘œ μ „ν™˜ν•  수 μžˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ 이 문제λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€. μ»¨νŠΈλ¦¬λ·°μ…˜μ„ μœ„ν•΄ [개발브런치](https://github.com/TheAlgorithms/Java/tree/Development)둜 μ „ν™˜ν•  수 μžˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ [이 이슈](https://github.com/TheAlgorithms/Java/issues/474)λ₯Ό μ°Έκ³ ν•˜μ‹­μ‹œμ˜€. - -### μžλ°”λ‘œ κ΅¬ν˜„λœ λͺ¨λ“  μ•Œκ³ λ¦¬μ¦˜λ“€ (ꡐ윑용) - -이것듀은 단지 μ‹œλ²”μ„ μœ„ν•œ 것이닀. ν‘œμ€€ μžλ°” λΌμ΄λΈŒλŸ¬λ¦¬μ—λŠ” μ„±λŠ₯μƒμ˜ 이유둜 더 λ‚˜μ€ 것듀이 κ΅¬ν˜„λ˜μ–΄μžˆλ‹€ - -## μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ - -### Bubble(버블 μ •λ ¬) - -![alt text][bubble-image] - -From [Wikipedia][bubble-wiki]: 버블 μ†ŒνŠΈ(sinking sor라고도 λΆˆλ¦¬μ›€)λŠ” 리슀트λ₯Ό 반볡적인 λ‹¨κ³„λ‘œ μ ‘κ·Όν•˜μ—¬ μ •λ ¬ν•œλ‹€. 각각의 짝을 λΉ„κ΅ν•˜λ©°, μˆœμ„œκ°€ 잘λͺ»λœ 경우 κ·Έμ ‘ν•œ μ•„μ΄ν…œλ“€μ„ μŠ€μ™‘ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. 더 이상 μŠ€μ™‘ν•  것이 없을 λ•ŒκΉŒμ§€ λ°˜λ³΅ν•˜λ©°, 반볡이 λλ‚¨μŒ λ¦¬μŠ€νŠΈκ°€ μ •λ ¬λ˜μ—ˆμŒμ„ μ˜λ―Έν•œλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n) -- 평균 μ„±λŠ₯ O(n^2) - -###### View the algorithm in [action][bubble-toptal] - -### Insertion(μ‚½μž… μ •λ ¬) - -![alt text][insertion-image] - -From [Wikipedia][insertion-wiki]: μ‚½μž… 정렬은 μ΅œμ’… μ •λ ¬λœ λ°°μ—΄(λ˜λŠ” 리슀트)을 ν•œλ²ˆμ— ν•˜λ‚˜μ”© κ΅¬μΆ•ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. 이것은 큰 λ¦¬μŠ€νŠΈμ—μ„œ 더 λ‚˜μ€ μ•Œκ³ λ¦¬μ¦˜μΈ 퀡 μ†ŒνŠΈ, νž™ μ†ŒνŠΈ, λ˜λŠ” λ¨Έμ§€ μ†ŒνŠΈλ³΄λ‹€ 훨씬 μ•ˆμ’‹μ€ νš¨μœ¨μ„ κ°€μ§„λ‹€. κ·Έλ¦Όμ—μ„œ 각 λ§‰λŒ€λŠ” μ •λ ¬ν•΄μ•Ό ν•˜λŠ” λ°°μ—΄μ˜ μš”μ†Œλ₯Ό λ‚˜νƒ€λ‚Έλ‹€. 상단과 두 번째 상단 λ§‰λŒ€μ˜ 첫 번째 κ΅μ°¨μ μ—μ„œ λ°œμƒν•˜λŠ” 것은 두 번째 μš”μ†Œκ°€ 첫 번째 μš”μ†Œλ³΄λ‹€ 더 높은 μš°μ„  μˆœμœ„λ₯Ό κ°€μ§€κΈ° λ•Œλ¬Έμ— λ§‰λŒ€λ‘œ ν‘œμ‹œλ˜λŠ” μ΄λŸ¬ν•œ μš”μ†Œλ₯Ό κ΅ν™˜ν•œ 것이닀. 이 방법을 λ°˜λ³΅ν•˜λ©΄ μ‚½μž… 정렬이 μ™„λ£Œλœλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n) -- 평균 O(n^2) - -###### View the algorithm in [action][insertion-toptal] - -### Merge(합병 μ •λ ¬) - -![alt text][merge-image] - -From [Wikipedia][merge-wiki]: 컴퓨터 κ³Όν•™μ—μ„œ, 합병 정렬은 효율적인, λ²”μš©μ μΈ, 비ꡐ 기반 μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. λŒ€λΆ€λΆ„μ˜ κ΅¬ν˜„μ€ μ•ˆμ •μ μΈ λΆ„λ₯˜λ₯Ό μ΄λ£¨λŠ”λ°, 이것은 κ΅¬ν˜„μ΄ μ •λ ¬λœ 좜λ ₯에 λ™μΌν•œ μš”μ†Œμ˜ μž…λ ₯ μˆœμ„œλ₯Ό μœ μ§€ν•œλ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€. 합병 정렬은 1945년에 John von Neumann이 발λͺ…ν•œ λΆ„ν•  정볡 μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n log n) (일반적) -- 졜고의 μ„±λŠ₯ O(n log n) -- 평균 O(n log n) - -###### View the algorithm in [action][merge-toptal] - -### Quick(퀡 μ •λ ¬) - -![alt text][quick-image] - -From [Wikipedia][quick-wiki]: 퀡 μ •λ ¬sometimes called partition-exchange sort)은 효율적인 μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ, λ°°μ—΄μ˜ μš”μ†Œλ₯Ό μˆœμ„œλŒ€λ‘œ μ •λ ¬ν•˜λŠ” 체계적인 방법 μ—­ν™œμ„ ν•œλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n log n) or O(n) with three-way partition -- 평균 O(n log n) - -###### View the algorithm in [action][quick-toptal] - -### Selection(선택 μ •λ ¬) - -![alt text][selection-image] - -From [Wikipedia][selection-wiki]: μ•Œκ³ λ¦¬μ¦˜ μž…λ ₯ 리슀트λ₯Ό 두 λΆ€λΆ„μœΌλ‘œ λ‚˜λˆˆλ‹€ : 첫 뢀뢄은 μ•„μ΄ν…œλ“€μ΄ 이미 μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ μ •λ ¬λ˜μ—ˆλ‹€. 그리고 남은 λΆ€λΆ„μ˜ μ•„μ΄ν…œλ“€μ€ λ‚˜λ¨Έμ§€ ν•­λͺ©μ„ μ°¨μ§€ν•˜λŠ” λ¦¬μŠ€νŠΈμ΄λ‹€. μ²˜μŒμ—λŠ” μ •λ ¬λœ λ¦¬μŠ€νŠΈλŠ” 곡백이고 λ‚˜λ¨Έμ§€κ°€ 전뢀이닀. 였λ₯΄μ°¨μˆœ(λ˜λŠ” λ‚΄λ¦Όμ°¨μˆœ) μ•Œκ³ λ¦¬μ¦˜μ€ κ°€μž₯ μž‘μ€ μš”μ†Œλ₯Ό μ •λ ¬λ˜μ§€ μ•Šμ€ λ¦¬μŠ€νŠΈμ—μ„œ μ°Ύκ³  정렬이 μ•ˆλœ κ°€μž₯ μ™Όμͺ½(μ •λ ¬λœ 리슀트) λ¦¬μŠ€νŠΈμ™€ λ°”κΎΌλ‹€. μ΄λ ‡κ²Œ 였λ₯Έμͺ½μœΌλ‘œ λ‚˜μ•„κ°„λ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n^2) -- 졜고의 μ„±λŠ₯ O(n^2) -- 평균 O(n^2) - -###### View the algorithm in [action][selection-toptal] - -### Shell(μ‰˜ μ •λ ¬) - -![alt text][shell-image] - -From [Wikipedia][shell-wiki]: μ‰˜ 정렬은 멀리 λ–¨μ–΄μ Έ μžˆλŠ” ν•­λͺ©μ˜ κ΅ν™˜μ„ ν—ˆμš©ν•˜λŠ” μ‚½μž… μ’…λ₯˜μ˜ μΌλ°˜ν™”μ΄λ‹€. κ·Έ μ•„μ΄λ””μ–΄λŠ” λͺ¨λ“  n번째 μš”μ†Œκ°€ μ •λ ¬λœ λͺ©λ‘μ„ μ œκ³΅ν•œλ‹€λŠ” 것을 κ³ λ €ν•˜μ—¬ μ–΄λŠ κ³³μ—μ„œλ“ μ§€ μ‹œμž‘ν•˜λ„λ‘ μš”μ†Œμ˜ λͺ©λ‘μ„ λ°°μ—΄ν•˜λŠ” 것이닀. μ΄λŸ¬ν•œ λͺ©λ‘μ€ h-sorted둜 μ•Œλ €μ Έ μžˆλ‹€. λ§ˆμ°¬κ°€μ§€λ‘œ, 각각 κ°œλ³„μ μœΌλ‘œ μ •λ ¬λœ h μΈν„°λ¦¬λΈŒ λͺ©λ‘μœΌλ‘œ κ°„μ£Όν•  수 μžˆλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(nlog2 2n) -- 졜고의 μ„±λŠ₯ O(n log n) -- Average case performance depends on gap sequence - -###### View the algorithm in [action][shell-toptal] - -### μ‹œκ°„ λ³΅μž‘μ„± κ·Έλž˜ν”„ - -μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μ˜ λ³΅μž‘μ„± 비ꡐ (버블 μ •λ ¬, μ‚½μž… μ •λ ¬, 선택 μ •λ ¬) - -[λ³΅μž‘μ„± κ·Έλž˜ν”„](https://github.com/prateekiiest/Python/blob/master/sorts/sortinggraphs.png) - ---- - -## 검색 μ•Œκ³ λ¦¬μ¦˜ - -### Linear (μ„ ν˜• 탐색) - -![alt text][linear-image] - -From [Wikipedia][linear-wiki]: μ„ ν˜• 탐색 λ˜λŠ” 순차 탐색은 λͺ©λ‘ λ‚΄μ—μ„œ λͺ©ν‘œκ°’을 μ°ΎλŠ” 방법이닀. 일치 ν•­λͺ©μ΄ λ°œκ²¬λ˜κ±°λ‚˜ λͺ¨λ“  μš”μ†Œκ°€ 탐색될 λ•ŒκΉŒμ§€ λͺ©λ‘μ˜ 각 μš”μ†Œμ— λŒ€ν•΄ λͺ©ν‘œκ°’을 순차적으둜 κ²€μ‚¬ν•œλ‹€. -μ„ ν˜• 검색은 μ΅œμ•…μ˜ μ„ ν˜• μ‹œκ°„μœΌλ‘œ μ‹€ν–‰λ˜λ©° μ΅œλŒ€ n개의 λΉ„κ΅μ—μ„œ 이루어진닀. μ—¬κΈ°μ„œ n은 λͺ©λ‘μ˜ 길이닀. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(n) -- 졜고의 μ„±λŠ₯ O(1) -- 평균 O(n) -- μ΅œμ•…μ˜ 경우 곡간 λ³΅μž‘μ„± O(1) iterative - -### Binary (이진 탐색) - -![alt text][binary-image] - -From [Wikipedia][binary-wiki]: 이진 탐색, (also known as half-interval search or logarithmic search), 은 μ •λ ¬λœ λ°°μ—΄ λ‚΄μ—μ„œ λͺ©ν‘œκ°’μ˜ μœ„μΉ˜λ₯Ό μ°ΎλŠ” 검색 μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. λͺ©ν‘œκ°’을 λ°°μ—΄μ˜ 쀑간 μš”μ†Œμ™€ λΉ„κ΅ν•œλ‹€; λ§Œμ•½ λͺ©ν‘œκ°’이 λ™μΌν•˜μ§€ μ•ŠμœΌλ©΄, λͺ©ν‘œλ¬Όμ˜ 절반이 제거되고 검색이 성곡할 λ•ŒκΉŒμ§€ λ‚˜λ¨Έμ§€ μ ˆλ°˜μ—μ„œ μ†λœλ‹€. - -**속성** - -- μ΅œμ•…μ˜ μ„±λŠ₯ O(log n) -- 졜고의 μ„±λŠ₯ O(1) -- 평균 O(log n) -- μ΅œμ•…μ˜ 경우 곡간 λ³΅μž‘μ„± O(1) - -[bubble-toptal]: https://www.toptal.com/developers/sorting-algorithms/bubble-sort -[bubble-wiki]: https://en.wikipedia.org/wiki/Bubble_sort -[bubble-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Bubblesort-edited-color.svg/220px-Bubblesort-edited-color.svg.png "Bubble Sort" -[insertion-toptal]: https://www.toptal.com/developers/sorting-algorithms/insertion-sort -[insertion-wiki]: https://en.wikipedia.org/wiki/Insertion_sort -[insertion-image]: https://upload.wikimedia.org/wikipedia/commons/7/7e/Insertionsort-edited.png "Insertion Sort" -[quick-toptal]: https://www.toptal.com/developers/sorting-algorithms/quick-sort -[quick-wiki]: https://en.wikipedia.org/wiki/Quicksort -[quick-image]: https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif "Quick Sort" -[merge-toptal]: https://www.toptal.com/developers/sorting-algorithms/merge-sort -[merge-wiki]: https://en.wikipedia.org/wiki/Merge_sort -[merge-image]: https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif "Merge Sort" -[selection-toptal]: https://www.toptal.com/developers/sorting-algorithms/selection-sort -[selection-wiki]: https://en.wikipedia.org/wiki/Selection_sort -[selection-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Selection_sort_animation.gif/250px-Selection_sort_animation.gif "Selection Sort Sort" -[shell-toptal]: https://www.toptal.com/developers/sorting-algorithms/shell-sort -[shell-wiki]: https://en.wikipedia.org/wiki/Shellsort -[shell-image]: https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif "Shell Sort" -[linear-wiki]: https://en.wikipedia.org/wiki/Linear_search -[linear-image]: http://www.tutorialspoint.com/data_structures_algorithms/images/linear_search.gif -[binary-wiki]: https://en.wikipedia.org/wiki/Binary_search_algorithm -[binary-image]: https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_search_into_array.png - ---- - -## λ‚˜λ¨Έμ§€ μ•Œκ³ λ¦¬μ¦˜μ— λŒ€ν•œ 링크 - -| μ „ν™˜ | λ‹€μ΄λ‚˜λ―Ήν”„λ‘œκ·Έλž˜λ°(DP) | μ•”ν˜Έ | κ·Έ μ™Έ 것듀 | -| --------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------ | -| [Any Base to Any Base](Conversions/AnyBaseToAnyBase.java) | [Coin Change](DynamicProgramming/CoinChange.java) | [Caesar](Ciphers/Caesar.java) | [Heap Sort](Sorts/HeapSort.java) | -| [Any Base to Decimal](Conversions/AnyBaseToDecimal.java) | [Egg Dropping](DynamicProgramming/EggDropping.java) | [Columnar Transposition Cipher](Ciphers/ColumnarTranspositionCipher.java) | [Palindromic Prime Checker](Misc/PalindromePrime.java) | -| [Binary to Decimal](Conversions/BinaryToDecimal.java) | [Fibonacci](DynamicProgramming/Fibonacci.java) | [RSA](Ciphers/RSA.java) | More soon... | -| [Binary to HexaDecimal](Conversions/BinaryToHexadecimal.java) | [Kadane Algorithm](DynamicProgramming/KadaneAlgorithm.java) | more coming soon... | -| [Binary to Octal](Conversions/BinaryToOctal.java) | [Knapsack](DynamicProgramming/Knapsack.java) | -| [Decimal To Any Base](Conversions/DecimalToAnyBase.java) | [Longest Common Subsequence](DynamicProgramming/LongestCommonSubsequence.java) | -| [Decimal To Binary](Conversions/DecimalToBinary.java) | [Longest Increasing Subsequence](DynamicProgramming/LongestIncreasingSubsequence.java) | -| [Decimal To Hexadecimal](Conversions/DecimalToHexaDecimal.java) | [Rod Cutting](DynamicProgramming/RodCutting.java) | -| and much more... | and more... | - -### 자료 ꡬ쑰 - -| κ·Έλž˜ν”„ | νž™ | 리슀트 | 큐 | -| ------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------- | -| | [빈 νž™ μ˜ˆμ™Έμ²˜λ¦¬](DataStructures/Heaps/EmptyHeapException.java) | [μ›ν˜• μ—°κ²°λ¦¬μŠ€νŠΈ](DataStructures/Lists/CircleLinkedList.java) | [μ œλ„ˆλ¦­ μ–΄λ ˆμ΄ 리슀트 큐](DataStructures/Queues/GenericArrayListQueue.java) | -| | [νž™](DataStructures/Heaps/Heap.java) | [이쀑 μ—°κ²°λ¦¬μŠ€νŠΈ](DataStructures/Lists/DoublyLinkedList.java) | [큐](DataStructures/Queues/Queues.java) | -| [κ·Έλž˜ν”„](DataStructures/Graphs/Graphs.java) | [νž™ μš”μ†Œ](DataStructures/Heaps/HeapElement.java) | [λ‹¨μˆœ μ—°κ²°λ¦¬μŠ€νŠΈ](DataStructures/Lists/SinglyLinkedList.java) | -| [크루슀칼 μ•Œκ³ λ¦¬μ¦˜](DataStructures/Graphs/Kruskal.java) | [μ΅œλŒ€νž™](DataStructures/Heaps/MaxHeap.java) | -| [ν–‰λ ¬ κ·Έλž˜ν”„](DataStructures/Graphs/MatrixGraphs.java) | [μ΅œμ†Œνž™](DataStructures/Heaps/MinHeap.java) | -| [ν”„λ¦Ό μ΅œμ†Œμ‹ μž₯트리](DataStructures/Graphs/PrimMST.java) | - -| μŠ€νƒ | 트리 | -| --------------------------------------------------------------- | ------------------------------------------------- | -| [λ…Έλ“œ μŠ€νƒ](DataStructures/Stacks/NodeStack.java) | [AVL 트리](DataStructures/Trees/AVLTree.java) | -| [μ—°κ²°λ¦¬μŠ€νŠΈ μŠ€νƒ](DataStructures/Stacks/StackOfLinkedList.java) | [이진 트리](DataStructures/Trees/BinaryTree.java) | -| [μŠ€νƒ](DataStructures/Stacks) | And much more... | - -- [Bags](DataStructures/Bags/Bag.java) -- [Buffer](DataStructures/Buffers/CircularBuffer.java) -- [HashMap](DataStructures/HashMap/Hashing/HashMap.java) -- diff --git a/pmd-exclude.properties b/pmd-exclude.properties index a3c95b12fa4b..64562c524728 100644 --- a/pmd-exclude.properties +++ b/pmd-exclude.properties @@ -53,14 +53,11 @@ com.thealgorithms.maths.Gaussian=UselessParentheses com.thealgorithms.maths.GcdSolutionWrapper=UselessParentheses com.thealgorithms.maths.HeronsFormula=UselessParentheses com.thealgorithms.maths.JugglerSequence=UselessMainMethod -com.thealgorithms.maths.KaprekarNumbers=UselessParentheses com.thealgorithms.maths.KeithNumber=UselessMainMethod,UselessParentheses -com.thealgorithms.maths.LeonardoNumber=UselessParentheses com.thealgorithms.maths.LinearDiophantineEquationsSolver=UselessMainMethod,UselessParentheses com.thealgorithms.maths.MagicSquare=UselessMainMethod com.thealgorithms.maths.PiNilakantha=UselessMainMethod com.thealgorithms.maths.Prime.PrimeCheck=UselessMainMethod -com.thealgorithms.maths.PythagoreanTriple=UselessMainMethod com.thealgorithms.maths.RomanNumeralUtil=UselessParentheses com.thealgorithms.maths.SecondMinMax=UselessParentheses com.thealgorithms.maths.SecondMinMaxTest=UnnecessaryFullyQualifiedName @@ -71,7 +68,6 @@ com.thealgorithms.maths.TrinomialTriangle=UselessMainMethod,UselessParentheses com.thealgorithms.maths.VectorCrossProduct=UselessMainMethod com.thealgorithms.maths.Volume=UselessParentheses com.thealgorithms.matrix.RotateMatrixBy90Degrees=UselessMainMethod -com.thealgorithms.misc.Sparsity=UselessParentheses com.thealgorithms.others.BankersAlgorithm=UselessMainMethod com.thealgorithms.others.BrianKernighanAlgorithm=UselessMainMethod com.thealgorithms.others.CRC16=UselessMainMethod,UselessParentheses @@ -79,11 +75,9 @@ com.thealgorithms.others.CRC32=UselessMainMethod com.thealgorithms.others.Damm=UnnecessaryFullyQualifiedName,UselessMainMethod com.thealgorithms.others.Dijkstra=UselessMainMethod com.thealgorithms.others.GaussLegendre=UselessMainMethod -com.thealgorithms.others.HappyNumbersSeq=UselessMainMethod com.thealgorithms.others.Huffman=UselessMainMethod com.thealgorithms.others.InsertDeleteInArray=UselessMainMethod com.thealgorithms.others.KochSnowflake=UselessMainMethod -com.thealgorithms.others.Krishnamurthy=UselessMainMethod com.thealgorithms.others.LinearCongruentialGenerator=UselessMainMethod com.thealgorithms.others.Luhn=UnnecessaryFullyQualifiedName,UselessMainMethod com.thealgorithms.others.Mandelbrot=UselessMainMethod,UselessParentheses @@ -94,7 +88,6 @@ com.thealgorithms.others.PerlinNoise=UselessMainMethod,UselessParentheses com.thealgorithms.others.QueueUsingTwoStacks=UselessParentheses com.thealgorithms.others.Trieac=UselessMainMethod,UselessParentheses com.thealgorithms.others.Verhoeff=UnnecessaryFullyQualifiedName,UselessMainMethod -com.thealgorithms.puzzlesandgames.Sudoku=UselessMainMethod com.thealgorithms.recursion.DiceThrower=UselessMainMethod com.thealgorithms.searches.HowManyTimesRotated=UselessMainMethod com.thealgorithms.searches.InterpolationSearch=UselessParentheses @@ -108,15 +101,11 @@ com.thealgorithms.sorts.MergeSortNoExtraSpace=UselessParentheses com.thealgorithms.sorts.RadixSort=UselessParentheses com.thealgorithms.sorts.TreeSort=UselessMainMethod com.thealgorithms.sorts.WiggleSort=UselessParentheses -com.thealgorithms.stacks.LargestRectangle=UselessMainMethod com.thealgorithms.stacks.MaximumMinimumWindow=UselessMainMethod com.thealgorithms.stacks.PostfixToInfix=UselessParentheses -com.thealgorithms.strings.Alphabetical=UselessMainMethod com.thealgorithms.strings.HorspoolSearch=UnnecessaryFullyQualifiedName,UselessParentheses -com.thealgorithms.strings.KMP=UselessMainMethod com.thealgorithms.strings.Lower=UselessMainMethod com.thealgorithms.strings.Palindrome=UselessParentheses com.thealgorithms.strings.Pangram=UselessMainMethod -com.thealgorithms.strings.RabinKarp=UselessMainMethod com.thealgorithms.strings.Rotation=UselessMainMethod com.thealgorithms.strings.Upper=UselessMainMethod diff --git a/pom.xml b/pom.xml index a685e334460c..dab7447430e5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ UTF-8 21 21 - 3.27.6 + 3.27.7 @@ -20,7 +20,7 @@ org.junit junit-bom - 6.0.2 + 6.0.3 pom import @@ -42,7 +42,7 @@ org.mockito mockito-core - 5.21.0 + 5.23.0 test @@ -61,7 +61,7 @@ maven-surefire-plugin - 3.5.4 + 3.5.5 @@ -69,7 +69,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.15.0 21 @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 13.0.0 + 13.3.0 @@ -127,7 +127,7 @@ com.mebigfatguy.fb-contrib fb-contrib - 7.7.3 + 7.7.4 com.h3xstream.findsecbugs diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 3e2f1ff84ca8..8c42802520e3 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -8,18 +8,9 @@ - - - - - - - - - @@ -32,15 +23,9 @@ - - - - - - @@ -50,15 +35,9 @@ - - - - - - @@ -71,9 +50,6 @@ - - - @@ -83,9 +59,6 @@ - - - @@ -114,12 +87,6 @@ - - - - - - @@ -129,9 +96,6 @@ - - - @@ -150,9 +114,6 @@ - - - @@ -189,15 +150,9 @@ - - - - - - @@ -205,7 +160,10 @@ - + + + + diff --git a/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java b/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java index f8cd0c40c20e..d05e33a4242f 100644 --- a/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java +++ b/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java @@ -20,8 +20,8 @@ private ArrayCombination() { * @throws IllegalArgumentException if n or k are negative, or if k is greater than n. */ public static List> combination(int n, int k) { - if (n < 0 || k < 0 || k > n) { - throw new IllegalArgumentException("Invalid input: n must be non-negative, k must be non-negative and less than or equal to n."); + if (k < 0 || k > n) { + throw new IllegalArgumentException("Invalid input: 0 ≀ k ≀ n is required."); } List> combinations = new ArrayList<>(); @@ -48,7 +48,7 @@ private static void combine(List> combinations, List curr for (int i = start; i < n; i++) { current.add(i); combine(combinations, current, i + 1, n, k); - current.remove(current.size() - 1); // Backtrack + current.removeLast(); // Backtrack } } } diff --git a/src/main/java/com/thealgorithms/backtracking/WordSearch.java b/src/main/java/com/thealgorithms/backtracking/WordSearch.java index 174ca90ccaab..452f17b6ace6 100644 --- a/src/main/java/com/thealgorithms/backtracking/WordSearch.java +++ b/src/main/java/com/thealgorithms/backtracking/WordSearch.java @@ -35,22 +35,6 @@ * - Stack space for the recursive DFS function, where L is the maximum depth of recursion (length of the word). */ public class WordSearch { - private final int[] dx = {0, 0, 1, -1}; - private final int[] dy = {1, -1, 0, 0}; - private boolean[][] visited; - private char[][] board; - private String word; - - /** - * Checks if the given (x, y) coordinates are valid positions in the board. - * - * @param x The row index. - * @param y The column index. - * @return True if the coordinates are within the bounds of the board; false otherwise. - */ - private boolean isValid(int x, int y) { - return x >= 0 && x < board.length && y >= 0 && y < board[0].length; - } /** * Performs Depth First Search (DFS) from the cell (x, y) @@ -58,28 +42,27 @@ private boolean isValid(int x, int y) { * * @param x The current row index. * @param y The current column index. - * @param nextIdx The index of the next character in the word to be matched. + * @param idx The index of the next character in the word to be matched. * @return True if a valid path is found to match the remaining characters of the word; false otherwise. */ - private boolean doDFS(int x, int y, int nextIdx) { - visited[x][y] = true; - if (nextIdx == word.length()) { + + private boolean dfs(char[][] board, int x, int y, String word, int idx) { + if (idx == word.length()) { return true; } - for (int i = 0; i < 4; ++i) { - int xi = x + dx[i]; - int yi = y + dy[i]; - if (isValid(xi, yi) && board[xi][yi] == word.charAt(nextIdx) && !visited[xi][yi]) { - boolean exists = doDFS(xi, yi, nextIdx + 1); - if (exists) { - return true; - } - } + if (x < 0 || y < 0 || x >= board.length || y >= board[0].length || board[x][y] != word.charAt(idx)) { + return false; } - visited[x][y] = false; // Backtrack - return false; + char temp = board[x][y]; + board[x][y] = '#'; + + boolean found = dfs(board, x + 1, y, word, idx + 1) || dfs(board, x - 1, y, word, idx + 1) || dfs(board, x, y + 1, word, idx + 1) || dfs(board, x, y - 1, word, idx + 1); + + board[x][y] = temp; + + return found; } /** @@ -90,20 +73,21 @@ private boolean doDFS(int x, int y, int nextIdx) { * @param word The target word to search for in the board. * @return True if the word exists in the board; false otherwise. */ + public boolean exist(char[][] board, String word) { - this.board = board; - this.word = word; - for (int i = 0; i < board.length; ++i) { - for (int j = 0; j < board[0].length; ++j) { - if (board[i][j] == word.charAt(0)) { - visited = new boolean[board.length][board[0].length]; - boolean exists = doDFS(i, j, 1); - if (exists) { - return true; - } + + int m = board.length; + int n = board[0].length; + + // DFS search + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == word.charAt(0) && dfs(board, i, j, word, 0)) { + return true; } } } + return false; } } diff --git a/src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java b/src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java new file mode 100644 index 000000000000..6383caa59b1f --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java @@ -0,0 +1,174 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * ElGamal Encryption Algorithm Implementation. + * + *

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

+ * + *

+ * Key Features: + *

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

+ * + * @author Chahat Sandhu, singhc7 + * @see ElGamal Encryption (Wikipedia) + * @see Safe Primes + */ +public final class ElGamalCipher { + + private static final SecureRandom RANDOM = new SecureRandom(); + private static final int PRIME_CERTAINTY = 40; + private static final int MIN_BIT_LENGTH = 256; + + private ElGamalCipher() { + } + + /** + * A container for the Public and Private keys. + * + * @param p The prime modulus. + * @param g The generator (primitive root). + * @param y The public key component (g^x mod p). + * @param x The private key. + */ + public record KeyPair(BigInteger p, BigInteger g, BigInteger y, BigInteger x) { + } + + /** + * Container for the encryption result. + * + * @param a The first component (g^k mod p). + * @param b The second component (y^k * m mod p). + */ + public record CipherText(BigInteger a, BigInteger b) { + } + + /** + * Generates a valid ElGamal KeyPair using a Safe Prime. + * + * @param bitLength The bit length of the prime modulus p. Must be at least 256. + * @return A valid KeyPair (p, g, y, x). + * @throws IllegalArgumentException if bitLength is too small. + */ + public static KeyPair generateKeys(int bitLength) { + if (bitLength < MIN_BIT_LENGTH) { + throw new IllegalArgumentException("Bit length must be at least " + MIN_BIT_LENGTH + " for security."); + } + + BigInteger p; + BigInteger q; + BigInteger g; + BigInteger x; + BigInteger y; + + // Generate Safe Prime p = 2q + 1 + do { + q = new BigInteger(bitLength - 1, PRIME_CERTAINTY, RANDOM); + p = q.multiply(BigInteger.TWO).add(BigInteger.ONE); + } while (!p.isProbablePrime(PRIME_CERTAINTY)); + + // Find a Generator g (Primitive Root modulo p) + do { + g = new BigInteger(bitLength, RANDOM).mod(p.subtract(BigInteger.TWO)).add(BigInteger.TWO); + } while (!isValidGenerator(g, p, q)); + + // Generate Private Key x in range [2, p-2] + do { + x = new BigInteger(bitLength, RANDOM); + } while (x.compareTo(BigInteger.TWO) < 0 || x.compareTo(p.subtract(BigInteger.TWO)) > 0); + + // Compute Public Key y = g^x mod p + y = g.modPow(x, p); + + return new KeyPair(p, g, y, x); + } + + /** + * Encrypts a message using the public key. + * + * @param message The message converted to BigInteger. + * @param p The prime modulus. + * @param g The generator. + * @param y The public key component. + * @return The CipherText pair (a, b). + * @throws IllegalArgumentException if inputs are null, negative, or message >= p. + */ + public static CipherText encrypt(BigInteger message, BigInteger p, BigInteger g, BigInteger y) { + if (message == null || p == null || g == null || y == null) { + throw new IllegalArgumentException("Inputs cannot be null."); + } + if (message.compareTo(BigInteger.ZERO) < 0) { + throw new IllegalArgumentException("Message must be non-negative."); + } + if (message.compareTo(p) >= 0) { + throw new IllegalArgumentException("Message must be smaller than the prime modulus p."); + } + + BigInteger k; + BigInteger pMinus1 = p.subtract(BigInteger.ONE); + + // Select ephemeral key k such that 1 < k < p-1 and gcd(k, p-1) = 1 + do { + k = new BigInteger(p.bitLength(), RANDOM); + } while (k.compareTo(BigInteger.ONE) <= 0 || k.compareTo(pMinus1) >= 0 || !k.gcd(pMinus1).equals(BigInteger.ONE)); + + BigInteger a = g.modPow(k, p); + BigInteger b = y.modPow(k, p).multiply(message).mod(p); + + return new CipherText(a, b); + } + + /** + * Decrypts a ciphertext using the private key. + * + * @param cipher The CipherText (a, b). + * @param x The private key. + * @param p The prime modulus. + * @return The decrypted message as BigInteger. + * @throws IllegalArgumentException if inputs are null. + */ + public static BigInteger decrypt(CipherText cipher, BigInteger x, BigInteger p) { + if (cipher == null || x == null || p == null) { + throw new IllegalArgumentException("Inputs cannot be null."); + } + + BigInteger a = cipher.a(); + BigInteger b = cipher.b(); + + BigInteger s = a.modPow(x, p); + BigInteger sInverse = s.modInverse(p); + + return b.multiply(sInverse).mod(p); + } + + /** + * Verifies if g is a valid generator for safe prime p = 2q + 1. + * + * @param g The candidate generator. + * @param p The safe prime. + * @param q The Sophie Germain prime (p-1)/2. + * @return True if g is a primitive root, False otherwise. + */ + private static boolean isValidGenerator(BigInteger g, BigInteger p, BigInteger q) { + // Fix: Must use braces {} for all if statements + if (g.equals(BigInteger.ONE)) { + return false; + } + if (g.modPow(BigInteger.TWO, p).equals(BigInteger.ONE)) { + return false; + } + return !g.modPow(q, p).equals(BigInteger.ONE); + } +} diff --git a/src/main/java/com/thealgorithms/compression/HuffmanCoding.java b/src/main/java/com/thealgorithms/compression/HuffmanCoding.java new file mode 100644 index 000000000000..d7f9d58d2429 --- /dev/null +++ b/src/main/java/com/thealgorithms/compression/HuffmanCoding.java @@ -0,0 +1,253 @@ +package com.thealgorithms.compression; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * Huffman Coding Compression Algorithm Implementation. + *

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

+ *

+ * Key Features: + *

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

+ * @author Chahat Sandhu, singhc7 + * @see Huffman Coding (Wikipedia) + */ +public class HuffmanCoding { + + private Node root; + private final Map huffmanCodes; + + /** + * Represents a node within the Huffman Tree. + * Implements {@link Comparable} to allow sorting by frequency in a PriorityQueue. + */ + private static class Node implements Comparable { + final char ch; + final int freq; + final Node left; + final Node right; + + /** + * Constructs a leaf node containing a specific character and its frequency. + * + * @param ch The character stored in this leaf. + * @param freq The frequency of occurrence of the character. + */ + Node(char ch, int freq) { + this.ch = ch; + this.freq = freq; + this.left = null; + this.right = null; + } + + /** + * Constructs an internal node that merges two child nodes. + * The character is defaulted to the null character ('\0'). + * + * @param freq The combined frequency of the left and right child nodes. + * @param left The left child node. + * @param right The right child node. + */ + Node(int freq, Node left, Node right) { + this.ch = '\0'; + this.freq = freq; + this.left = left; + this.right = right; + } + + /** + * Determines if the current node is a leaf (contains no children). + * + * @return {@code true} if both left and right children are null, {@code false} otherwise. + */ + boolean isLeaf() { + return left == null && right == null; + } + + /** + * Compares this node with another node based on their frequencies. + * Used by the PriorityQueue to maintain the min-heap property. + * + * @param other The other Node to compare against. + * @return A negative integer, zero, or a positive integer as this node's frequency + * is less than, equal to, or greater than the specified node's frequency. + */ + @Override + public int compareTo(Node other) { + return Integer.compare(this.freq, other.freq); + } + } + + /** + * Initializes the Huffman Tree and generates immutable prefix-free codes + * based on the character frequencies in the provided text. + * + * @param text The input string used to calculate frequencies and build the optimal tree. + * If null or empty, an empty tree and dictionary are created. + */ + public HuffmanCoding(String text) { + if (text == null || text.isEmpty()) { + this.huffmanCodes = Collections.emptyMap(); + return; + } + + Map tempCodes = new HashMap<>(); + buildTree(text); + generateCodes(root, "", tempCodes); + + if (tempCodes.size() == 1) { + tempCodes.put(root.ch, "0"); + } + + this.huffmanCodes = Collections.unmodifiableMap(tempCodes); + } + + /** + * Computes character frequencies and constructs the Huffman Tree using a min-heap. + * The optimal tree is built by repeatedly extracting the two lowest-frequency nodes + * and merging them until a single root node remains. + * + * @param text The input text to analyze. + */ + private void buildTree(String text) { + Map freqMap = new HashMap<>(); + for (char c : text.toCharArray()) { + freqMap.put(c, freqMap.getOrDefault(c, 0) + 1); + } + + PriorityQueue pq = new PriorityQueue<>(); + for (Map.Entry entry : freqMap.entrySet()) { + pq.add(new Node(entry.getKey(), entry.getValue())); + } + + while (pq.size() > 1) { + Node left = pq.poll(); + Node right = pq.poll(); + pq.add(new Node(left.freq + right.freq, left, right)); + } + + root = pq.poll(); + } + + /** + * Recursively traverses the Huffman Tree to generate prefix-free binary codes. + * Left traversals append a '0' to the code, while right traversals append a '1'. + * + * @param node The current node in the traversal. + * @param code The accumulated binary string for the current path. + * @param map The temporary dictionary to populate with the final character-to-code mappings. + */ + private void generateCodes(Node node, String code, Map map) { + if (node == null) { + return; + } + if (node.isLeaf()) { + map.put(node.ch, code); + return; + } + generateCodes(node.left, code + "0", map); + generateCodes(node.right, code + "1", map); + } + + /** + * Encodes the given plaintext string into a binary string using the generated Huffman dictionary. + * + * @param text The plaintext string to compress. + * @return A string of '0's and '1's representing the compressed data. + * Returns an empty string if the input is null or empty. + * @throws IllegalStateException If attempting to encode when the Huffman tree is empty. + * @throws IllegalArgumentException If the input text contains a character not present + * in the original text used to build the tree. + */ + public String encode(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + if (root == null) { + throw new IllegalStateException("Huffman tree is empty."); + } + + StringBuilder sb = new StringBuilder(); + for (char c : text.toCharArray()) { + if (!huffmanCodes.containsKey(c)) { + throw new IllegalArgumentException(String.format("Character '%c' (U+%04X) not found in Huffman dictionary.", c, (int) c)); + } + sb.append(huffmanCodes.get(c)); + } + return sb.toString(); + } + + /** + * Decodes the given binary string back into the original plaintext using the Huffman Tree. + * Validates the integrity of the binary payload during traversal. + * + * @param encodedText The binary string of '0's and '1's to decompress. + * @return The reconstructed plaintext string. Returns an empty string if the input is null or empty. + * @throws IllegalStateException If attempting to decode when the Huffman tree is empty. + * @throws IllegalArgumentException If the binary string contains characters other than '0' or '1', + * or if the sequence ends abruptly without reaching a leaf node. + */ + public String decode(String encodedText) { + if (encodedText == null || encodedText.isEmpty()) { + return ""; + } + if (root == null) { + throw new IllegalStateException("Huffman tree is empty."); + } + + StringBuilder sb = new StringBuilder(); + + if (root.isLeaf()) { + for (char bit : encodedText.toCharArray()) { + if (bit != '0') { + throw new IllegalArgumentException("Invalid binary sequence for single-character tree."); + } + sb.append(root.ch); + } + return sb.toString(); + } + + Node current = root; + for (char bit : encodedText.toCharArray()) { + if (bit != '0' && bit != '1') { + throw new IllegalArgumentException("Encoded text contains invalid characters: " + bit); + } + + current = (bit == '0') ? current.left : current.right; + + if (current.isLeaf()) { + sb.append(current.ch); + current = root; + } + } + + if (current != root) { + throw new IllegalArgumentException("Malformed encoded string: incomplete sequence ending."); + } + + return sb.toString(); + } + + /** + * Retrieves the generated Huffman dictionary mapping characters to their binary codes. + * + * @return An unmodifiable map containing the character-to-binary-code mappings to prevent + * external mutation of the algorithm's state. + */ + public Map getHuffmanCodes() { + return huffmanCodes; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java b/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java index 7a9448fd8fe7..7698cc832981 100644 --- a/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java +++ b/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java @@ -1,3 +1,10 @@ +/** + * [Brief description of what the algorithm does] + *

+ * Time Complexity: O(n) [or appropriate complexity] + * Space Complexity: O(n) + * * @author Reshma Kakkirala + */ package com.thealgorithms.conversions; import java.util.Arrays; diff --git a/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java b/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java index cd5dc580b694..dbdb2d806209 100644 --- a/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java +++ b/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java @@ -63,7 +63,8 @@ public void add(final E element) { * * @param index the index at which the element is to be placed * @param element the element to be inserted at the specified index - * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the number of elements + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or + * equal to the number of elements */ public void put(final int index, E element) { if (index < 0) { @@ -82,7 +83,8 @@ public void put(final int index, E element) { * * @param index the index of the element to retrieve * @return the element at the specified index - * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the current size + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or + * equal to the current size */ @SuppressWarnings("unchecked") public E get(final int index) { @@ -97,7 +99,8 @@ public E get(final int index) { * * @param index the index of the element to be removed * @return the element that was removed from the array - * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the current size + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or + * equal to the current size */ public E remove(final int index) { if (index < 0 || index >= size) { @@ -127,6 +130,21 @@ public boolean isEmpty() { return size == 0; } + /** + * Checks whether the array contains the specified element. + * + * @param element the element to check for + * @return true if the array contains the specified element, false otherwise + */ + public boolean contains(final E element) { + for (int i = 0; i < size; i++) { + if (Objects.equals(elements[i], element)) { + return true; + } + } + return false; + } + /** * Returns a sequential stream with this collection as its source. * @@ -137,7 +155,8 @@ public Stream stream() { } /** - * Ensures that the array has enough capacity to hold the specified number of elements. + * Ensures that the array has enough capacity to hold the specified number of + * elements. * * @param minCapacity the minimum capacity required */ @@ -150,7 +169,8 @@ private void ensureCapacity(int minCapacity) { /** * Removes the element at the specified index without resizing the array. - * This method shifts any subsequent elements to the left and clears the last element. + * This method shifts any subsequent elements to the left and clears the last + * element. * * @param index the index of the element to remove */ @@ -163,7 +183,8 @@ private void fastRemove(int index) { } /** - * Returns a string representation of the array, including only the elements that are currently stored. + * Returns a string representation of the array, including only the elements + * that are currently stored. * * @return a string containing the elements in the array */ @@ -227,7 +248,9 @@ public E next() { /** * Removes the last element returned by this iterator. * - * @throws IllegalStateException if the next method has not yet been called, or the remove method has already been called after the last call to the next method + * @throws IllegalStateException if the next method has not yet been called, or + * the remove method has already been called after + * the last call to the next method */ @Override public void remove() { @@ -242,7 +265,8 @@ public void remove() { /** * Checks for concurrent modifications to the array during iteration. * - * @throws ConcurrentModificationException if the array has been modified structurally + * @throws ConcurrentModificationException if the array has been modified + * structurally */ private void checkForComodification() { if (modCount != expectedModCount) { @@ -251,7 +275,8 @@ private void checkForComodification() { } /** - * Performs the given action for each remaining element in the iterator until all elements have been processed. + * Performs the given action for each remaining element in the iterator until + * all elements have been processed. * * @param action the action to be performed for each element * @throws NullPointerException if the specified action is null diff --git a/src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java new file mode 100644 index 000000000000..0ee788db2ff9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedList.java @@ -0,0 +1,46 @@ +package com.thealgorithms.datastructures.lists; + +/** + * Returns the middle node of a singly linked list using the two-pointer technique. + * + *

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

+ * + *

This method does not modify the input list.

+ * + *

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

+ * + *

Complexity:

+ *
    + *
  • Time: {@code O(n)}
  • + *
  • Space: {@code O(1)}
  • + *
+ */ +public final class MiddleOfLinkedList { + + private MiddleOfLinkedList() { + } + + /** + * Returns the middle node of the list. + * + * @param head the head of the singly linked list; may be {@code null} + * @return the middle node (second middle for even-sized lists), or {@code null} if {@code head} is {@code null} + */ + public static SinglyLinkedListNode middleNode(final SinglyLinkedListNode head) { + if (head == null) { + return null; + } + + SinglyLinkedListNode slow = head; + SinglyLinkedListNode fast = head; + + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + return slow; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java b/src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java new file mode 100644 index 000000000000..79275dcefe20 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursion.java @@ -0,0 +1,28 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.Queue; + +/** + * Reverse a queue using recursion. + */ +public final class ReverseQueueRecursion { + private ReverseQueueRecursion() { + // private constructor to prevent instantiation + } + + /** + * Reverses the given queue recursively. + * + * @param queue the queue to reverse + * @param the type of elements in the queue + */ + public static void reverseQueue(final Queue queue) { + if (queue == null || queue.isEmpty()) { + return; + } + + final T front = queue.poll(); + reverseQueue(queue); + queue.add(front); + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java index 4c9c40c83174..323098a99887 100644 --- a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java +++ b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java @@ -66,10 +66,6 @@ public static class Location { } } - public Location[] createLocation(int numberValues) { - return new Location[numberValues]; - } - public Location buildLocation(double x, double y) { return new Location(x, y); } diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteValue.java b/src/main/java/com/thealgorithms/maths/AbsoluteValue.java index b9279d5a244a..114eb71b1015 100644 --- a/src/main/java/com/thealgorithms/maths/AbsoluteValue.java +++ b/src/main/java/com/thealgorithms/maths/AbsoluteValue.java @@ -11,6 +11,6 @@ private AbsoluteValue() { * @return The absolute value of the {@code number} */ public static int getAbsValue(int number) { - return number < 0 ? -number : number; + return Math.abs(number); } } diff --git a/src/main/java/com/thealgorithms/maths/Area.java b/src/main/java/com/thealgorithms/maths/Area.java index 1eba6666dde3..84fc67159379 100644 --- a/src/main/java/com/thealgorithms/maths/Area.java +++ b/src/main/java/com/thealgorithms/maths/Area.java @@ -10,17 +10,17 @@ private Area() { /** * String of IllegalArgumentException for radius */ - private static final String POSITIVE_RADIUS = "Must be a positive radius"; + private static final String POSITIVE_RADIUS = "Radius must be greater than 0"; /** * String of IllegalArgumentException for height */ - private static final String POSITIVE_HEIGHT = "Must be a positive height"; + private static final String POSITIVE_HEIGHT = "Height must be greater than 0"; /** * String of IllegalArgumentException for base */ - private static final String POSITIVE_BASE = "Must be a positive base"; + private static final String POSITIVE_BASE = "Base must be greater than 0"; /** * Calculate the surface area of a cube. @@ -30,11 +30,32 @@ private Area() { */ public static double surfaceAreaCube(final double sideLength) { if (sideLength <= 0) { - throw new IllegalArgumentException("Must be a positive sideLength"); + throw new IllegalArgumentException("Side length must be greater than 0"); } return 6 * sideLength * sideLength; } + /** + * Calculate the surface area of a cuboid. + * + * @param length length of the cuboid + * @param width width of the cuboid + * @param height height of the cuboid + * @return surface area of given cuboid + */ + public static double surfaceAreaCuboid(final double length, double width, double height) { + if (length <= 0) { + throw new IllegalArgumentException("Length must be greater than 0"); + } + if (width <= 0) { + throw new IllegalArgumentException("Width must be greater than 0"); + } + if (height <= 0) { + throw new IllegalArgumentException("Height must be greater than 0"); + } + return 2 * (length * width + length * height + width * height); + } + /** * Calculate the surface area of a sphere. * @@ -57,10 +78,10 @@ public static double surfaceAreaSphere(final double radius) { */ public static double surfaceAreaPyramid(final double sideLength, final double slantHeight) { if (sideLength <= 0) { - throw new IllegalArgumentException("Must be a positive sideLength"); + throw new IllegalArgumentException(""); } if (slantHeight <= 0) { - throw new IllegalArgumentException("Must be a positive slantHeight"); + throw new IllegalArgumentException("slant height must be greater than 0"); } double baseArea = sideLength * sideLength; double lateralSurfaceArea = 2 * sideLength * slantHeight; @@ -76,10 +97,10 @@ public static double surfaceAreaPyramid(final double sideLength, final double sl */ public static double surfaceAreaRectangle(final double length, final double width) { if (length <= 0) { - throw new IllegalArgumentException("Must be a positive length"); + throw new IllegalArgumentException("Length must be greater than 0"); } if (width <= 0) { - throw new IllegalArgumentException("Must be a positive width"); + throw new IllegalArgumentException("Width must be greater than 0"); } return length * width; } @@ -109,7 +130,7 @@ public static double surfaceAreaCylinder(final double radius, final double heigh */ public static double surfaceAreaSquare(final double sideLength) { if (sideLength <= 0) { - throw new IllegalArgumentException("Must be a positive sideLength"); + throw new IllegalArgumentException("Side Length must be greater than 0"); } return sideLength * sideLength; } @@ -121,14 +142,14 @@ public static double surfaceAreaSquare(final double sideLength) { * @param height height of triangle * @return area of given triangle */ - public static double surfaceAreaTriangle(final double base, final double height) { - if (base <= 0) { + public static double surfaceAreaTriangle(final double baseLength, final double height) { + if (baseLength <= 0) { throw new IllegalArgumentException(POSITIVE_BASE); } if (height <= 0) { throw new IllegalArgumentException(POSITIVE_HEIGHT); } - return base * height / 2; + return baseLength * height / 2; } /** @@ -138,14 +159,14 @@ public static double surfaceAreaTriangle(final double base, final double height) * @param height height of a parallelogram * @return area of given parallelogram */ - public static double surfaceAreaParallelogram(final double base, final double height) { - if (base <= 0) { + public static double surfaceAreaParallelogram(final double baseLength, final double height) { + if (baseLength <= 0) { throw new IllegalArgumentException(POSITIVE_BASE); } if (height <= 0) { throw new IllegalArgumentException(POSITIVE_HEIGHT); } - return base * height; + return baseLength * height; } /** @@ -156,17 +177,17 @@ public static double surfaceAreaParallelogram(final double base, final double he * @param height height of trapezium * @return area of given trapezium */ - public static double surfaceAreaTrapezium(final double base1, final double base2, final double height) { - if (base1 <= 0) { + public static double surfaceAreaTrapezium(final double baseLength1, final double baseLength2, final double height) { + if (baseLength1 <= 0) { throw new IllegalArgumentException(POSITIVE_BASE + 1); } - if (base2 <= 0) { + if (baseLength2 <= 0) { throw new IllegalArgumentException(POSITIVE_BASE + 2); } if (height <= 0) { throw new IllegalArgumentException(POSITIVE_HEIGHT); } - return (base1 + base2) * height / 2; + return (baseLength1 + baseLength2) * height / 2; } /** diff --git a/src/main/java/com/thealgorithms/maths/BellNumbers.java b/src/main/java/com/thealgorithms/maths/BellNumbers.java new file mode 100644 index 000000000000..d4dc1014f48b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/BellNumbers.java @@ -0,0 +1,59 @@ +package com.thealgorithms.maths; + +/** + * The Bell numbers count the number of partitions of a set. + * The n-th Bell number is the number of ways a set of n elements can be partitioned + * into nonempty subsets. + * + *

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

+ * + * @author Chahat Sandhu, singhc7 + * @see Bell Number (Wikipedia) + */ +public final class BellNumbers { + + private BellNumbers() { + } + + /** + * Calculates the n-th Bell number using the Bell Triangle. + * + * @param n the index of the Bell number (must be non-negative) + * @return the n-th Bell number + * @throws IllegalArgumentException if n is negative or n > 25 + */ + public static long compute(int n) { + if (n < 0) { + throw new IllegalArgumentException("n must be non-negative"); + } + if (n == 0) { + return 1; + } + if (n > 25) { + throw new IllegalArgumentException("n must be <= 25. For larger n, use BigInteger implementation."); + } + + // We use a 2D array to visualize the Bell Triangle + long[][] bellTriangle = new long[n + 1][n + 1]; + + // Base case: The triangle starts with 1 + bellTriangle[0][0] = 1; + + for (int i = 1; i <= n; i++) { + // Rule 1: The first number in a new row is the LAST number of the previous row + bellTriangle[i][0] = bellTriangle[i - 1][i - 1]; + + // Rule 2: Fill the rest of the row by adding the previous neighbor and the upper-left neighbor + for (int j = 1; j <= i; j++) { + bellTriangle[i][j] = bellTriangle[i][j - 1] + bellTriangle[i - 1][j - 1]; + } + } + + // The Bell number B_n is the first number in the n-th row + return bellTriangle[n][0]; + } +} diff --git a/src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java b/src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java new file mode 100644 index 000000000000..4b68b7824574 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ComplexNumberMultiply.java @@ -0,0 +1,32 @@ +package com.thealgorithms.maths; + +/** + * Multiplies two complex numbers represented as strings in the form "a+bi". + * Supports negative values and validates input format. + */ +public final class ComplexNumberMultiply { + + private ComplexNumberMultiply() { + } + + private static int[] parse(String num) { + if (num == null || !num.matches("-?\\d+\\+-?\\d+i")) { + throw new IllegalArgumentException("Invalid complex number format: " + num); + } + + String[] parts = num.split("\\+"); + int real = Integer.parseInt(parts[0]); + int imaginary = Integer.parseInt(parts[1].replace("i", "")); + return new int[] {real, imaginary}; + } + + public static String multiply(String num1, String num2) { + int[] a = parse(num1); + int[] b = parse(num2); + + int real = a[0] * b[0] - a[1] * b[1]; + int imaginary = a[0] * b[1] + a[1] * b[0]; + + return real + "+" + imaginary + "i"; + } +} diff --git a/src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java b/src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java new file mode 100644 index 000000000000..cd1c9205b328 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/DistanceBetweenTwoPoints.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +/** + * Distance Between Two Points in 2D Space. + * + *

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

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

Reference: https://en.wikipedia.org/wiki/Euclidean_distance + */ +public final class DistanceBetweenTwoPoints { + + private DistanceBetweenTwoPoints() { + // Utility class; prevent instantiation + } + + /** + * Calculate the Euclidean distance between two points. + * + * @param x1 x-coordinate of the first point + * @param y1 y-coordinate of the first point + * @param x2 x-coordinate of the second point + * @param y2 y-coordinate of the second point + * @return Euclidean distance between the two points + */ + public static double calculate(final double x1, final double y1, final double x2, final double y2) { + final double deltaX = x2 - x1; + final double deltaY = y2 - y1; + return Math.sqrt(deltaX * deltaX + deltaY * deltaY); + } +} diff --git a/src/main/java/com/thealgorithms/maths/Means.java b/src/main/java/com/thealgorithms/maths/Means.java index 5445a3caebc7..d77eb1d3f661 100644 --- a/src/main/java/com/thealgorithms/maths/Means.java +++ b/src/main/java/com/thealgorithms/maths/Means.java @@ -107,6 +107,28 @@ public static Double harmonic(final Iterable numbers) { return size / sumOfReciprocals; } + /** + * Computes the quadratic mean (root mean square) of the given numbers. + *

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

+ *

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

+ * + * @param numbers the input numbers (must not be empty) + * @return the quadratic mean of the input numbers + * @throws IllegalArgumentException if the input is empty + * @see Quadratic + * Mean + */ + public static Double quadratic(final Iterable numbers) { + checkIfNotEmpty(numbers); + double sumOfSquares = StreamSupport.stream(numbers.spliterator(), false).reduce(0d, (x, y) -> x + y * y); + int size = IterableUtils.size(numbers); + return Math.pow(sumOfSquares / size, 0.5); + } + /** * Validates that the input iterable is not empty. * diff --git a/src/main/java/com/thealgorithms/maths/Volume.java b/src/main/java/com/thealgorithms/maths/Volume.java index 0f282b2abae2..c0898c5424a0 100644 --- a/src/main/java/com/thealgorithms/maths/Volume.java +++ b/src/main/java/com/thealgorithms/maths/Volume.java @@ -102,4 +102,27 @@ public static double volumePyramid(double baseArea, double height) { public static double volumeFrustumOfCone(double r1, double r2, double height) { return (Math.PI * height / 3) * (r1 * r1 + r2 * r2 + r1 * r2); } + + /** + * Calculate the volume of a frustum of a pyramid. + * + * @param upperBaseArea area of the upper base + * @param lowerBaseArea area of the lower base + * @param height height of the frustum + * @return volume of the frustum + */ + public static double volumeFrustumOfPyramid(double upperBaseArea, double lowerBaseArea, double height) { + return (upperBaseArea + lowerBaseArea + Math.sqrt(upperBaseArea * lowerBaseArea)) * height / 3; + } + + /** + * Calculate the volume of a torus. + * + * @param majorRadius major radius of a torus + * @param minorRadius minor radius of a torus + * @return volume of the torus + */ + public static double volumeTorus(double majorRadius, double minorRadius) { + return 2 * Math.PI * Math.PI * majorRadius * minorRadius * minorRadius; + } } diff --git a/src/main/java/com/thealgorithms/others/cn/HammingDistance.java b/src/main/java/com/thealgorithms/others/cn/HammingDistance.java deleted file mode 100644 index c8239d53d606..000000000000 --- a/src/main/java/com/thealgorithms/others/cn/HammingDistance.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.thealgorithms.others.cn; - -public final class HammingDistance { - private HammingDistance() { - } - - private static void checkChar(char inChar) { - if (inChar != '0' && inChar != '1') { - throw new IllegalArgumentException("Input must be a binary string."); - } - } - - public static int compute(char charA, char charB) { - checkChar(charA); - checkChar(charB); - return charA == charB ? 0 : 1; - } - - public static int compute(String bitsStrA, String bitsStrB) { - if (bitsStrA.length() != bitsStrB.length()) { - throw new IllegalArgumentException("Input strings must have the same length."); - } - - int totalErrorBitCount = 0; - - for (int i = 0; i < bitsStrA.length(); i++) { - totalErrorBitCount += compute(bitsStrA.charAt(i), bitsStrB.charAt(i)); - } - - return totalErrorBitCount; - } -} diff --git a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java index 399c3f1e041f..d096e0a8d7cd 100644 --- a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java +++ b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java @@ -41,7 +41,7 @@ public static void resolveCollision(Body a, Body b) { double dy = b.y - a.y; double dist = Math.hypot(dx, dy); - if (dist == 0) { + if (dist < a.radius + b.radius) { return; // overlapping } diff --git a/src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java b/src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java new file mode 100644 index 000000000000..1be55039cff0 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/DifferenceArray.java @@ -0,0 +1,87 @@ +package com.thealgorithms.prefixsum; + +/** + * Implements the Difference Array algorithm. + * + *

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

+ * + *

+ * Key Operations: + *

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

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

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

+ * + * @param l The starting index (inclusive). + * @param r The ending index (inclusive). + * @param val The value to add. + * @throws IllegalArgumentException if the range is invalid. + */ + public void update(int l, int r, int val) { + if (l < 0 || r >= n || l > r) { + throw new IllegalArgumentException(String.format("Invalid range: [%d, %d] for array of size %d", l, r, n)); + } + + differenceArray[l] += val; + differenceArray[r + 1] -= val; + } + + /** + * Reconstructs the final array using prefix sums. + * + * @return The resulting array after all updates. Returns long[] to handle potential overflows. + */ + public long[] getResultArray() { + long[] result = new long[n]; + result[0] = differenceArray[0]; + + for (int i = 1; i < n; i++) { + result[i] = differenceArray[i] + result[i - 1]; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/prefixsum/PrefixSum.java b/src/main/java/com/thealgorithms/prefixsum/PrefixSum.java new file mode 100644 index 000000000000..47f6366e2924 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/PrefixSum.java @@ -0,0 +1,54 @@ +package com.thealgorithms.prefixsum; + +/** + * A class that implements the Prefix Sum algorithm. + * + *

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

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

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

This implementation uses a long array for the prefix sums to prevent + * integer overflow. + * + * @see Summed-area table (Wikipedia) + * @author Chahat Sandhu, singhc7 + */ +public class PrefixSum2D { + + private final long[][] prefixSums; + + /** + * Constructor to preprocess the input matrix. + * + * @param matrix The input integer matrix. + * @throws IllegalArgumentException if the matrix is null or empty. + */ + public PrefixSum2D(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + throw new IllegalArgumentException("Input matrix cannot be null or empty"); + } + + int rows = matrix.length; + int cols = matrix[0].length; + this.prefixSums = new long[rows + 1][cols + 1]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + // P[i+1][j+1] = current + above + left - diagonal_overlap + this.prefixSums[i + 1][j + 1] = matrix[i][j] + this.prefixSums[i][j + 1] + this.prefixSums[i + 1][j] - this.prefixSums[i][j]; + } + } + } + + /** + * Calculates the sum of the sub-matrix defined by (row1, col1) to (row2, col2). + * Indices are 0-based. + * + * @param row1 Top row index. + * @param col1 Left column index. + * @param row2 Bottom row index. + * @param col2 Right column index. + * @return The sum of the sub-matrix. + * @throws IndexOutOfBoundsException if indices are invalid. + */ + public long sumRegion(int row1, int col1, int row2, int col2) { + if (row1 < 0 || row2 >= prefixSums.length - 1 || row2 < row1) { + throw new IndexOutOfBoundsException("Invalid row indices"); + } + if (col1 < 0 || col2 >= prefixSums[0].length - 1 || col2 < col1) { + throw new IndexOutOfBoundsException("Invalid column indices"); + } + + return prefixSums[row2 + 1][col2 + 1] - prefixSums[row1][col2 + 1] - prefixSums[row2 + 1][col1] + prefixSums[row1][col1]; + } +} diff --git a/src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java b/src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java new file mode 100644 index 000000000000..14a02a2de4d0 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/RangeSumQuery.java @@ -0,0 +1,73 @@ +package com.thealgorithms.prefixsum; + +/** + * Implements an algorithm to efficiently compute the sum of elements + * between any two indices in an integer array using the Prefix Sum technique. + * + *

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

+ * + *

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

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

+ * + *

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

+ * + * @author Ruturaj Jadhav, ruturajjadhav07 + */ +public final class RangeSumQuery { + + private RangeSumQuery() { + // Utility class; prevent instantiation + } + + /** + * Computes the prefix sum array for efficient range queries. + * + * @param nums The input integer array. + * @return Prefix sum array where prefixSum[i+1] = sum of nums[0..i]. + * @throws IllegalArgumentException if nums is null. + */ + public static int[] buildPrefixSum(int[] nums) { + if (nums == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + return prefixSum; + } + + /** + * Returns the sum of elements from index left to right (inclusive) + * using the provided prefix sum array. + * + * @param prefixSum The prefix sum array computed using buildPrefixSum. + * @param left The start index (inclusive). + * @param right The end index (inclusive). + * @return The sum of elements in the range [left, right]. + * @throws IllegalArgumentException if indices are invalid. + */ + public static int sumRange(int[] prefixSum, int left, int right) { + if (prefixSum == null) { + throw new IllegalArgumentException("Prefix sum array cannot be null"); + } + if (left < 0 || right >= prefixSum.length - 1 || left > right) { + throw new IllegalArgumentException("Invalid range indices"); + } + return prefixSum[right + 1] - prefixSum[left]; + } +} diff --git a/src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java b/src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java new file mode 100644 index 000000000000..d6a6bbc01663 --- /dev/null +++ b/src/main/java/com/thealgorithms/prefixsum/SubarraySumEqualsK.java @@ -0,0 +1,72 @@ +package com.thealgorithms.prefixsum; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implements an algorithm to count the number of continuous subarrays + * whose sum equals a given value k. + * + *

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

+ * + *

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

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

+ * + *

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

+ * + *

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

+ * + * @see Prefix Sum (Wikipedia) + * @author Ruturaj Jadhav, ruturajjadhav07 + */ +public final class SubarraySumEqualsK { + + private SubarraySumEqualsK() { + // Utility class; prevent instantiation + } + + /** + * Counts the number of subarrays whose sum equals k. + * + * @param nums The input integer array. + * @param k The target sum. + * @return The number of continuous subarrays summing to k. + * @throws IllegalArgumentException if nums is null. + */ + public static int countSubarrays(int[] nums, int k) { + if (nums == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + Map prefixSumFrequency = new HashMap<>(); + prefixSumFrequency.put(0L, 1); + + long prefixSum = 0; + int count = 0; + + for (int num : nums) { + prefixSum += num; + + long requiredSum = prefixSum - k; + count += prefixSumFrequency.getOrDefault(requiredSum, 0); + + prefixSumFrequency.put(prefixSum, prefixSumFrequency.getOrDefault(prefixSum, 0) + 1); + } + + return count; + } +} diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java index 72e9a14ac070..d94bef69cd3a 100644 --- a/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java +++ b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java @@ -3,27 +3,32 @@ import java.util.List; /** - * The {@code TowerOfHanoi} class provides a recursive solution to the Tower of Hanoi puzzle. - * This puzzle involves moving a set of discs from one pole to another, following specific rules: + * Recursive solution to the Tower of Hanoi puzzle. + * + *

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

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

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

* *

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

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

+ * See Tower of Hanoi on Wikipedia. + *

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

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

*/ public static void shift(int n, String startPole, String intermediatePole, String endPole, List result) { - if (n != 0) { - // Move n-1 discs from startPole to intermediatePole - shift(n - 1, startPole, endPole, intermediatePole, result); + if (n < 0) { + throw new IllegalArgumentException("Number of discs must be non-negative"); + } + if (n == 0) { + return; + } - // Add the move of the nth disc from startPole to endPole - result.add(String.format("Move %d from %s to %s", n, startPole, endPole)); + // Move n-1 discs from startPole to intermediatePole + shift(n - 1, startPole, endPole, intermediatePole, result); - // Move the n-1 discs from intermediatePole to endPole - shift(n - 1, intermediatePole, startPole, endPole, result); - } + // Add the move of the nth disc from startPole to endPole + result.add(String.format("Move %d from %s to %s", n, startPole, endPole)); + + // Move the n-1 discs from intermediatePole to endPole + shift(n - 1, intermediatePole, startPole, endPole, result); } } diff --git a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java index 9bc6da2f7443..9c809858099e 100644 --- a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java +++ b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java @@ -1,16 +1,26 @@ package com.thealgorithms.recursion; -/* - The Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, - starting with 0 and 1. - NUMBER 0 1 2 3 4 5 6 7 8 9 10 ... - FIBONACCI 0 1 1 2 3 5 8 13 21 34 55 ... -*/ +/** + * The Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, + * starting with 0 and 1. + *

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

+ */ public final class FibonacciSeries { private FibonacciSeries() { throw new UnsupportedOperationException("Utility class"); } + + /** + * Calculates the nth term in the Fibonacci sequence using recursion. + * + * @param n the position in the Fibonacci sequence (must be non-negative) + * @return the nth Fibonacci number + * @throws IllegalArgumentException if n is negative + */ public static int fibonacci(int n) { if (n < 0) { throw new IllegalArgumentException("n must be a non-negative integer"); diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch.java b/src/main/java/com/thealgorithms/searches/BinarySearch.java index bedad1667f33..7a5361b280ea 100644 --- a/src/main/java/com/thealgorithms/searches/BinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/BinarySearch.java @@ -3,12 +3,32 @@ import com.thealgorithms.devutils.searches.SearchAlgorithm; /** - * Binary search is one of the most popular algorithms The algorithm finds the - * position of a target value within a sorted array + * Binary Search Algorithm Implementation * - *

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

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

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

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

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

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

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

Example Usage: + *

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

How it works: + *

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

Time Complexity: O(log n) because we halve the search space each time. + * Space Complexity: O(log n) due to recursive call stack. * - * @param array The array to make the binary search - * @param key The number you are looking for - * @param left The lower bound - * @param right The upper bound - * @return the location of the key + * @param The type of elements (must be Comparable) + * @param array The sorted array to search in + * @param key The element we're looking for + * @param left The leftmost index of current search range (inclusive) + * @param right The rightmost index of current search range (inclusive) + * @return The index where key is located, or -1 if not found */ private > int search(T[] array, T key, int left, int right) { + // Base case: Search space is exhausted + // This happens when left pointer crosses right pointer if (right < left) { - return -1; // this means that the key not found + return -1; // Key not found in the array } - // find median - int median = (left + right) >>> 1; + + // Calculate middle index + // Using (left + right) / 2 could cause integer overflow for large arrays + // So we use: left + (right - left) / 2 which is mathematically equivalent + // but prevents overflow + int median = (left + right) >>> 1; // Unsigned right shift is faster division by 2 + + // Get the value at middle position for comparison int comp = key.compareTo(array[median]); + // Case 1: Found the target element at middle position if (comp == 0) { - return median; - } else if (comp < 0) { + return median; // Return the index where element was found + } + // Case 2: Target is smaller than middle element + // This means if target exists, it must be in the LEFT half + else if (comp < 0) { + // Recursively search the left half + // New search range: [left, median - 1] return search(array, key, left, median - 1); - } else { + } + // Case 3: Target is greater than middle element + // This means if target exists, it must be in the RIGHT half + else { + // Recursively search the right half + // New search range: [median + 1, right] return search(array, key, median + 1, right); } } diff --git a/src/main/java/com/thealgorithms/searches/LinearSearch.java b/src/main/java/com/thealgorithms/searches/LinearSearch.java index c7b70edb5112..cb483d8dfedc 100644 --- a/src/main/java/com/thealgorithms/searches/LinearSearch.java +++ b/src/main/java/com/thealgorithms/searches/LinearSearch.java @@ -1,21 +1,26 @@ package com.thealgorithms.searches; import com.thealgorithms.devutils.searches.SearchAlgorithm; - /** - * Linear search is the easiest search algorithm It works with sorted and - * unsorted arrays (an binary search works only with sorted array) This - * algorithm just compares all elements of an array to find a value + * Linear Search is a simple searching algorithm that checks + * each element of the array sequentially until the target + * value is found or the array ends. + * + * It works for both sorted and unsorted arrays. * - *

- * Worst-case performance O(n) Best-case performance O(1) Average performance - * O(n) Worst-case space complexity + * Time Complexity: + * - Best case: O(1) + * - Average case: O(n) + * - Worst case: O(n) * - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) + * Space Complexity: O(1) + * + * @author Varun Upadhyay + * @author Podshivalov Nikita * @see BinarySearch * @see SearchAlgorithm */ + public class LinearSearch implements SearchAlgorithm { /** diff --git a/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java b/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java deleted file mode 100644 index 495e2e41bc5b..000000000000 --- a/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.thealgorithms.searches; - -import com.thealgorithms.devutils.searches.SearchAlgorithm; - -/** - * Binary search is one of the most popular algorithms The algorithm finds the - * position of a target value within a sorted array - * - *

- * Worst-case performance O(log n) Best-case performance O(1) Average - * performance O(log n) Worst-case space complexity O(1) - * - * @author D Sunil (https://github.com/sunilnitdgp) - * @see SearchAlgorithm - */ - -public class PerfectBinarySearch implements SearchAlgorithm { - - /** - * @param array is an array where the element should be found - * @param key is an element which should be found - * @param is any comparable type - * @return index of the element - */ - @Override - public > int find(T[] array, T key) { - return search(array, key, 0, array.length - 1); - } - - /** - * This method implements the Generic Binary Search iteratively. - * - * @param array The array to make the binary search - * @param key The number you are looking for - * @return the location of the key, or -1 if not found - */ - private static > int search(T[] array, T key, int left, int right) { - while (left <= right) { - int median = (left + right) >>> 1; - int comp = key.compareTo(array[median]); - - if (comp == 0) { - return median; // Key found - } - - if (comp < 0) { - right = median - 1; // Adjust the right bound - } else { - left = median + 1; // Adjust the left bound - } - } - return -1; // Key not found - } -} diff --git a/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java b/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java index daf0c12c0978..1716e78964ae 100644 --- a/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java @@ -23,28 +23,27 @@ public int find(T[] arr, T target) { // Recursive binary search function public int binsear(T[] arr, int left, int right, T target) { - if (right >= left) { - int mid = left + (right - left) / 2; - - // Compare the element at the middle with the target - int comparison = arr[mid].compareTo(target); + if (right < left) { + // Element is not present in the array + return -1; + } + final int mid = left + (right - left) / 2; - // If the element is equal to the target, return its index - if (comparison == 0) { - return mid; - } + // Compare the element at the middle with the target + final int comparison = arr[mid].compareTo(target); - // If the element is greater than the target, search in the left subarray - if (comparison > 0) { - return binsear(arr, left, mid - 1, target); - } + // If the element is equal to the target, return its index + if (comparison == 0) { + return mid; + } - // Otherwise, search in the right subarray - return binsear(arr, mid + 1, right, target); + // If the element is greater than the target, search in the left subarray + if (comparison > 0) { + return binsear(arr, left, mid - 1, target); } - // Element is not present in the array - return -1; + // Otherwise, search in the right subarray + return binsear(arr, mid + 1, right, target); } public static void main(String[] args) { diff --git a/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java b/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java deleted file mode 100644 index 6a2a46c2821f..000000000000 --- a/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.thealgorithms.searches; -public final class SortOrderAgnosticBinarySearch { - private SortOrderAgnosticBinarySearch() { - } - public static int find(int[] arr, int key) { - int start = 0; - int end = arr.length - 1; - boolean arrDescending = arr[start] > arr[end]; // checking for Array is in ascending order or descending order. - while (start <= end) { - int mid = end - start / 2; - if (arr[mid] == key) { - return mid; - } - if (arrDescending) { // boolean is true then our array is in descending order - if (key < arr[mid]) { - start = mid + 1; - } else { - end = mid - 1; - } - } else { // otherwise our array is in ascending order - if (key > arr[mid]) { - start = mid + 1; - } else { - end = mid - 1; - } - } - } - return -1; - } -} diff --git a/src/main/java/com/thealgorithms/sorts/InsertionSort.java b/src/main/java/com/thealgorithms/sorts/InsertionSort.java index fdbfd9cd1cfa..1e42f2a61271 100644 --- a/src/main/java/com/thealgorithms/sorts/InsertionSort.java +++ b/src/main/java/com/thealgorithms/sorts/InsertionSort.java @@ -33,30 +33,30 @@ public > T[] sort(T[] array) { } /** - * Sorts a subarray of the given array using the standard Insertion Sort algorithm. + * Sorts a subarray of the given items using the standard Insertion Sort algorithm. * - * @param array The array to be sorted - * @param lo The starting index of the subarray - * @param hi The ending index of the subarray (exclusive) - * @param The type of elements in the array, which must be comparable - * @return The sorted array + * @param items The items to be sorted + * @param startIndex The starting index of the subarray + * @param endIndex The ending index of the subarray (exclusive) + * @param The type of elements in the items, which must be comparable + * @return The sorted items */ - public > T[] sort(T[] array, final int lo, final int hi) { - if (array == null || lo >= hi) { - return array; + public > T[] sort(T[] items, final int startIndex, final int endIndex) { + if (items == null || startIndex >= endIndex) { + return items; } - for (int i = lo + 1; i < hi; i++) { - final T key = array[i]; + for (int i = startIndex + 1; i < endIndex; i++) { + final T key = items[i]; int j = i - 1; - while (j >= lo && SortUtils.less(key, array[j])) { - array[j + 1] = array[j]; + while (j >= startIndex && SortUtils.less(key, items[j])) { + items[j + 1] = items[j]; j--; } - array[j + 1] = key; + items[j + 1] = key; } - return array; + return items; } /** diff --git a/src/main/java/com/thealgorithms/sorts/MergeSort.java b/src/main/java/com/thealgorithms/sorts/MergeSort.java index f7a7c8da004d..5db9c48b4f61 100644 --- a/src/main/java/com/thealgorithms/sorts/MergeSort.java +++ b/src/main/java/com/thealgorithms/sorts/MergeSort.java @@ -10,7 +10,7 @@ @SuppressWarnings("rawtypes") class MergeSort implements SortAlgorithm { - private Comparable[] aux; + private Comparable[] tempArray; /** * Generic merge sort algorithm. @@ -26,7 +26,7 @@ class MergeSort implements SortAlgorithm { */ @Override public > T[] sort(T[] unsorted) { - aux = new Comparable[unsorted.length]; + tempArray = new Comparable[unsorted.length]; doSort(unsorted, 0, unsorted.length - 1); return unsorted; } @@ -58,17 +58,17 @@ private > void doSort(T[] arr, int left, int right) { private > void merge(T[] arr, int left, int mid, int right) { int i = left; int j = mid + 1; - System.arraycopy(arr, left, aux, left, right + 1 - left); + System.arraycopy(arr, left, tempArray, left, right + 1 - left); for (int k = left; k <= right; k++) { if (j > right) { - arr[k] = (T) aux[i++]; + arr[k] = (T) tempArray[i++]; } else if (i > mid) { - arr[k] = (T) aux[j++]; - } else if (less(aux[j], aux[i])) { - arr[k] = (T) aux[j++]; + arr[k] = (T) tempArray[j++]; + } else if (less(tempArray[j], tempArray[i])) { + arr[k] = (T) tempArray[j++]; } else { - arr[k] = (T) aux[i++]; + arr[k] = (T) tempArray[i++]; } } } diff --git a/src/main/java/com/thealgorithms/sorts/PancakeSort.java b/src/main/java/com/thealgorithms/sorts/PancakeSort.java index 6079672a1d77..6522aefd7ae3 100644 --- a/src/main/java/com/thealgorithms/sorts/PancakeSort.java +++ b/src/main/java/com/thealgorithms/sorts/PancakeSort.java @@ -15,7 +15,7 @@ public > T[] sort(T[] array) { } for (int currentSize = 0; currentSize < array.length; currentSize++) { - int maxIndex = findMaxIndex(array, currentSize); + int maxIndex = findIndexOfMax(array, currentSize); SortUtils.flip(array, maxIndex, array.length - 1 - currentSize); } @@ -30,7 +30,7 @@ public > T[] sort(T[] array) { * @param the type of elements in the array * @return the index of the maximum element */ - private > int findMaxIndex(T[] array, int currentSize) { + private > int findIndexOfMax(T[] array, int currentSize) { T max = array[0]; int maxIndex = 0; for (int i = 0; i < array.length - currentSize; i++) { diff --git a/src/main/java/com/thealgorithms/strings/KMP.java b/src/main/java/com/thealgorithms/strings/KMP.java index 07d3b0415006..0317abe6f39a 100644 --- a/src/main/java/com/thealgorithms/strings/KMP.java +++ b/src/main/java/com/thealgorithms/strings/KMP.java @@ -1,5 +1,8 @@ package com.thealgorithms.strings; +import java.util.ArrayList; +import java.util.List; + /** * Implementation of Knuth–Morris–Pratt algorithm Usage: see the main function * for an example @@ -8,16 +11,19 @@ public final class KMP { private KMP() { } - // a working example - - public static void main(String[] args) { - final String haystack = "AAAAABAAABA"; // This is the full string - final String needle = "AAAA"; // This is the substring that we want to find - kmpMatcher(haystack, needle); - } + /** + * find the starting index in string haystack[] that matches the search word P[] + * + * @param haystack The text to be searched + * @param needle The pattern to be searched for + * @return A list of starting indices where the pattern is found + */ + public static List kmpMatcher(final String haystack, final String needle) { + List occurrences = new ArrayList<>(); + if (haystack == null || needle == null || needle.isEmpty()) { + return occurrences; + } - // find the starting index in string haystack[] that matches the search word P[] - public static void kmpMatcher(final String haystack, final String needle) { final int m = haystack.length(); final int n = needle.length(); final int[] pi = computePrefixFunction(needle); @@ -32,10 +38,11 @@ public static void kmpMatcher(final String haystack, final String needle) { } if (q == n) { - System.out.println("Pattern starts: " + (i + 1 - n)); + occurrences.add(i + 1 - n); q = pi[q - 1]; } } + return occurrences; } // return the prefix function diff --git a/src/main/java/com/thealgorithms/strings/KasaiAlgorithm.java b/src/main/java/com/thealgorithms/strings/KasaiAlgorithm.java new file mode 100644 index 000000000000..b8b10dcf4538 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/KasaiAlgorithm.java @@ -0,0 +1,79 @@ +package com.thealgorithms.strings; + +/** + * Kasai's Algorithm for constructing the Longest Common Prefix (LCP) array. + * + *

+ * The LCP array stores the lengths of the longest common prefixes between + * lexicographically adjacent suffixes of a string. Kasai's algorithm computes + * this array in O(N) time given the string and its suffix array. + *

+ * + * @see LCP array - Wikipedia + */ +public final class KasaiAlgorithm { + + private KasaiAlgorithm() { + } + + /** + * Computes the LCP array using Kasai's algorithm. + * + * @param text the original string + * @param suffixArr the suffix array of the string + * @return the LCP array of length N, where LCP[i] is the length of the longest + * common prefix of the suffixes indexed by suffixArr[i] and suffixArr[i+1]. + * The last element LCP[N-1] is always 0. + * @throws IllegalArgumentException if text or suffixArr is null, or their lengths differ + */ + public static int[] kasai(String text, int[] suffixArr) { + if (text == null || suffixArr == null) { + throw new IllegalArgumentException("Text and suffix array must not be null."); + } + int n = text.length(); + if (suffixArr.length != n) { + throw new IllegalArgumentException("Suffix array length must match text length."); + } + if (n == 0) { + return new int[0]; + } + + // Compute the inverse suffix array + // invSuff[i] stores the index of the suffix text.substring(i) in the suffix array + int[] invSuff = new int[n]; + for (int i = 0; i < n; i++) { + if (suffixArr[i] < 0 || suffixArr[i] >= n) { + throw new IllegalArgumentException("Suffix array contains out-of-bounds index."); + } + invSuff[suffixArr[i]] = i; + } + + int[] lcp = new int[n]; + int k = 0; // Length of the longest common prefix + + for (int i = 0; i < n; i++) { + // Suffix at index i has not a next suffix in suffix array + int rank = invSuff[i]; + if (rank == n - 1) { + k = 0; + continue; + } + + int nextSuffixIndex = suffixArr[rank + 1]; + + // Directly match characters to find LCP + while (i + k < n && nextSuffixIndex + k < n && text.charAt(i + k) == text.charAt(nextSuffixIndex + k)) { + k++; + } + + lcp[rank] = k; + + // Delete the starting character from the string + if (k > 0) { + k--; + } + } + + return lcp; + } +} diff --git a/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java b/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java index 6808cd50602f..51e8dc6b02c3 100644 --- a/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java +++ b/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java @@ -13,6 +13,12 @@ private LongestNonRepetitiveSubstring() { /** * Finds the length of the longest substring without repeating characters. * + * Uses the sliding window technique with a HashMap to track + * the last seen index of each character. + * + * Time Complexity: O(n), where n is the length of the input string. + * Space Complexity: O(min(n, m)), where m is the size of the character set. + * * @param s the input string * @return the length of the longest non-repetitive substring */ diff --git a/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java b/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java deleted file mode 100644 index ca500357ba77..000000000000 --- a/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.thealgorithms.strings; - -final class LongestPalindromicSubstring { - private LongestPalindromicSubstring() { - } - - /** - * Finds the longest palindromic substring in the given string. - * - * @param s the input string - * @return the longest palindromic substring - */ - public static String longestPalindrome(String s) { - if (s == null || s.isEmpty()) { - return ""; - } - String maxStr = ""; - for (int i = 0; i < s.length(); ++i) { - for (int j = i; j < s.length(); ++j) { - if (isValid(s, i, j) && (j - i + 1 > maxStr.length())) { - maxStr = s.substring(i, j + 1); - } - } - } - return maxStr; - } - - private static boolean isValid(String s, int lo, int hi) { - int n = hi - lo + 1; - for (int i = 0; i < n / 2; ++i) { - if (s.charAt(lo + i) != s.charAt(hi - i)) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java b/src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java new file mode 100644 index 000000000000..87c9278fd4bf --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LongestRepeatedSubstring.java @@ -0,0 +1,83 @@ +package com.thealgorithms.strings; + +/** + * Finds the longest substring that occurs at least twice in a given string. + * + *

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

+ * + *

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

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

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

+ * + * @param text the original string + * @param suffixArray the suffix array of the string + * @return the LCP array of length n-1 + */ + static int[] buildLcpArray(String text, int[] suffixArray) { + final int n = text.length(); + final int[] rank = new int[n]; + final int[] lcp = new int[n - 1]; + + for (int i = 0; i < n; i++) { + rank[suffixArray[i]] = i; + } + + int k = 0; + for (int i = 0; i < n; i++) { + if (rank[i] == n - 1) { + k = 0; + continue; + } + final int j = suffixArray[rank[i] + 1]; + while (i + k < n && j + k < n && text.charAt(i + k) == text.charAt(j + k)) { + k++; + } + lcp[rank[i]] = k; + if (k > 0) { + k--; + } + } + + return lcp; + } +} diff --git a/src/main/java/com/thealgorithms/strings/MyAtoi.java b/src/main/java/com/thealgorithms/strings/MyAtoi.java index 5a7c2ce53b1c..92de4039a582 100644 --- a/src/main/java/com/thealgorithms/strings/MyAtoi.java +++ b/src/main/java/com/thealgorithms/strings/MyAtoi.java @@ -45,7 +45,9 @@ public static int myAtoi(String s) { int number = 0; while (index < length) { char ch = s.charAt(index); - if (!Character.isDigit(ch)) { + + // Accept only ASCII digits + if (ch < '0' || ch > '9') { break; } diff --git a/src/main/java/com/thealgorithms/strings/RabinKarp.java b/src/main/java/com/thealgorithms/strings/RabinKarp.java index bb8df3358453..be17f87c3656 100644 --- a/src/main/java/com/thealgorithms/strings/RabinKarp.java +++ b/src/main/java/com/thealgorithms/strings/RabinKarp.java @@ -1,32 +1,30 @@ package com.thealgorithms.strings; -import java.util.Scanner; +import java.util.ArrayList; +import java.util.List; /** * @author Prateek Kumar Oraon (https://github.com/prateekKrOraon) * - An implementation of Rabin-Karp string matching algorithm - Program will simply end if there is no match + * An implementation of Rabin-Karp string matching algorithm + * Program will simply end if there is no match */ public final class RabinKarp { private RabinKarp() { } - public static Scanner scanner = null; - public static final int ALPHABET_SIZE = 256; + private static final int ALPHABET_SIZE = 256; - public static void main(String[] args) { - scanner = new Scanner(System.in); - System.out.println("Enter String"); - String text = scanner.nextLine(); - System.out.println("Enter pattern"); - String pattern = scanner.nextLine(); - - int q = 101; - searchPat(text, pattern, q); + public static List search(String text, String pattern) { + return search(text, pattern, 101); } - private static void searchPat(String text, String pattern, int q) { + public static List search(String text, String pattern, int q) { + List occurrences = new ArrayList<>(); + if (text == null || pattern == null || pattern.isEmpty()) { + return occurrences; + } + int m = pattern.length(); int n = text.length(); int t = 0; @@ -35,48 +33,42 @@ private static void searchPat(String text, String pattern, int q) { int j = 0; int i = 0; - h = (int) Math.pow(ALPHABET_SIZE, m - 1) % q; + if (m > n) { + return new ArrayList<>(); + } + + // h = pow(ALPHABET_SIZE, m-1) % q + for (i = 0; i < m - 1; i++) { + h = h * ALPHABET_SIZE % q; + } for (i = 0; i < m; i++) { - // hash value is calculated for each character and then added with the hash value of the - // next character for pattern as well as the text for length equal to the length of - // pattern p = (ALPHABET_SIZE * p + pattern.charAt(i)) % q; t = (ALPHABET_SIZE * t + text.charAt(i)) % q; } for (i = 0; i <= n - m; i++) { - // if the calculated hash value of the pattern and text matches then - // all the characters of the pattern is matched with the text of length equal to length - // of the pattern if all matches then pattern exist in string if not then the hash value - // of the first character of the text is subtracted and hash value of the next character - // after the end of the evaluated characters is added if (p == t) { - // if hash value matches then the individual characters are matched for (j = 0; j < m; j++) { - // if not matched then break out of the loop if (text.charAt(i + j) != pattern.charAt(j)) { break; } } - // if all characters are matched then pattern exist in the string if (j == m) { - System.out.println("Pattern found at index " + i); + occurrences.add(i); } } - // if i 0) { + result.deleteCharAt(result.length() - 1); + } + } else { + result.append(c); + } + } + return result.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/TopKFrequentWords.java b/src/main/java/com/thealgorithms/strings/TopKFrequentWords.java new file mode 100644 index 000000000000..106de304cf40 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/TopKFrequentWords.java @@ -0,0 +1,56 @@ +package com.thealgorithms.strings; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility class to find the top-k most frequent words. + * + *

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

Reference: + * https://en.wikipedia.org/wiki/Top-k_problem + * + */ +public final class TopKFrequentWords { + private TopKFrequentWords() { + } + + /** + * Finds the k most frequent words. + * + * @param words input array of words + * @param k number of words to return + * @return list of top-k words ordered by frequency then lexicographical order + * @throws IllegalArgumentException if words is null, k is negative, or words contains null + */ + public static List findTopKFrequentWords(String[] words, int k) { + if (words == null) { + throw new IllegalArgumentException("Input words array cannot be null."); + } + if (k < 0) { + throw new IllegalArgumentException("k cannot be negative."); + } + if (k == 0 || words.length == 0) { + return List.of(); + } + + Map frequency = new HashMap<>(); + for (String word : words) { + if (word == null) { + throw new IllegalArgumentException("Input words cannot contain null values."); + } + frequency.put(word, frequency.getOrDefault(word, 0) + 1); + } + + List candidates = new ArrayList<>(frequency.keySet()); + candidates.sort(Comparator.comparingInt(frequency::get).reversed().thenComparing(Comparator.naturalOrder())); + + int limit = Math.min(k, candidates.size()); + return new ArrayList<>(candidates.subList(0, limit)); + } +} diff --git a/src/main/java/com/thealgorithms/strings/ValidParentheses.java b/src/main/java/com/thealgorithms/strings/ValidParentheses.java deleted file mode 100644 index 25a72f379dec..000000000000 --- a/src/main/java/com/thealgorithms/strings/ValidParentheses.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.thealgorithms.strings; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Map; - -/** - * Validates if a given string has valid matching parentheses. - *

- * A string is considered valid if: - *

    - *
  • Open brackets are closed by the same type of brackets.
  • - *
  • Brackets are closed in the correct order.
  • - *
  • Every closing bracket has a corresponding open bracket of the same type.
  • - *
- * - * Allowed characters: '(', ')', '{', '}', '[', ']' - */ -public final class ValidParentheses { - private ValidParentheses() { - } - - private static final Map BRACKET_PAIRS = Map.of(')', '(', '}', '{', ']', '['); - - /** - * Checks if the input string has valid parentheses. - * - * @param s the string containing only bracket characters - * @return true if valid, false otherwise - * @throws IllegalArgumentException if the string contains invalid characters or is null - */ - public static boolean isValid(String s) { - if (s == null) { - throw new IllegalArgumentException("Input string cannot be null"); - } - - Deque stack = new ArrayDeque<>(); - - for (char c : s.toCharArray()) { - if (BRACKET_PAIRS.containsValue(c)) { - stack.push(c); // opening bracket - } else if (BRACKET_PAIRS.containsKey(c)) { - if (stack.isEmpty() || stack.pop() != BRACKET_PAIRS.get(c)) { - return false; - } - } else { - throw new IllegalArgumentException("Unexpected character: " + c); - } - } - - return stack.isEmpty(); - } -} diff --git a/src/test/java/com/thealgorithms/backtracking/CombinationTest.java b/src/test/java/com/thealgorithms/backtracking/CombinationTest.java index a9d1163f3ecd..5d2f99ccadf8 100644 --- a/src/test/java/com/thealgorithms/backtracking/CombinationTest.java +++ b/src/test/java/com/thealgorithms/backtracking/CombinationTest.java @@ -28,16 +28,16 @@ void testNoElement() { @Test void testLengthOne() { List> result = Combination.combination(new Integer[] {1, 2}, 1); - assertTrue(result.get(0).iterator().next() == 1); - assertTrue(result.get(1).iterator().next() == 2); + assertEquals(1, result.get(0).iterator().next()); + assertEquals(2, result.get(1).iterator().next()); } @Test void testLengthTwo() { List> result = Combination.combination(new Integer[] {1, 2}, 2); Integer[] arr = result.get(0).toArray(new Integer[2]); - assertTrue(arr[0] == 1); - assertTrue(arr[1] == 2); + assertEquals(1, arr[0]); + assertEquals(2, arr[1]); } @Test diff --git a/src/test/java/com/thealgorithms/backtracking/PermutationTest.java b/src/test/java/com/thealgorithms/backtracking/PermutationTest.java index 76a714829109..54747e5e73a1 100644 --- a/src/test/java/com/thealgorithms/backtracking/PermutationTest.java +++ b/src/test/java/com/thealgorithms/backtracking/PermutationTest.java @@ -12,13 +12,13 @@ public class PermutationTest { @Test void testNoElement() { List result = Permutation.permutation(new Integer[] {}); - assertEquals(result.get(0).length, 0); + assertEquals(0, result.get(0).length); } @Test void testSingleElement() { List result = Permutation.permutation(new Integer[] {1}); - assertEquals(result.get(0)[0], 1); + assertEquals(1, result.get(0)[0]); } @Test diff --git a/src/test/java/com/thealgorithms/ciphers/ECCTest.java b/src/test/java/com/thealgorithms/ciphers/ECCTest.java index 701f801af1c8..b78ba51f7c3e 100644 --- a/src/test/java/com/thealgorithms/ciphers/ECCTest.java +++ b/src/test/java/com/thealgorithms/ciphers/ECCTest.java @@ -37,7 +37,7 @@ void testEncrypt() { System.out.println("Base Point G: " + curve.getBasePoint()); // Verify that the ciphertext is not empty - assertEquals(cipherText.length, 2); // Check if the ciphertext contains two points (R and S) + assertEquals(2, cipherText.length); // Check if the ciphertext contains two points (R and S) // Output the encrypted coordinate points System.out.println("Encrypted Points:"); diff --git a/src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java b/src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java new file mode 100644 index 000000000000..63dec4846bbc --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/ElGamalCipherTest.java @@ -0,0 +1,145 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Unit tests for ElGamalCipher. + * Includes property-based testing (homomorphism), probabilistic checks, + * and boundary validation. + */ +class ElGamalCipherTest { + + private static ElGamalCipher.KeyPair sharedKeys; + + @BeforeAll + static void setup() { + // Generate 256-bit keys for efficient unit testing + sharedKeys = ElGamalCipher.generateKeys(256); + } + + @Test + @DisplayName("Test Key Generation Validity") + void testKeyGeneration() { + Assertions.assertNotNull(sharedKeys.p()); + Assertions.assertNotNull(sharedKeys.g()); + Assertions.assertNotNull(sharedKeys.x()); + Assertions.assertNotNull(sharedKeys.y()); + + // Verify generator bounds: 1 < g < p + Assertions.assertTrue(sharedKeys.g().compareTo(BigInteger.ONE) > 0); + Assertions.assertTrue(sharedKeys.g().compareTo(sharedKeys.p()) < 0); + + // Verify private key bounds: 1 < x < p-1 + Assertions.assertTrue(sharedKeys.x().compareTo(BigInteger.ONE) > 0); + Assertions.assertTrue(sharedKeys.x().compareTo(sharedKeys.p().subtract(BigInteger.ONE)) < 0); + } + + @Test + @DisplayName("Security Check: Probabilistic Encryption") + void testSemanticSecurity() { + // Encrypting the same message twice MUST yield different ciphertexts + // due to the random ephemeral key 'k'. + BigInteger message = new BigInteger("123456789"); + + ElGamalCipher.CipherText c1 = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + ElGamalCipher.CipherText c2 = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + + // Check that the ephemeral keys (and thus 'a' components) were different + Assertions.assertNotEquals(c1.a(), c2.a(), "Ciphertexts must be randomized (Semantic Security violation)"); + Assertions.assertNotEquals(c1.b(), c2.b()); + + // But both must decrypt to the original message + Assertions.assertEquals(ElGamalCipher.decrypt(c1, sharedKeys.x(), sharedKeys.p()), message); + Assertions.assertEquals(ElGamalCipher.decrypt(c2, sharedKeys.x(), sharedKeys.p()), message); + } + + @ParameterizedTest + @MethodSource("provideMessages") + @DisplayName("Parameterized Test: Encrypt and Decrypt various messages") + void testEncryptDecrypt(String messageStr) { + BigInteger message = new BigInteger(messageStr.getBytes()); + + // Skip if message exceeds the test key size (256 bits) + if (message.compareTo(sharedKeys.p()) >= 0) { + return; + } + + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, sharedKeys.x(), sharedKeys.p()); + + Assertions.assertEquals(message, decrypted, "Decrypted BigInteger must match original"); + Assertions.assertEquals(messageStr, new String(decrypted.toByteArray()), "Decrypted string must match original"); + } + + static Stream provideMessages() { + return Stream.of("Hello World", "TheAlgorithms", "A", "1234567890", "!@#$%^&*()"); + } + + @Test + @DisplayName("Edge Case: Message equals 0") + void testMessageZero() { + BigInteger zero = BigInteger.ZERO; + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(zero, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, sharedKeys.x(), sharedKeys.p()); + + Assertions.assertEquals(zero, decrypted, "Should successfully encrypt/decrypt zero"); + } + + @Test + @DisplayName("Edge Case: Message equals p-1") + void testMessageMaxBound() { + BigInteger pMinus1 = sharedKeys.p().subtract(BigInteger.ONE); + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(pMinus1, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, sharedKeys.x(), sharedKeys.p()); + + Assertions.assertEquals(pMinus1, decrypted, "Should successfully encrypt/decrypt p-1"); + } + + @Test + @DisplayName("Negative Test: Message >= p should fail") + void testMessageTooLarge() { + BigInteger tooLarge = sharedKeys.p(); + Assertions.assertThrows(IllegalArgumentException.class, () -> ElGamalCipher.encrypt(tooLarge, sharedKeys.p(), sharedKeys.g(), sharedKeys.y())); + } + + @Test + @DisplayName("Negative Test: Decrypt with wrong private key") + void testWrongKeyDecryption() { + BigInteger message = new BigInteger("99999"); + ElGamalCipher.CipherText ciphertext = ElGamalCipher.encrypt(message, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + + // Generate a fake private key + BigInteger wrongX = sharedKeys.x().add(BigInteger.ONE); + + BigInteger decrypted = ElGamalCipher.decrypt(ciphertext, wrongX, sharedKeys.p()); + + Assertions.assertNotEquals(message, decrypted, "Decryption with wrong key must yield incorrect result"); + } + + @Test + @DisplayName("Property Test: Multiplicative Homomorphism") + void testHomomorphism() { + BigInteger m1 = new BigInteger("50"); + BigInteger m2 = BigInteger.TEN; // Fix: Replaced new BigInteger("10") with BigInteger.TEN + + ElGamalCipher.CipherText c1 = ElGamalCipher.encrypt(m1, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + ElGamalCipher.CipherText c2 = ElGamalCipher.encrypt(m2, sharedKeys.p(), sharedKeys.g(), sharedKeys.y()); + + // Multiply ciphertexts component-wise: (a1*a2, b1*b2) + BigInteger aNew = c1.a().multiply(c2.a()).mod(sharedKeys.p()); + BigInteger bNew = c1.b().multiply(c2.b()).mod(sharedKeys.p()); + ElGamalCipher.CipherText cCombined = new ElGamalCipher.CipherText(aNew, bNew); + + BigInteger decrypted = ElGamalCipher.decrypt(cCombined, sharedKeys.x(), sharedKeys.p()); + BigInteger expected = m1.multiply(m2).mod(sharedKeys.p()); + + Assertions.assertEquals(expected, decrypted, "Cipher must satisfy multiplicative homomorphism"); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java b/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java index 4ba6787cc97e..ecb7455c1ba2 100644 --- a/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java +++ b/src/test/java/com/thealgorithms/ciphers/PermutationCipherTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.ciphers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; @@ -121,8 +122,8 @@ void testNullString() { String decrypted = cipher.decrypt(encrypted, key); // then - assertEquals(null, encrypted); - assertEquals(null, decrypted); + assertNull(encrypted); + assertNull(decrypted); } @Test diff --git a/src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java b/src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java new file mode 100644 index 000000000000..f919417899db --- /dev/null +++ b/src/test/java/com/thealgorithms/compression/HuffmanCodingTest.java @@ -0,0 +1,110 @@ +package com.thealgorithms.compression; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class HuffmanCodingTest { + + @Test + void testStandardLifecycle() { + String input = "efficiency is key"; + HuffmanCoding huffman = new HuffmanCoding(input); + + String encoded = huffman.encode(input); + assertNotNull(encoded); + assertTrue(encoded.matches("[01]+")); + assertEquals(input, huffman.decode(encoded)); + } + + @Test + void testNullAndEmptyHandling() { + HuffmanCoding huffman = new HuffmanCoding(""); + assertEquals("", huffman.encode("")); + assertEquals("", huffman.decode("")); + + HuffmanCoding huffmanNull = new HuffmanCoding(null); + assertEquals("", huffmanNull.encode(null)); + assertEquals("", huffmanNull.decode(null)); + } + + @Test + void testSingleCharacterEdgeCase() { + String input = "aaaaa"; + HuffmanCoding huffman = new HuffmanCoding(input); + + String encoded = huffman.encode(input); + assertEquals("00000", encoded); + assertEquals(input, huffman.decode(encoded)); + } + + @Test + void testUnicodeAndSpecialCharacters() { + // Tests spacing, symbols, non-latin alphabets, and surrogate pairs (emojis) + String input = "Hello, World! πŸš€\nLine 2: こんにけは"; + HuffmanCoding huffman = new HuffmanCoding(input); + + String encoded = huffman.encode(input); + assertEquals(input, huffman.decode(encoded)); + } + + @Test + void testFailFastOnUnseenCharacter() { + HuffmanCoding huffman = new HuffmanCoding("abc"); + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> huffman.encode("abcd") // 'd' was not in the original tree + ); + assertTrue(exception.getMessage().contains("not found in Huffman dictionary")); + } + + @Test + void testFailFastOnInvalidBinaryCharacter() { + HuffmanCoding huffman = new HuffmanCoding("abc"); + String encoded = huffman.encode("abc"); + + // Inject a '2' into the binary stream + String corruptedEncoded = encoded + "2"; + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> huffman.decode(corruptedEncoded)); + assertTrue(exception.getMessage().contains("contains invalid characters")); + } + + @Test + void testFailFastOnIncompleteSequence() { + HuffmanCoding huffman = new HuffmanCoding("abcd"); + String encoded = huffman.encode("abc"); + + // Truncate the last bit to simulate an incomplete byte/sequence transfer + String truncatedEncoded = encoded.substring(0, encoded.length() - 1); + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> huffman.decode(truncatedEncoded)); + assertTrue(exception.getMessage().contains("incomplete sequence")); + } + + @Test + void testImmutabilityOfDictionary() { + HuffmanCoding huffman = new HuffmanCoding("abc"); + var codes = huffman.getHuffmanCodes(); + + assertThrows(UnsupportedOperationException.class, () -> codes.put('z', "0101")); + } + + @Test + void testStressVolume() { + StringBuilder sb = new StringBuilder(); + // Generate a 100,000 character string + for (int i = 0; i < 100000; i++) { + sb.append((char) ('a' + (i % 26))); + } + String largeInput = sb.toString(); + + HuffmanCoding huffman = new HuffmanCoding(largeInput); + String encoded = huffman.encode(largeInput); + + assertEquals(largeInput, huffman.decode(encoded)); + } +} diff --git a/src/test/java/com/thealgorithms/compression/LZ78Test.java b/src/test/java/com/thealgorithms/compression/LZ78Test.java index 7889b50b76f3..da1fd8d23318 100644 --- a/src/test/java/com/thealgorithms/compression/LZ78Test.java +++ b/src/test/java/com/thealgorithms/compression/LZ78Test.java @@ -1,7 +1,6 @@ package com.thealgorithms.compression; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -286,7 +285,6 @@ void testTokenStructure() { // All tokens should have valid indices (>= 0) for (LZ78.Token token : compressed) { assertTrue(token.index() >= 0); - assertNotNull(token.nextChar()); } String decompressed = LZ78.decompress(compressed); diff --git a/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java b/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java index 8fdc93e1ca22..39e3fa0abe77 100644 --- a/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java +++ b/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java @@ -255,4 +255,23 @@ public void testCapacityDoubling() { assertEquals(3, array.getSize()); assertEquals("Charlie", array.get(2)); } + + @Test + public void testContains() { + DynamicArray array = new DynamicArray<>(); + array.add(1); + array.add(2); + array.add(3); + + assertTrue(array.contains(2)); + assertFalse(array.contains(5)); + } + + @Test + public void testContainsWithNull() { + DynamicArray array = new DynamicArray<>(); + array.add(null); + + assertTrue(array.contains(null)); + } } diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java index 5d1733a3e97c..6b6e670a258b 100644 --- a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java @@ -1,11 +1,9 @@ package com.thealgorithms.datastructures.hashmap.hashing; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class GenericHashMapUsingArrayTest { @@ -16,10 +14,10 @@ void testGenericHashmapWhichUsesArrayAndBothKeyAndValueAreStrings() { map.put("Nepal", "Kathmandu"); map.put("India", "New Delhi"); map.put("Australia", "Sydney"); - assertNotNull(map); - assertEquals(4, map.size()); - assertEquals("Kathmandu", map.get("Nepal")); - assertEquals("Sydney", map.get("Australia")); + Assertions.assertNotNull(map); + Assertions.assertEquals(4, map.size()); + Assertions.assertEquals("Kathmandu", map.get("Nepal")); + Assertions.assertEquals("Sydney", map.get("Australia")); } @Test @@ -29,12 +27,12 @@ void testGenericHashmapWhichUsesArrayAndKeyIsStringValueIsInteger() { map.put("Nepal", 25); map.put("India", 101); map.put("Australia", 99); - assertNotNull(map); - assertEquals(4, map.size()); - assertEquals(25, map.get("Nepal")); - assertEquals(99, map.get("Australia")); + Assertions.assertNotNull(map); + Assertions.assertEquals(4, map.size()); + Assertions.assertEquals(25, map.get("Nepal")); + Assertions.assertEquals(99, map.get("Australia")); map.remove("Nepal"); - assertFalse(map.containsKey("Nepal")); + Assertions.assertFalse(map.containsKey("Nepal")); } @Test @@ -44,11 +42,11 @@ void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() { map.put(34, "Kathmandu"); map.put(46, "New Delhi"); map.put(89, "Sydney"); - assertNotNull(map); - assertEquals(4, map.size()); - assertEquals("Sydney", map.get(89)); - assertEquals("Washington DC", map.get(101)); - assertTrue(map.containsKey(46)); + Assertions.assertNotNull(map); + Assertions.assertEquals(4, map.size()); + Assertions.assertEquals("Sydney", map.get(89)); + Assertions.assertEquals("Washington DC", map.get(101)); + Assertions.assertTrue(map.containsKey(46)); } @Test @@ -56,7 +54,7 @@ void testRemoveNonExistentKey() { GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); map.put("USA", "Washington DC"); map.remove("Nepal"); // Attempting to remove a non-existent key - assertEquals(1, map.size()); // Size should remain the same + Assertions.assertEquals(1, map.size()); // Size should remain the same } @Test @@ -65,8 +63,8 @@ void testRehashing() { for (int i = 0; i < 20; i++) { map.put("Key" + i, "Value" + i); } - assertEquals(20, map.size()); // Ensure all items were added - assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash + Assertions.assertEquals(20, map.size()); // Ensure all items were added + Assertions.assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash } @Test @@ -74,7 +72,7 @@ void testUpdateValueForExistingKey() { GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); map.put("USA", "Washington DC"); map.put("USA", "New Washington DC"); // Updating value for existing key - assertEquals("New Washington DC", map.get("USA")); + Assertions.assertEquals("New Washington DC", map.get("USA")); } @Test @@ -83,14 +81,154 @@ void testToStringMethod() { map.put("USA", "Washington DC"); map.put("Nepal", "Kathmandu"); String expected = "{USA : Washington DC, Nepal : Kathmandu}"; - assertEquals(expected, map.toString()); + Assertions.assertEquals(expected, map.toString()); } @Test void testContainsKey() { GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); map.put("USA", "Washington DC"); - assertTrue(map.containsKey("USA")); - assertFalse(map.containsKey("Nepal")); + Assertions.assertTrue(map.containsKey("USA")); + Assertions.assertFalse(map.containsKey("Nepal")); + } + + // ======= Added tests from the new version ======= + + @Test + void shouldThrowNullPointerExceptionForNullKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + String nullKey = null; // Use variable to avoid static analysis false positive + Assertions.assertThrows(NullPointerException.class, () -> map.put(nullKey, "value")); + } + + @Test + void shouldStoreNullValueForKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("keyWithNullValue", null); + Assertions.assertEquals(1, map.size()); + Assertions.assertNull(map.get("keyWithNullValue")); + // Note: containsKey returns false for null values due to implementation + Assertions.assertFalse(map.containsKey("keyWithNullValue")); + } + + @Test + void shouldHandleCollisionWhenKeysHashToSameBucket() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + Integer key1 = 1; + Integer key2 = 17; + map.put(key1, 100); + map.put(key2, 200); + Assertions.assertEquals(2, map.size()); + Assertions.assertEquals(100, map.get(key1)); + Assertions.assertEquals(200, map.get(key2)); + Assertions.assertTrue(map.containsKey(key1)); + Assertions.assertTrue(map.containsKey(key2)); + } + + @Test + void shouldHandleEmptyStringAsKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("", "valueForEmptyKey"); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("valueForEmptyKey", map.get("")); + Assertions.assertTrue(map.containsKey("")); + } + + @Test + void shouldHandleEmptyStringAsValue() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("keyForEmptyValue", ""); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("", map.get("keyForEmptyValue")); + Assertions.assertTrue(map.containsKey("keyForEmptyValue")); + } + + @Test + void shouldHandleNegativeIntegerKeys() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put(-1, 100); + map.put(-100, 200); + Assertions.assertEquals(2, map.size()); + Assertions.assertEquals(100, map.get(-1)); + Assertions.assertEquals(200, map.get(-100)); + Assertions.assertTrue(map.containsKey(-1)); + Assertions.assertTrue(map.containsKey(-100)); + } + + @Test + void shouldHandleZeroAsKey() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put(0, 100); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals(100, map.get(0)); + Assertions.assertTrue(map.containsKey(0)); + } + + @Test + void shouldHandleStringWithSpecialCharacters() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put("key!@#$%^&*()", "value<>?/\\|"); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("value<>?/\\|", map.get("key!@#$%^&*()")); + Assertions.assertTrue(map.containsKey("key!@#$%^&*()")); + } + + @Test + void shouldHandleLongStrings() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + StringBuilder longKey = new StringBuilder(); + StringBuilder longValue = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + longKey.append("a"); + longValue.append("b"); + } + String key = longKey.toString(); + String value = longValue.toString(); + map.put(key, value); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals(value, map.get(key)); + Assertions.assertTrue(map.containsKey(key)); + } + + @ParameterizedTest + @ValueSource(strings = {"a", "ab", "abc", "test", "longerString"}) + void shouldHandleKeysOfDifferentLengths(String key) { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + map.put(key, "value"); + Assertions.assertEquals(1, map.size()); + Assertions.assertEquals("value", map.get(key)); + Assertions.assertTrue(map.containsKey(key)); + } + + @Test + void shouldHandleUpdateOnExistingKeyInCollisionBucket() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + Integer key1 = 1; + Integer key2 = 17; + map.put(key1, 100); + map.put(key2, 200); + Assertions.assertEquals(2, map.size()); + map.put(key2, 999); + Assertions.assertEquals(2, map.size()); + Assertions.assertEquals(100, map.get(key1)); + Assertions.assertEquals(999, map.get(key2)); + Assertions.assertTrue(map.containsKey(key1)); + Assertions.assertTrue(map.containsKey(key2)); + } + + @Test + void shouldHandleExactlyLoadFactorBoundary() { + GenericHashMapUsingArray map = new GenericHashMapUsingArray<>(); + // Fill exactly to load factor (12 items with capacity 16 and 0.75 load factor) + for (int i = 0; i < 12; i++) { + map.put(i, i * 10); + } + Assertions.assertEquals(12, map.size()); + // Act - This should trigger rehash on 13th item + map.put(12, 120); + // Assert - Rehash should have happened + Assertions.assertEquals(13, map.size()); + Assertions.assertEquals(120, map.get(12)); + Assertions.assertTrue(map.containsKey(12)); } } diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java index 44551a8adac6..ef7739a2e8a9 100644 --- a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java @@ -81,19 +81,19 @@ void containsTest() { @Test void sizeTest() { Map map = getMap(); - assertEquals(map.size(), 0); + assertEquals(0, map.size()); for (int i = -100; i < 100; i++) { map.put(i, String.valueOf(i)); } - assertEquals(map.size(), 200); + assertEquals(200, map.size()); for (int i = -50; i < 50; i++) { map.delete(i); } - assertEquals(map.size(), 100); + assertEquals(100, map.size()); } @Test diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java index d04a9de8a94b..792969200c82 100644 --- a/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java +++ b/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; @@ -39,7 +40,7 @@ void testEquals() { assertEquals(element1, element2); // Same key and info assertNotEquals(element1, element3); // Different key - assertNotEquals(null, element1); // Check for null + assertNotNull(element1); assertNotEquals("String", element1); // Check for different type } diff --git a/src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java new file mode 100644 index 000000000000..ba5614a07916 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/MiddleOfLinkedListTest.java @@ -0,0 +1,74 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Objects; +import org.junit.jupiter.api.Test; + +public class MiddleOfLinkedListTest { + + private static SinglyLinkedListNode listOf(int firstValue, int... remainingValues) { + SinglyLinkedListNode head = new SinglyLinkedListNode(firstValue); + SinglyLinkedListNode current = head; + + for (int i = 0; i < remainingValues.length; i++) { + current.next = new SinglyLinkedListNode(remainingValues[i]); + current = current.next; + } + return head; + } + + @Test + void middleNodeOddLength() { + SinglyLinkedListNode head = listOf(1, 2, 3, 4, 5); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(3, middle.value); + } + + @Test + void middleNodeEvenLengthReturnsSecondMiddle() { + SinglyLinkedListNode head = listOf(1, 2, 3, 4, 5, 6); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(4, middle.value); + } + + @Test + void middleNodeSingleElement() { + SinglyLinkedListNode head = listOf(42); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(42, middle.value); + } + + @Test + void middleNodeTwoElementsReturnsSecond() { + SinglyLinkedListNode head = listOf(10, 20); + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(head)); + assertEquals(20, middle.value); + } + + @Test + void middleNodeNullHead() { + assertNull(MiddleOfLinkedList.middleNode(null)); + } + + @Test + void middleNodeDoesNotModifyListStructure() { + SinglyLinkedListNode first = new SinglyLinkedListNode(1); + SinglyLinkedListNode second = new SinglyLinkedListNode(2); + SinglyLinkedListNode third = new SinglyLinkedListNode(3); + SinglyLinkedListNode fourth = new SinglyLinkedListNode(4); + + first.next = second; + second.next = third; + third.next = fourth; + + SinglyLinkedListNode middle = Objects.requireNonNull(MiddleOfLinkedList.middleNode(first)); + assertEquals(3, middle.value); + + assertEquals(second, first.next); + assertEquals(third, second.next); + assertEquals(fourth, third.next); + assertNull(fourth.next); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java b/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java index e97fe091c556..3bb8bbabb761 100644 --- a/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java +++ b/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java @@ -9,14 +9,14 @@ class PriorityQueuesTest { void testPQInsertion() { PriorityQueue myQueue = new PriorityQueue(4); myQueue.insert(2); - Assertions.assertEquals(myQueue.peek(), 2); + Assertions.assertEquals(2, myQueue.peek()); myQueue.insert(5); myQueue.insert(3); - Assertions.assertEquals(myQueue.peek(), 5); + Assertions.assertEquals(5, myQueue.peek()); myQueue.insert(10); - Assertions.assertEquals(myQueue.peek(), 10); + Assertions.assertEquals(10, myQueue.peek()); } @Test @@ -28,32 +28,32 @@ void testPQDeletion() { myQueue.insert(10); myQueue.remove(); - Assertions.assertEquals(myQueue.peek(), 5); + Assertions.assertEquals(5, myQueue.peek()); myQueue.remove(); myQueue.remove(); - Assertions.assertEquals(myQueue.peek(), 2); + Assertions.assertEquals(2, myQueue.peek()); } @Test void testPQExtra() { PriorityQueue myQueue = new PriorityQueue(4); - Assertions.assertEquals(myQueue.isEmpty(), true); - Assertions.assertEquals(myQueue.isFull(), false); + Assertions.assertTrue(myQueue.isEmpty()); + Assertions.assertFalse(myQueue.isFull()); myQueue.insert(2); myQueue.insert(5); - Assertions.assertEquals(myQueue.isFull(), false); + Assertions.assertFalse(myQueue.isFull()); myQueue.insert(3); myQueue.insert(10); - Assertions.assertEquals(myQueue.isEmpty(), false); - Assertions.assertEquals(myQueue.isFull(), true); + Assertions.assertFalse(myQueue.isEmpty()); + Assertions.assertTrue(myQueue.isFull()); myQueue.remove(); - Assertions.assertEquals(myQueue.getSize(), 3); - Assertions.assertEquals(myQueue.peek(), 5); + Assertions.assertEquals(3, myQueue.getSize()); + Assertions.assertEquals(5, myQueue.peek()); myQueue.remove(); myQueue.remove(); - Assertions.assertEquals(myQueue.peek(), 2); - Assertions.assertEquals(myQueue.getSize(), 1); + Assertions.assertEquals(2, myQueue.peek()); + Assertions.assertEquals(1, myQueue.getSize()); } @Test diff --git a/src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java b/src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java new file mode 100644 index 000000000000..e3abe15b6a46 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/ReverseQueueRecursionTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.LinkedList; +import java.util.Queue; +import org.junit.jupiter.api.Test; + +class ReverseQueueRecursionTest { + @Test + void testReverseMultipleElements() { + Queue queue = new LinkedList<>(); + queue.add(1); + queue.add(2); + queue.add(3); + queue.add(4); + ReverseQueueRecursion.reverseQueue(queue); + assertEquals(4, queue.poll()); + assertEquals(3, queue.poll()); + assertEquals(2, queue.poll()); + assertEquals(1, queue.poll()); + assertTrue(queue.isEmpty()); + } + + @Test + void testReverseSingleElement() { + Queue queue = new LinkedList<>(); + queue.add(42); + ReverseQueueRecursion.reverseQueue(queue); + assertEquals(42, queue.poll()); + assertTrue(queue.isEmpty()); + } + + @Test + void testReverseEmptyQueue() { + Queue queue = new LinkedList<>(); + ReverseQueueRecursion.reverseQueue(queue); + assertTrue(queue.isEmpty()); + } + + @Test + void testReverseStringQueue() { + Queue queue = new LinkedList<>(); + queue.add("A"); + queue.add("B"); + queue.add("C"); + ReverseQueueRecursion.reverseQueue(queue); + assertEquals("C", queue.poll()); + assertEquals("B", queue.poll()); + assertEquals("A", queue.poll()); + assertTrue(queue.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java index 09ada594faca..52b74a7a1faf 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ public void searchAndNotFound() { treap.insert(3); treap.insert(8); treap.insert(1); - assertEquals(null, treap.search(4)); + assertNull(treap.search(4)); } @Test diff --git a/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java b/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java index 38784228d68e..b25fd796b112 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java @@ -16,14 +16,6 @@ public void testBuildLocation() { assertEquals(4.0, point.y); } - @Test - public void testCreateLocation() { - ClosestPair cp = new ClosestPair(5); - ClosestPair.Location[] locations = cp.createLocation(5); - assertNotNull(locations); - assertEquals(5, locations.length); - } - @Test public void testXPartition() { ClosestPair cp = new ClosestPair(5); diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java index 40bbdff15ca6..91169c4cc9d8 100644 --- a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.dynamicprogramming; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; @@ -55,27 +56,24 @@ public void testLCSWithBothEmptyStrings() { public void testLCSWithNullFirstString() { String str1 = null; String str2 = "XYZ"; - String expected = null; // Should return null if first string is null String result = LongestCommonSubsequence.getLCS(str1, str2); - assertEquals(expected, result); + assertNull(result); } @Test public void testLCSWithNullSecondString() { String str1 = "ABC"; String str2 = null; - String expected = null; // Should return null if second string is null String result = LongestCommonSubsequence.getLCS(str1, str2); - assertEquals(expected, result); + assertNull(result); } @Test public void testLCSWithNullBothStrings() { String str1 = null; String str2 = null; - String expected = null; // Should return null if both strings are null String result = LongestCommonSubsequence.getLCS(str1, str2); - assertEquals(expected, result); + assertNull(result); } @Test diff --git a/src/test/java/com/thealgorithms/io/BufferedReaderTest.java b/src/test/java/com/thealgorithms/io/BufferedReaderTest.java index 891c3066058e..088e86f8f7c5 100644 --- a/src/test/java/com/thealgorithms/io/BufferedReaderTest.java +++ b/src/test/java/com/thealgorithms/io/BufferedReaderTest.java @@ -17,15 +17,15 @@ public void testPeeks() throws IOException { BufferedReader reader = new BufferedReader(input); // read the first letter - assertEquals(reader.read(), 'H'); + assertEquals('H', reader.read()); len--; - assertEquals(reader.available(), len); + assertEquals(len, reader.available()); // position: H[e]llo!\nWorld! // reader.read() will be == 'e' - assertEquals(reader.peek(1), 'l'); - assertEquals(reader.peek(2), 'l'); // second l - assertEquals(reader.peek(3), 'o'); + assertEquals('l', reader.peek(1)); + assertEquals('l', reader.peek(2)); // second l + assertEquals('o', reader.peek(3)); } @Test @@ -38,21 +38,21 @@ public void testMixes() throws IOException { BufferedReader reader = new BufferedReader(input); // read the first letter - assertEquals(reader.read(), 'H'); // first letter + assertEquals('H', reader.read()); // first letter len--; - assertEquals(reader.peek(1), 'l'); // third later (second letter after 'H') - assertEquals(reader.read(), 'e'); // second letter + assertEquals('l', reader.peek(1)); // third later (second letter after 'H') + assertEquals('e', reader.read()); // second letter len--; - assertEquals(reader.available(), len); + assertEquals(len, reader.available()); // position: H[e]llo!\nWorld! - assertEquals(reader.peek(2), 'o'); // second l - assertEquals(reader.peek(3), '!'); - assertEquals(reader.peek(4), '\n'); + assertEquals('o', reader.peek(2)); // second l + assertEquals('!', reader.peek(3)); + assertEquals('\n', reader.peek(4)); - assertEquals(reader.read(), 'l'); // third letter - assertEquals(reader.peek(1), 'o'); // fourth letter + assertEquals('l', reader.read()); // third letter + assertEquals('o', reader.peek(1)); // fourth letter for (int i = 0; i < 6; i++) { reader.read(); @@ -74,23 +74,23 @@ public void testBlockPractical() throws IOException { ByteArrayInputStream input = new ByteArrayInputStream(bytes); BufferedReader reader = new BufferedReader(input); - assertEquals(reader.peek(), 'H'); - assertEquals(reader.read(), '!'); // read the first letter + assertEquals('H', reader.peek()); + assertEquals('!', reader.read()); // read the first letter len--; // this only reads the next 5 bytes (Hello) because // the default buffer size = 5 - assertEquals(new String(reader.readBlock()), "Hello"); + assertEquals("Hello", new String(reader.readBlock())); len -= 5; assertEquals(reader.available(), len); // maybe kind of a practical demonstration / use case if (reader.read() == '\n') { - assertEquals(reader.read(), 'W'); - assertEquals(reader.read(), 'o'); + assertEquals('W', reader.read()); + assertEquals('o', reader.read()); // the rest of the blocks - assertEquals(new String(reader.readBlock()), "rld!"); + assertEquals("rld!", new String(reader.readBlock())); } else { // should not reach throw new IOException("Something not right"); diff --git a/src/test/java/com/thealgorithms/maths/AreaTest.java b/src/test/java/com/thealgorithms/maths/AreaTest.java index b28afb85fbc3..1c2fe53ff3f3 100644 --- a/src/test/java/com/thealgorithms/maths/AreaTest.java +++ b/src/test/java/com/thealgorithms/maths/AreaTest.java @@ -16,6 +16,11 @@ void testSurfaceAreaCube() { assertEquals(6.0, Area.surfaceAreaCube(1)); } + @Test + void testSurfaceAreaCuboid() { + assertEquals(214.0, Area.surfaceAreaCuboid(5, 6, 7)); + } + @Test void testSurfaceAreaSphere() { assertEquals(12.566370614359172, Area.surfaceAreaSphere(1)); @@ -70,6 +75,12 @@ void surfaceAreaCone() { void testAllIllegalInput() { assertAll(() -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCube(0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCuboid(0, 1, 2)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCuboid(1, 0, 2)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCuboid(1, 2, 0)), () -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaSphere(0)), () diff --git a/src/test/java/com/thealgorithms/maths/BellNumbersTest.java b/src/test/java/com/thealgorithms/maths/BellNumbersTest.java new file mode 100644 index 000000000000..8dd83cf0f7a9 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/BellNumbersTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class BellNumbersTest { + + @Test + void testStandardCases() { + // Base cases and small numbers + assertEquals(1, BellNumbers.compute(0)); + assertEquals(1, BellNumbers.compute(1)); + assertEquals(2, BellNumbers.compute(2)); + assertEquals(5, BellNumbers.compute(3)); + assertEquals(15, BellNumbers.compute(4)); + assertEquals(52, BellNumbers.compute(5)); + } + + @Test + void testMediumNumber() { + // B10 = 115,975 + assertEquals(115975, BellNumbers.compute(10)); + // B15 = 1,382,958,545 + assertEquals(1382958545L, BellNumbers.compute(15)); + } + + @Test + void testLargeNumber() { + // B20 = 51,724,158,235,372 + // We use the 'L' suffix to tell Java this is a long literal + assertEquals(51724158235372L, BellNumbers.compute(20)); + } + + @Test + void testMaxLongCapacity() { + // B25 is the largest Bell number that fits in a Java long (signed 64-bit) + // B25 = 4,638,590,332,229,999,353 + assertEquals(4638590332229999353L, BellNumbers.compute(25)); + } + + @Test + void testNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> BellNumbers.compute(-1)); + } + + @Test + void testOverflowProtection() { + // We expect an exception if the user asks for the impossible + assertThrows(IllegalArgumentException.class, () -> BellNumbers.compute(26)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java b/src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java new file mode 100644 index 000000000000..02e964b53771 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ComplexNumberMultiplyTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class ComplexNumberMultiplyTest { + + @Test + void testExample() { + assertEquals("0+2i", ComplexNumberMultiply.multiply("1+1i", "1+1i")); + } + + @Test + void testNegative() { + assertEquals("0+-2i", ComplexNumberMultiply.multiply("1+-1i", "1+-1i")); + } + + @Test + void testZero() { + assertEquals("0+0i", ComplexNumberMultiply.multiply("0+0i", "5+3i")); + } + + @Test + void testInvalidFormat() { + assertThrows(IllegalArgumentException.class, () -> ComplexNumberMultiply.multiply("1+1", "1+1i")); + } + + @Test + void testNullInput() { + assertThrows(IllegalArgumentException.class, () -> ComplexNumberMultiply.multiply(null, "1+1i")); + } +} diff --git a/src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java b/src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java new file mode 100644 index 000000000000..6bd124629740 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/DistanceBetweenTwoPointsTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class DistanceBetweenTwoPointsTest { + + @Test + void testDistanceSimple() { + assertEquals(5.0, DistanceBetweenTwoPoints.calculate(0, 0, 3, 4), 1e-9); + } + + @Test + void testDistanceNegativeCoordinates() { + assertEquals(5.0, DistanceBetweenTwoPoints.calculate(-1, -1, 2, 3), 1e-9); + } + + @Test + void testSamePoint() { + assertEquals(0.0, DistanceBetweenTwoPoints.calculate(2, 2, 2, 2), 1e-9); + } +} diff --git a/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java b/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java index 3a14b80dd4f9..66f3b7b03938 100644 --- a/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java +++ b/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java @@ -9,78 +9,78 @@ public class DistanceFormulaTest { @Test void euclideanTest1() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(1, 1, 2, 2), 1.4142135623730951); + Assertions.assertEquals(1.4142135623730951, DistanceFormula.euclideanDistance(1, 1, 2, 2)); } @Test void euclideanTest2() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(1, 3, 8, 0), 7.0710678118654755); + Assertions.assertEquals(7.0710678118654755, DistanceFormula.euclideanDistance(1, 3, 8, 0)); } @Test void euclideanTest3() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(2.4, 9.1, 55.1, 100), 110.91911467371168); + Assertions.assertEquals(110.91911467371168, DistanceFormula.euclideanDistance(2.4, 9.1, 55.1, 100)); } @Test void euclideanTest4() { - Assertions.assertEquals(DistanceFormula.euclideanDistance(1000, 13, 20000, 84), 19022.067605809836); + Assertions.assertEquals(19022.067605809836, DistanceFormula.euclideanDistance(1000, 13, 20000, 84)); } @Test public void manhattantest1() { - assertEquals(DistanceFormula.manhattanDistance(1, 2, 3, 4), 4); + assertEquals(4, DistanceFormula.manhattanDistance(1, 2, 3, 4)); } @Test public void manhattantest2() { - assertEquals(DistanceFormula.manhattanDistance(6.5, 8.4, 20.1, 13.6), 18.8); + assertEquals(18.8, DistanceFormula.manhattanDistance(6.5, 8.4, 20.1, 13.6)); } @Test public void manhattanTest3() { - assertEquals(DistanceFormula.manhattanDistance(10.112, 50, 8, 25.67), 26.442); + assertEquals(26.442, DistanceFormula.manhattanDistance(10.112, 50, 8, 25.67)); } @Test public void hammingTest1() { int[] array1 = {1, 1, 1, 1}; int[] array2 = {0, 0, 0, 0}; - assertEquals(DistanceFormula.hammingDistance(array1, array2), 4); + assertEquals(4, DistanceFormula.hammingDistance(array1, array2)); } @Test public void hammingTest2() { int[] array1 = {1, 1, 1, 1}; int[] array2 = {1, 1, 1, 1}; - assertEquals(DistanceFormula.hammingDistance(array1, array2), 0); + assertEquals(0, DistanceFormula.hammingDistance(array1, array2)); } @Test public void hammingTest3() { int[] array1 = {1, 0, 0, 1, 1, 0, 1, 1, 0}; int[] array2 = {0, 1, 0, 0, 1, 1, 1, 0, 0}; - assertEquals(DistanceFormula.hammingDistance(array1, array2), 5); + assertEquals(5, DistanceFormula.hammingDistance(array1, array2)); } @Test public void minkowskiTest1() { double[] array1 = {1, 3, 8, 5}; double[] array2 = {4, 2, 6, 9}; - assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 1), 10); + assertEquals(10, DistanceFormula.minkowskiDistance(array1, array2, 1)); } @Test public void minkowskiTest2() { double[] array1 = {1, 3, 8, 5}; double[] array2 = {4, 2, 6, 9}; - assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 2), 5.477225575051661); + assertEquals(5.477225575051661, DistanceFormula.minkowskiDistance(array1, array2, 2)); } @Test public void minkowskiTest3() { double[] array1 = {1, 3, 8, 5}; double[] array2 = {4, 2, 6, 9}; - assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 3), 4.641588833612778); + assertEquals(4.641588833612778, DistanceFormula.minkowskiDistance(array1, array2, 3)); } } diff --git a/src/test/java/com/thealgorithms/maths/FactorialTest.java b/src/test/java/com/thealgorithms/maths/FactorialTest.java index b38dc45589ee..3ff7097b8113 100644 --- a/src/test/java/com/thealgorithms/maths/FactorialTest.java +++ b/src/test/java/com/thealgorithms/maths/FactorialTest.java @@ -11,7 +11,7 @@ public class FactorialTest { @Test public void testWhenInvalidInoutProvidedShouldThrowException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> Factorial.factorial(-1)); - assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); + assertEquals(EXCEPTION_MESSAGE, exception.getMessage()); } @Test diff --git a/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java b/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java index c4205985dbfd..885382e29ca2 100644 --- a/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java +++ b/src/test/java/com/thealgorithms/maths/LinearDiophantineEquationsSolverTest.java @@ -176,7 +176,7 @@ void testSolutionEquality() { assertEquals(solution1, solution2); assertNotEquals(solution3, solution1); assertEquals(solution1, solution1); - assertNotEquals(null, solution1); + assertNotNull(solution1); assertNotEquals("string", solution1); } @@ -217,7 +217,7 @@ void testGcdSolutionWrapperEquality() { assertEquals(wrapper1, wrapper2); assertNotEquals(wrapper3, wrapper1); assertEquals(wrapper1, wrapper1); - assertNotEquals(null, wrapper1); + assertNotNull(wrapper1); assertNotEquals("string", wrapper1); } diff --git a/src/test/java/com/thealgorithms/maths/MeansTest.java b/src/test/java/com/thealgorithms/maths/MeansTest.java index deee0a931910..853fdbea3963 100644 --- a/src/test/java/com/thealgorithms/maths/MeansTest.java +++ b/src/test/java/com/thealgorithms/maths/MeansTest.java @@ -172,6 +172,53 @@ void testHarmonicMeanWithLinkedList() { assertEquals(expected, Means.harmonic(numbers), EPSILON); } + // ========== Quadratic Mean Tests ========== + + @Test + void testQuadraticMeanThrowsExceptionForEmptyList() { + List numbers = new ArrayList<>(); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> Means.quadratic(numbers)); + assertTrue(exception.getMessage().contains("Empty list")); + } + + @Test + void testQuadraticMeanSingleNumber() { + LinkedHashSet numbers = new LinkedHashSet<>(Arrays.asList(2.5)); + assertEquals(2.5, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanTwoNumbers() { + List numbers = Arrays.asList(1.0, 7.0); + assertEquals(5.0, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanMultipleNumbers() { + Vector numbers = new Vector<>(Arrays.asList(1.0, 2.5, 3.0, 7.5, 10.0)); + double expected = Math.sqrt(34.5); + assertEquals(expected, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanThreeNumbers() { + List numbers = Arrays.asList(3.0, 6.0, 9.0); + double expected = Math.sqrt(42.0); + assertEquals(expected, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanIdenticalNumbers() { + List numbers = Arrays.asList(5.0, 5.0, 5.0); + assertEquals(5.0, Means.quadratic(numbers), EPSILON); + } + + @Test + void testQuadraticMeanWithLinkedList() { + LinkedList numbers = new LinkedList<>(Arrays.asList(1.0, 5.0, 11.0)); + assertEquals(7.0, Means.quadratic(numbers), EPSILON); + } + // ========== Additional Edge Case Tests ========== @Test @@ -198,21 +245,25 @@ void testAllMeansConsistencyForIdenticalValues() { double arithmetic = Means.arithmetic(numbers); double geometric = Means.geometric(numbers); double harmonic = Means.harmonic(numbers); + double quadratic = Means.quadratic(numbers); assertEquals(7.5, arithmetic, EPSILON); assertEquals(7.5, geometric, EPSILON); assertEquals(7.5, harmonic, EPSILON); + assertEquals(7.5, quadratic, EPSILON); } @Test void testMeansRelationship() { - // For positive numbers, harmonic mean ≀ geometric mean ≀ arithmetic mean + // For positive numbers, harmonic mean ≀ geometric mean ≀ arithmetic mean ≀ quadratic mean List numbers = Arrays.asList(2.0, 4.0, 8.0); double arithmetic = Means.arithmetic(numbers); double geometric = Means.geometric(numbers); double harmonic = Means.harmonic(numbers); + double quadratic = Means.quadratic(numbers); assertTrue(harmonic <= geometric, "Harmonic mean should be ≀ geometric mean"); assertTrue(geometric <= arithmetic, "Geometric mean should be ≀ arithmetic mean"); + assertTrue(arithmetic <= quadratic, "Arithmetic mean should be ≀ quadratic mean"); } } diff --git a/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java b/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java index 3fe58dadf8a5..1ee437b190c5 100644 --- a/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java +++ b/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java @@ -48,22 +48,22 @@ public void testGetWithSameObject() { var uglyNumbers = new NthUglyNumber(new int[] {7, 2, 5, 3}); for (final var tc : testCases.entrySet()) { - assertEquals(uglyNumbers.get(tc.getKey()), tc.getValue()); + assertEquals(tc.getValue(), uglyNumbers.get(tc.getKey())); } - assertEquals(uglyNumbers.get(999), 385875); + assertEquals(385875, uglyNumbers.get(999)); } @Test public void testGetWithBase1() { var uglyNumbers = new NthUglyNumber(new int[] {1}); - assertEquals(uglyNumbers.get(10), 1); + assertEquals(1, uglyNumbers.get(10)); } @Test public void testGetWithBase2() { var uglyNumbers = new NthUglyNumber(new int[] {2}); - assertEquals(uglyNumbers.get(5), 32); + assertEquals(32, uglyNumbers.get(5)); } @Test diff --git a/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java b/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java index a70100c0b913..4e4bd85d07b5 100644 --- a/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java +++ b/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java @@ -25,6 +25,6 @@ public void testNumbersAreNotPalindromes() { @Test public void testIfNegativeInputThenExceptionExpected() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> PalindromeNumber.isPalindrome(-1)); - Assertions.assertEquals(exception.getMessage(), "Input parameter must not be negative!"); + Assertions.assertEquals("Input parameter must not be negative!", exception.getMessage()); } } diff --git a/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java b/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java index 7649e21eb231..a9b78be88042 100644 --- a/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java +++ b/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java @@ -14,13 +14,13 @@ public class ParseIntegerTest { @Test public void testNullInput() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> ParseInteger.parseInt(null)); - Assertions.assertEquals(exception.getMessage(), NULL_PARAMETER_MESSAGE); + Assertions.assertEquals(NULL_PARAMETER_MESSAGE, exception.getMessage()); } @Test public void testEmptyInput() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> ParseInteger.parseInt("")); - Assertions.assertEquals(exception.getMessage(), EMPTY_PARAMETER_MESSAGE); + Assertions.assertEquals(EMPTY_PARAMETER_MESSAGE, exception.getMessage()); } @Test diff --git a/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java b/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java index a2046511ddf5..a6552d56783c 100644 --- a/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java +++ b/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java @@ -14,10 +14,10 @@ public void testSolveEquationRealRoots() { double c = 1.9; ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); - Assertions.assertEquals(roots.length, 2); - Assertions.assertEquals(roots[0].real, -0.27810465435684306); + Assertions.assertEquals(2, roots.length, 2); + Assertions.assertEquals(-0.27810465435684306, roots[0].real); Assertions.assertNull(roots[0].imaginary); - Assertions.assertEquals(roots[1].real, -1.6266572504050616); + Assertions.assertEquals(-1.6266572504050616, roots[1].real); Assertions.assertNull(roots[1].imaginary); } @@ -29,8 +29,8 @@ public void testSolveEquationEqualRoots() { double c = 1; ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); - Assertions.assertEquals(roots.length, 1); - Assertions.assertEquals(roots[0].real, -1); + Assertions.assertEquals(1, roots.length); + Assertions.assertEquals(-1, roots[0].real); } @Test @@ -41,10 +41,10 @@ public void testSolveEquationComplexRoots() { double c = 5.6; ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); - Assertions.assertEquals(roots.length, 2); - Assertions.assertEquals(roots[0].real, -0.8695652173913044); - Assertions.assertEquals(roots[0].imaginary, 1.2956229935435948); - Assertions.assertEquals(roots[1].real, -0.8695652173913044); - Assertions.assertEquals(roots[1].imaginary, -1.2956229935435948); + Assertions.assertEquals(2, roots.length); + Assertions.assertEquals(-0.8695652173913044, roots[0].real); + Assertions.assertEquals(1.2956229935435948, roots[0].imaginary); + Assertions.assertEquals(-0.8695652173913044, roots[1].real); + Assertions.assertEquals(-1.2956229935435948, roots[1].imaginary); } } diff --git a/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java b/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java index c744614e5cfa..c5d47f2213a9 100644 --- a/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java +++ b/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java @@ -29,19 +29,19 @@ public TestCase(final int[] inInputArray, final int inSecondMin, final int inSec @Test public void testForEmptyInputArray() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMin(new int[] {})); - assertEquals(exception.getMessage(), EXP_MSG_ARR_LEN_LESS_2); + assertEquals(EXP_MSG_ARR_LEN_LESS_2, exception.getMessage()); } @Test public void testForArrayWithSingleElement() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMax(new int[] {1})); - assertEquals(exception.getMessage(), EXP_MSG_ARR_LEN_LESS_2); + assertEquals(EXP_MSG_ARR_LEN_LESS_2, exception.getMessage()); } @Test public void testForArrayWithSameElements() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMin(new int[] {1, 1, 1, 1})); - assertEquals(exception.getMessage(), EXP_MSG_ARR_SAME_ELE); + assertEquals(EXP_MSG_ARR_SAME_ELE, exception.getMessage()); } @ParameterizedTest diff --git a/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java b/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java index 2c10d2d14f3e..4716d389a4ca 100644 --- a/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java +++ b/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java @@ -8,19 +8,19 @@ public class StandardDeviationTest { @Test void test1() { double[] t1 = new double[] {1, 1, 1, 1, 1}; - Assertions.assertEquals(StandardDeviation.stdDev(t1), 0.0); + Assertions.assertEquals(0.0, StandardDeviation.stdDev(t1)); } @Test void test2() { double[] t2 = new double[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - Assertions.assertEquals(StandardDeviation.stdDev(t2), 2.8722813232690143); + Assertions.assertEquals(2.8722813232690143, StandardDeviation.stdDev(t2)); } @Test void test3() { double[] t3 = new double[] {1.1, 8.5, 20.3, 2.4, 6.2}; - Assertions.assertEquals(StandardDeviation.stdDev(t3), 6.8308125431752265); + Assertions.assertEquals(6.8308125431752265, StandardDeviation.stdDev(t3)); } @Test @@ -32,6 +32,6 @@ void test4() { 100.00045, 56.7, }; - Assertions.assertEquals(StandardDeviation.stdDev(t4), 38.506117353865775); + Assertions.assertEquals(38.506117353865775, StandardDeviation.stdDev(t4)); } } diff --git a/src/test/java/com/thealgorithms/maths/StandardScoreTest.java b/src/test/java/com/thealgorithms/maths/StandardScoreTest.java index 436b1fd011c6..6858b87ad2c6 100644 --- a/src/test/java/com/thealgorithms/maths/StandardScoreTest.java +++ b/src/test/java/com/thealgorithms/maths/StandardScoreTest.java @@ -7,21 +7,21 @@ public class StandardScoreTest { @Test void test1() { - Assertions.assertEquals(StandardScore.zScore(2, 0, 5), 0.4); + Assertions.assertEquals(0.4, StandardScore.zScore(2, 0, 5)); } @Test void test2() { - Assertions.assertEquals(StandardScore.zScore(1, 1, 1), 0.0); + Assertions.assertEquals(0.0, StandardScore.zScore(1, 1, 1)); } @Test void test3() { - Assertions.assertEquals(StandardScore.zScore(2.5, 1.8, 0.7), 1.0); + Assertions.assertEquals(1.0, StandardScore.zScore(2.5, 1.8, 0.7)); } @Test void test4() { - Assertions.assertEquals(StandardScore.zScore(8.9, 3, 4.2), 1.4047619047619049); + Assertions.assertEquals(1.4047619047619049, StandardScore.zScore(8.9, 3, 4.2)); } } diff --git a/src/test/java/com/thealgorithms/maths/VolumeTest.java b/src/test/java/com/thealgorithms/maths/VolumeTest.java index 7cd0c6716147..c159d7566b46 100644 --- a/src/test/java/com/thealgorithms/maths/VolumeTest.java +++ b/src/test/java/com/thealgorithms/maths/VolumeTest.java @@ -1,6 +1,6 @@ package com.thealgorithms.maths; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; @@ -10,30 +10,36 @@ public class VolumeTest { public void volume() { /* test cube */ - assertTrue(Volume.volumeCube(7) == 343.0); + assertEquals(343.0, Volume.volumeCube(7)); /* test cuboid */ - assertTrue(Volume.volumeCuboid(2, 5, 7) == 70.0); + assertEquals(70.0, Volume.volumeCuboid(2, 5, 7)); /* test sphere */ - assertTrue(Volume.volumeSphere(7) == 1436.7550402417319); + assertEquals(1436.7550402417319, Volume.volumeSphere(7)); /* test cylinder */ - assertTrue(Volume.volumeCylinder(3, 7) == 197.92033717615698); + assertEquals(197.92033717615698, Volume.volumeCylinder(3, 7)); /* test hemisphere */ - assertTrue(Volume.volumeHemisphere(7) == 718.3775201208659); + assertEquals(718.3775201208659, Volume.volumeHemisphere(7)); /* test cone */ - assertTrue(Volume.volumeCone(3, 7) == 65.97344572538566); + assertEquals(65.97344572538566, Volume.volumeCone(3, 7)); /* test prism */ - assertTrue(Volume.volumePrism(10, 2) == 20.0); + assertEquals(20.0, Volume.volumePrism(10, 2)); /* test pyramid */ - assertTrue(Volume.volumePyramid(10, 3) == 10.0); + assertEquals(10.0, Volume.volumePyramid(10, 3)); /* test frustum */ - assertTrue(Volume.volumeFrustumOfCone(3, 5, 7) == 359.188760060433); + assertEquals(359.188760060433, Volume.volumeFrustumOfCone(3, 5, 7)); + + /* test pyramid frustum */ + assertEquals(140.0, Volume.volumeFrustumOfPyramid(6, 24, 10)); + + /* test torus */ + assertEquals(39.47841760435743, Volume.volumeTorus(2, 1)); } } diff --git a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java index f41953035846..c4a74af0ba8b 100644 --- a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java +++ b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java @@ -17,7 +17,7 @@ public class MedianOfRunningArrayTest { public void testWhenInvalidInoutProvidedShouldThrowException() { var stream = new MedianOfRunningArrayInteger(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, stream::getMedian); - assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); + assertEquals(EXCEPTION_MESSAGE, exception.getMessage()); } @Test diff --git a/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java b/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java index 915b83e376b6..c1adafa18d9f 100644 --- a/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java +++ b/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.misc; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -67,7 +68,7 @@ void testShuffleRetainsElements() { ShuffleArray.shuffle(arr); // Check that the shuffled array contains the same elements - assertTrue(arr.length == 5); + assertEquals(5, arr.length); for (int i = 1; i <= 5; i++) { assertTrue(contains(arr, i)); } diff --git a/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java b/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java deleted file mode 100644 index 3b657e441b1c..000000000000 --- a/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.thealgorithms.others; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.thealgorithms.dynamicprogramming.NewManShanksPrime; -import org.junit.jupiter.api.Test; - -public class NewManShanksPrimeTest { - - @Test - void testOne() { - assertTrue(NewManShanksPrime.nthManShanksPrime(1, 1)); - } - - @Test - void testTwo() { - assertTrue(NewManShanksPrime.nthManShanksPrime(2, 3)); - } - - @Test - void testThree() { - assertTrue(NewManShanksPrime.nthManShanksPrime(3, 7)); - } - - @Test - void testFour() { - assertTrue(NewManShanksPrime.nthManShanksPrime(4, 17)); - } - - @Test - void testFive() { - assertTrue(NewManShanksPrime.nthManShanksPrime(5, 41)); - } - - @Test - void testSix() { - assertTrue(NewManShanksPrime.nthManShanksPrime(6, 99)); - } - - @Test - void testSeven() { - assertTrue(NewManShanksPrime.nthManShanksPrime(7, 239)); - } - - @Test - void testEight() { - assertTrue(NewManShanksPrime.nthManShanksPrime(8, 577)); - } -} diff --git a/src/test/java/com/thealgorithms/others/PasswordGenTest.java b/src/test/java/com/thealgorithms/others/PasswordGenTest.java index 76492556e75f..4dcdf6b9cf4f 100644 --- a/src/test/java/com/thealgorithms/others/PasswordGenTest.java +++ b/src/test/java/com/thealgorithms/others/PasswordGenTest.java @@ -17,7 +17,7 @@ public void failGenerationWithSameMinMaxLengthTest() { @Test public void generateOneCharacterPassword() { String tempPassword = PasswordGen.generatePassword(1, 2); - assertTrue(tempPassword.length() == 1); + assertEquals(1, tempPassword.length()); } @Test diff --git a/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java b/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java deleted file mode 100644 index 669f928cd247..000000000000 --- a/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.thealgorithms.others.cn; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -public class HammingDistanceTest { - @Test - public void checkForDifferentBits() { - int answer = HammingDistance.compute("000", "011"); - Assertions.assertThat(answer).isEqualTo(2); - } - - /* - - 1 0 1 0 1 - 1 1 1 1 0 - ---------- - 0 1 0 1 1 - - - */ - @Test - public void checkForDifferentBitsLength() { - int answer = HammingDistance.compute("10101", "11110"); - Assertions.assertThat(answer).isEqualTo(3); - } - - @Test - public void checkForSameBits() { - String someBits = "111"; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void checkForLongDataBits() { - int answer = HammingDistance.compute("10010101101010000100110100", "00110100001011001100110101"); - Assertions.assertThat(answer).isEqualTo(7); - } - - @Test - public void mismatchDataBits() { - Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("100010", "00011"); }); - - Assertions.assertThat(ex.getMessage()).contains("must have the same length"); - } - - @Test - public void mismatchDataBits2() { - Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("1", "11"); }); - - Assertions.assertThat(ex.getMessage()).contains("must have the same length"); - } - - @Test - public void checkForLongDataBitsSame() { - String someBits = "10010101101010000100110100"; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void checkForEmptyInput() { - String someBits = ""; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void checkForInputOfLength1() { - String someBits = "0"; - int answer = HammingDistance.compute(someBits, someBits); - Assertions.assertThat(answer).isEqualTo(0); - } - - @Test - public void computeThrowsExceptionWhenInputsAreNotBitStrs() { - Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("1A", "11"); }); - - Assertions.assertThat(ex.getMessage()).contains("must be a binary string"); - } -} diff --git a/src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java b/src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java new file mode 100644 index 000000000000..88a480f25f1a --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/DifferenceArrayTest.java @@ -0,0 +1,110 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class DifferenceArrayTest { + + @Test + void testStandardRangeUpdate() { + int[] input = {10, 20, 30, 40, 50}; + DifferenceArray da = new DifferenceArray(input); + + da.update(1, 3, 5); + + long[] expected = {10, 25, 35, 45, 50}; + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testMultipleOverlappingUpdates() { + int[] input = {10, 10, 10, 10, 10}; + DifferenceArray da = new DifferenceArray(input); + + da.update(0, 2, 10); + da.update(2, 4, 20); + + long[] expected = {20, 20, 40, 30, 30}; + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testIntegerOverflowSafety() { + int[] input = {Integer.MAX_VALUE, 100}; + DifferenceArray da = new DifferenceArray(input); + + da.update(0, 0, 100); + + long[] result = da.getResultArray(); + long expectedVal = (long) Integer.MAX_VALUE + 100; + + assertEquals(expectedVal, result[0]); + } + + @Test + void testFullRangeUpdate() { + int[] input = {1, 2, 3}; + DifferenceArray da = new DifferenceArray(input); + + da.update(0, 2, 100); + + long[] expected = {101, 102, 103}; + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testBoundaryWriteOptimization() { + int[] input = {5, 5}; + DifferenceArray da = new DifferenceArray(input); + + da.update(1, 1, 5); + + long[] expected = {5, 10}; + + assertArrayEquals(expected, da.getResultArray()); + } + + @Test + void testLargeMassiveUpdate() { + int[] input = {0}; + DifferenceArray da = new DifferenceArray(input); + + int iterations = 100000; + for (int i = 0; i < iterations; i++) { + da.update(0, 0, 1); + } + + assertEquals(100000L, da.getResultArray()[0]); + } + + @Test + void testNullInputThrowsException() { + assertThrows(IllegalArgumentException.class, () -> new DifferenceArray(null)); + } + + @Test + void testEmptyInputThrowsException() { + assertThrows(IllegalArgumentException.class, () -> new DifferenceArray(new int[] {})); + } + + @Test + void testInvalidRangeNegativeIndex() { + DifferenceArray da = new DifferenceArray(new int[] {1, 2, 3}); + assertThrows(IllegalArgumentException.class, () -> da.update(-1, 1, 5)); + } + + @Test + void testInvalidRangeOutOfBounds() { + DifferenceArray da = new DifferenceArray(new int[] {1, 2, 3}); + assertThrows(IllegalArgumentException.class, () -> da.update(0, 3, 5)); + } + + @Test + void testInvalidRangeStartGreaterThanEnd() { + DifferenceArray da = new DifferenceArray(new int[] {1, 2, 3}); + assertThrows(IllegalArgumentException.class, () -> da.update(2, 1, 5)); + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java b/src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java new file mode 100644 index 000000000000..87feff859356 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/PrefixSum2DTest.java @@ -0,0 +1,92 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PrefixSum2DTest { + + @Test + @DisplayName("Test basic 3x3 square matrix") + void testStandardSquare() { + int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Sum of top-left 2x2: {1,2, 4,5} -> 12 + assertEquals(12L, ps.sumRegion(0, 0, 1, 1)); + // Sum of bottom-right 2x2: {5,6, 8,9} -> 28 + assertEquals(28L, ps.sumRegion(1, 1, 2, 2)); + // Full matrix -> 45 + assertEquals(45L, ps.sumRegion(0, 0, 2, 2)); + } + + @Test + @DisplayName("Test rectangular matrix (more cols than rows)") + void testRectangularWide() { + int[][] matrix = {{1, 1, 1, 1}, {2, 2, 2, 2}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Sum of first 3 columns of both rows -> (1*3) + (2*3) = 9 + assertEquals(9L, ps.sumRegion(0, 0, 1, 2)); + } + + @Test + @DisplayName("Test rectangular matrix (more rows than cols)") + void testRectangularTall() { + int[][] matrix = {{1}, {2}, {3}, {4}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Sum of middle two elements -> 2+3 = 5 + assertEquals(5L, ps.sumRegion(1, 0, 2, 0)); + } + + @Test + @DisplayName("Test single element matrix") + void testSingleElement() { + int[][] matrix = {{100}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + assertEquals(100L, ps.sumRegion(0, 0, 0, 0)); + } + + @Test + @DisplayName("Test large numbers for overflow (Integer -> Long)") + void testLargeNumbers() { + // 2 billion. Two of these sum to > MAX_INT + int val = 2_000_000_000; + int[][] matrix = {{val, val}, {val, val}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // 4 * 2B = 8 Billion + assertEquals(8_000_000_000L, ps.sumRegion(0, 0, 1, 1)); + } + + @Test + @DisplayName("Test invalid inputs") + void testInvalidInputs() { + assertThrows(IllegalArgumentException.class, () -> new PrefixSum2D(null)); + assertThrows(IllegalArgumentException.class, () -> new PrefixSum2D(new int[][] {})); // empty + assertThrows(IllegalArgumentException.class, () -> new PrefixSum2D(new int[][] {{}})); // empty row + } + + @Test + @DisplayName("Test invalid query ranges") + void testInvalidRanges() { + int[][] matrix = {{1, 2}, {3, 4}}; + PrefixSum2D ps = new PrefixSum2D(matrix); + + // Negative indices + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(-1, 0, 0, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, -1, 0, 0)); + + // Out of bounds + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, 0, 2, 0)); // row2 too big + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, 0, 0, 2)); // col2 too big + + // Inverted ranges (start > end) + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(1, 0, 0, 0)); // row1 > row2 + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRegion(0, 1, 0, 0)); // col1 > col2 + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java b/src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java new file mode 100644 index 000000000000..a421b62e9306 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/PrefixSumTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PrefixSumTest { + + @Test + @DisplayName("Test basic sum with positive integers") + void testStandardCase() { + int[] input = {1, 2, 3, 4, 5}; + PrefixSum ps = new PrefixSum(input); + + // Sum of range [0, 4] -> 15 + assertEquals(15L, ps.sumRange(0, 4)); + + // Sum of range [1, 3] -> 9 + assertEquals(9L, ps.sumRange(1, 3)); + } + + @Test + @DisplayName("Test array with negative numbers and zeros") + void testNegativeAndZeros() { + int[] input = {-2, 0, 3, -5, 2, -1}; + PrefixSum ps = new PrefixSum(input); + + assertEquals(1L, ps.sumRange(0, 2)); + assertEquals(-1L, ps.sumRange(2, 5)); + assertEquals(0L, ps.sumRange(1, 1)); + } + + @Test + @DisplayName("Test with large integers to verify overflow handling") + void testLargeNumbers() { + // Two values that fit in int, but their sum exceeds Integer.MAX_VALUE + // Integer.MAX_VALUE is approx 2.14 billion. + int val = 2_000_000_000; + int[] input = {val, val, val}; + PrefixSum ps = new PrefixSum(input); + + // Sum of three 2 billion values is 6 billion (fits in long, overflows int) + assertEquals(6_000_000_000L, ps.sumRange(0, 2)); + } + + @Test + @DisplayName("Test single element array") + void testSingleElement() { + int[] input = {42}; + PrefixSum ps = new PrefixSum(input); + assertEquals(42L, ps.sumRange(0, 0)); + } + + @Test + @DisplayName("Test constructor with null input") + void testNullInput() { + assertThrows(IllegalArgumentException.class, () -> new PrefixSum(null)); + } + + @Test + @DisplayName("Test empty array behavior") + void testEmptyArray() { + int[] input = {}; + PrefixSum ps = new PrefixSum(input); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(0, 0)); + } + + @Test + @DisplayName("Test invalid range indices") + void testInvalidIndices() { + int[] input = {10, 20, 30}; + PrefixSum ps = new PrefixSum(input); + + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(-1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(0, 3)); + assertThrows(IndexOutOfBoundsException.class, () -> ps.sumRange(2, 1)); + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java b/src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java new file mode 100644 index 000000000000..12072318ac74 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/RangeSumQueryTest.java @@ -0,0 +1,73 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link RangeSumQuery}. + */ +class RangeSumQueryTest { + + @Test + void testBasicExample() { + int[] nums = {1, 2, 3, 4, 5}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(6, RangeSumQuery.sumRange(prefixSum, 0, 2)); // 1+2+3 + assertEquals(9, RangeSumQuery.sumRange(prefixSum, 1, 3)); // 2+3+4 + assertEquals(15, RangeSumQuery.sumRange(prefixSum, 0, 4)); // 1+2+3+4+5 + assertEquals(12, RangeSumQuery.sumRange(prefixSum, 2, 4)); // 3+4+5 + } + + @Test + void testSingleElement() { + int[] nums = {7}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(7, RangeSumQuery.sumRange(prefixSum, 0, 0)); + } + + @Test + void testAllZeros() { + int[] nums = {0, 0, 0, 0}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(0, RangeSumQuery.sumRange(prefixSum, 0, 3)); + assertEquals(0, RangeSumQuery.sumRange(prefixSum, 1, 2)); + } + + @Test + void testNegativeNumbers() { + int[] nums = {-1, 2, -3, 4}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertEquals(-2, RangeSumQuery.sumRange(prefixSum, 0, 2)); // -1+2-3 + assertEquals(3, RangeSumQuery.sumRange(prefixSum, 1, 3)); // 2-3+4 + } + + @Test + void testEmptyArrayThrowsException() { + int[] nums = {}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, 0, 0)); + } + + @Test + void testNullArrayThrowsException() { + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.buildPrefixSum(null)); + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(null, 0, 0)); + } + + @Test + void testInvalidIndicesThrowsException() { + int[] nums = {1, 2, 3}; + int[] prefixSum = RangeSumQuery.buildPrefixSum(nums); + + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, 1, 5)); + assertThrows(IllegalArgumentException.class, () -> RangeSumQuery.sumRange(prefixSum, 2, 1)); + } +} diff --git a/src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java b/src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java new file mode 100644 index 000000000000..68f85b713046 --- /dev/null +++ b/src/test/java/com/thealgorithms/prefixsum/SubarraySumEqualskTest.java @@ -0,0 +1,59 @@ +package com.thealgorithms.prefixsum; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link SubarraySumEqualsK}. + */ +class SubarraySumEqualsKTest { + + @Test + void testBasicExample() { + int[] nums = {1, 1, 1}; + int k = 2; + assertEquals(2, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testWithNegativeNumbers() { + int[] nums = {1, -1, 0}; + int k = 0; + assertEquals(3, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testSingleElementEqualToK() { + int[] nums = {5}; + int k = 5; + assertEquals(1, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testSingleElementNotEqualToK() { + int[] nums = {5}; + int k = 3; + assertEquals(0, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testAllZeros() { + int[] nums = {0, 0, 0}; + int k = 0; + assertEquals(6, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testEmptyArray() { + int[] nums = {}; + int k = 0; + assertEquals(0, SubarraySumEqualsK.countSubarrays(nums, k)); + } + + @Test + void testNullArrayThrowsException() { + assertThrows(IllegalArgumentException.class, () -> SubarraySumEqualsK.countSubarrays(null, 0)); + } +} diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java index 42669eb03bb4..f0a2686d3e4b 100644 --- a/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java +++ b/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java @@ -1,14 +1,31 @@ package com.thealgorithms.puzzlesandgames; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class TowerOfHanoiTest { + @ParameterizedTest + @MethodSource("diskCountAndMoveCount") + void testMoveCountMatchesFormula(int disks, int expectedMoves) { + List result = new ArrayList<>(); + TowerOfHanoi.shift(disks, "A", "B", "C", result); + assertEquals(expectedMoves, result.size()); + } + + private static Stream diskCountAndMoveCount() { + return Stream.of(Arguments.of(1, 1), Arguments.of(2, 3), Arguments.of(3, 7), Arguments.of(4, 15), Arguments.of(5, 31), Arguments.of(10, 1023)); + } + @Test public void testHanoiWithOneDisc() { List result = new ArrayList<>(); @@ -39,6 +56,15 @@ public void testHanoiWithThreeDiscs() { assertEquals(expected, result); } + @Test + public void testHanoiWithDifferentPoles() { + List result = new ArrayList<>(); + TowerOfHanoi.shift(2, "X", "Y", "Z", result); + + List expected = List.of("Move 1 from X to Y", "Move 2 from X to Z", "Move 1 from Y to Z"); + assertEquals(expected, result); + } + @Test public void testHanoiWithZeroDiscs() { List result = new ArrayList<>(); @@ -47,4 +73,10 @@ public void testHanoiWithZeroDiscs() { // There should be no moves if there are 0 discs assertTrue(result.isEmpty()); } + + @Test + public void testHanoiWithNegativeDiscsThrows() { + List result = new ArrayList<>(); + assertThrows(IllegalArgumentException.class, () -> TowerOfHanoi.shift(-1, "Pole1", "Pole2", "Pole3", result)); + } } diff --git a/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java b/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java index 18f0afc6a0a6..dec2c86de9c7 100644 --- a/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java +++ b/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java @@ -117,7 +117,7 @@ public void binarySearch2dArrayTestTargetInMiddle() { int target = 8; // Assert that the requirement, that the target is in the middle row and middle column, is // fulfilled. - assertEquals(arr[arr.length / 2][arr[0].length / 2], target); + assertEquals(target, arr[arr.length / 2][arr[0].length / 2]); int[] ans = BinarySearch2dArray.binarySearch(arr, target); System.out.println(Arrays.toString(ans)); assertEquals(1, ans[0]); @@ -135,8 +135,8 @@ public void binarySearch2dArrayTestTargetAboveMiddleRowInMiddleColumn() { // Assert that the requirement, that he target is in the middle column, // in an array with an even number of columns, and on the row "above" the middle row. - assertEquals(arr[0].length % 2, 0); - assertEquals(arr[arr.length / 2 - 1][arr[0].length / 2], target); + assertEquals(0, arr[0].length % 2); + assertEquals(target, arr[arr.length / 2 - 1][arr[0].length / 2]); int[] ans = BinarySearch2dArray.binarySearch(arr, target); System.out.println(Arrays.toString(ans)); assertEquals(0, ans[0]); diff --git a/src/test/java/com/thealgorithms/searches/KMPSearchTest.java b/src/test/java/com/thealgorithms/searches/KMPSearchTest.java index cb804ac6a6a3..216c5fcd7d2c 100644 --- a/src/test/java/com/thealgorithms/searches/KMPSearchTest.java +++ b/src/test/java/com/thealgorithms/searches/KMPSearchTest.java @@ -14,7 +14,7 @@ public void kmpSearchTestLast() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 10); + assertEquals(10, value); } @Test @@ -25,7 +25,7 @@ public void kmpSearchTestFront() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 0); + assertEquals(0, value); } @Test @@ -36,7 +36,7 @@ public void kmpSearchTestMiddle() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 4); + assertEquals(4, value); } @Test @@ -47,7 +47,7 @@ public void kmpSearchTestNotFound() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, 4); + assertEquals(4, value); } @Test @@ -58,6 +58,6 @@ public void kmpSearchTest4() { KMPSearch kmpSearch = new KMPSearch(); int value = kmpSearch.kmpSearch(pat, txt); System.out.println(value); - assertEquals(value, -1); + assertEquals(-1, value); } } diff --git a/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java b/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java index 534c2a4487b2..c0d82489209f 100644 --- a/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java +++ b/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Random; import org.junit.jupiter.api.Test; class LinearSearchThreadTest { @@ -62,10 +63,11 @@ void testSearcherEmptySegment() throws InterruptedException { void testSearcherRandomNumbers() throws InterruptedException { int size = 200; int[] array = new int[size]; + Random random = new Random(); for (int i = 0; i < size; i++) { - array[i] = (int) (Math.random() * 100); + array[i] = random.nextInt(100); } - int target = array[(int) (Math.random() * size)]; // Randomly select a target that is present + final int target = array[random.nextInt(size)]; // Randomly select a target that is present Searcher searcher = new Searcher(array, 0, size, target); searcher.start(); searcher.join(); diff --git a/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java deleted file mode 100644 index 6eab20f45467..000000000000 --- a/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.thealgorithms.searches; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -/** - * @author D Sunil (https://github.com/sunilnitdgp) - * @see PerfectBinarySearch - */ -public class PerfectBinarySearchTest { - - @Test - public void testIntegerBinarySearch() { - Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - PerfectBinarySearch binarySearch = new PerfectBinarySearch<>(); - - // Test cases for elements present in the array - assertEquals(0, binarySearch.find(array, 1)); // First element - assertEquals(4, binarySearch.find(array, 5)); // Middle element - assertEquals(9, binarySearch.find(array, 10)); // Last element - assertEquals(6, binarySearch.find(array, 7)); // Element in the middle - - // Test cases for elements not in the array - assertEquals(-1, binarySearch.find(array, 0)); // Element before the array - assertEquals(-1, binarySearch.find(array, 11)); // Element after the array - assertEquals(-1, binarySearch.find(array, 100)); // Element not in the array - } - - @Test - public void testStringBinarySearch() { - String[] array = {"apple", "banana", "cherry", "date", "fig"}; - PerfectBinarySearch binarySearch = new PerfectBinarySearch<>(); - - // Test cases for elements not in the array - assertEquals(-1, binarySearch.find(array, "apricot")); // Element not in the array - assertEquals(-1, binarySearch.find(array, "bananaa")); // Element not in the array - - // Test cases for elements present in the array - assertEquals(0, binarySearch.find(array, "apple")); // First element - assertEquals(2, binarySearch.find(array, "cherry")); // Middle element - assertEquals(4, binarySearch.find(array, "fig")); // Last element - } -} diff --git a/src/test/java/com/thealgorithms/searches/QuickSelectTest.java b/src/test/java/com/thealgorithms/searches/QuickSelectTest.java index cf160b0ff4b5..4c96be76861a 100644 --- a/src/test/java/com/thealgorithms/searches/QuickSelectTest.java +++ b/src/test/java/com/thealgorithms/searches/QuickSelectTest.java @@ -172,7 +172,7 @@ void quickSelect70thPercentileOfManyElements() { void quickSelectMedianOfThreeCharacters() { List elements = Arrays.asList('X', 'Z', 'Y'); char actual = QuickSelect.select(elements, 1); - assertEquals(actual, 'Y'); + assertEquals('Y', actual); } @Test diff --git a/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java deleted file mode 100644 index e2917733d1d9..000000000000 --- a/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.thealgorithms.searches; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class SortOrderAgnosticBinarySearchTest { - - @Test - public void testAscending() { - int[] arr = {1, 2, 3, 4, 5}; // for ascending order. - int target = 2; - int ans = SortOrderAgnosticBinarySearch.find(arr, target); - int excepted = 1; - assertEquals(excepted, ans); - } - - @Test - public void testDescending() { - int[] arr = {5, 4, 3, 2, 1}; // for descending order. - int target = 2; - int ans = SortOrderAgnosticBinarySearch.find(arr, target); - int excepted = 3; - assertEquals(excepted, ans); - } -} diff --git a/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java b/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java index d5588b2b968e..e19f5b928263 100644 --- a/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java +++ b/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java @@ -58,7 +58,7 @@ public void failureTest() { Exception exception = assertThrows(RuntimeException.class, () -> TopologicalSort.sort(graph)); String expected = "This graph contains a cycle. No linear ordering is possible. " + "Back edge: 6 -> 2"; - assertEquals(exception.getMessage(), expected); + assertEquals(expected, exception.getMessage()); } @Test void testEmptyGraph() { diff --git a/src/test/java/com/thealgorithms/strings/KMPTest.java b/src/test/java/com/thealgorithms/strings/KMPTest.java new file mode 100644 index 000000000000..9fa5f398d420 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/KMPTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class KMPTest { + + @Test + public void testNullInputs() { + assertEquals(List.of(), KMP.kmpMatcher(null, "A")); + assertEquals(List.of(), KMP.kmpMatcher("A", null)); + assertEquals(List.of(), KMP.kmpMatcher(null, null)); + } + + @Test + public void testKMPMatcher() { + assertEquals(List.of(0, 1), KMP.kmpMatcher("AAAAABAAABA", "AAAA")); + assertEquals(List.of(0, 3), KMP.kmpMatcher("ABCABC", "ABC")); + assertEquals(List.of(10), KMP.kmpMatcher("ABABDABACDABABCABAB", "ABABCABAB")); + assertEquals(List.of(), KMP.kmpMatcher("ABCDE", "FGH")); + assertEquals(List.of(), KMP.kmpMatcher("A", "AA")); + assertEquals(List.of(0, 1, 2), KMP.kmpMatcher("AAA", "A")); + assertEquals(List.of(0), KMP.kmpMatcher("A", "A")); + assertEquals(List.of(), KMP.kmpMatcher("", "A")); + assertEquals(List.of(), KMP.kmpMatcher("A", "")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/KasaiAlgorithmTest.java b/src/test/java/com/thealgorithms/strings/KasaiAlgorithmTest.java new file mode 100644 index 000000000000..c22cc77df18a --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/KasaiAlgorithmTest.java @@ -0,0 +1,75 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class KasaiAlgorithmTest { + + @Test + public void testKasaiBanana() { + String text = "banana"; + // Suffixes: + // 0: banana + // 1: anana + // 2: nana + // 3: ana + // 4: na + // 5: a + // + // Sorted Suffixes: + // 5: a + // 3: ana + // 1: anana + // 0: banana + // 4: na + // 2: nana + int[] suffixArr = {5, 3, 1, 0, 4, 2}; + + int[] expectedLcp = {1, 3, 0, 0, 2, 0}; + + assertArrayEquals(expectedLcp, KasaiAlgorithm.kasai(text, suffixArr)); + } + + @Test + public void testKasaiAaaa() { + String text = "aaaa"; + // Sorted Suffixes: + // 3: a + // 2: aa + // 1: aaa + // 0: aaaa + int[] suffixArr = {3, 2, 1, 0}; + int[] expectedLcp = {1, 2, 3, 0}; + + assertArrayEquals(expectedLcp, KasaiAlgorithm.kasai(text, suffixArr)); + } + + @Test + public void testKasaiEmptyString() { + assertArrayEquals(new int[0], KasaiAlgorithm.kasai("", new int[0])); + } + + @Test + public void testKasaiSingleChar() { + assertArrayEquals(new int[] {0}, KasaiAlgorithm.kasai("A", new int[] {0})); + } + + @Test + public void testKasaiNullTextOrSuffixArray() { + assertThrows(IllegalArgumentException.class, () -> KasaiAlgorithm.kasai(null, new int[] {0})); + assertThrows(IllegalArgumentException.class, () -> KasaiAlgorithm.kasai("A", null)); + } + + @Test + public void testKasaiInvalidSuffixArrayLength() { + assertThrows(IllegalArgumentException.class, () -> KasaiAlgorithm.kasai("A", new int[] {0, 1})); + } + + @Test + public void testKasaiInvalidSuffixArrayIndex() { + assertThrows(IllegalArgumentException.class, () -> KasaiAlgorithm.kasai("A", new int[] {1})); // Out of bounds + assertThrows(IllegalArgumentException.class, () -> KasaiAlgorithm.kasai("A", new int[] {-1})); // Out of bounds + } +} diff --git a/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java deleted file mode 100644 index aa13c0f4a474..000000000000 --- a/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.thealgorithms.strings; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.stream.Stream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class LongestPalindromicSubstringTest { - - @ParameterizedTest - @MethodSource("provideTestCasesForLongestPalindrome") - void testLongestPalindrome(String input, String expected) { - assertEquals(expected, LongestPalindromicSubstring.longestPalindrome(input)); - } - - private static Stream provideTestCasesForLongestPalindrome() { - return Stream.of(Arguments.of("babad", "bab"), Arguments.of("cbbd", "bb"), Arguments.of("a", "a"), Arguments.of("", ""), Arguments.of("abc", "a"), Arguments.of(null, ""), Arguments.of("aaaaa", "aaaaa")); - } -} diff --git a/src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java new file mode 100644 index 000000000000..366f6863340d --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LongestRepeatedSubstringTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class LongestRepeatedSubstringTest { + + @ParameterizedTest(name = "\"{0}\" -> \"{1}\"") + @MethodSource("provideTestCases") + void testLongestRepeatedSubstring(String input, String expected) { + assertEquals(expected, LongestRepeatedSubstring.longestRepeatedSubstring(input)); + } + + private static Stream provideTestCases() { + return Stream.of(Arguments.of("banana", "ana"), Arguments.of("abcabc", "abc"), Arguments.of("aaaa", "aaa"), Arguments.of("abcd", ""), Arguments.of("a", ""), Arguments.of("", ""), Arguments.of(null, ""), Arguments.of("aab", "a"), Arguments.of("aa", "a"), Arguments.of("mississippi", "issi")); + } + + @ParameterizedTest(name = "\"{0}\" -> LCP={1}") + @MethodSource("provideLcpTestCases") + void testBuildLcpArray(String input, int[] expectedLcp) { + int[] suffixArray = SuffixArray.buildSuffixArray(input); + assertArrayEquals(expectedLcp, LongestRepeatedSubstring.buildLcpArray(input, suffixArray)); + } + + private static Stream provideLcpTestCases() { + return Stream.of(Arguments.of("banana", new int[] {1, 3, 0, 0, 2}), Arguments.of("ab", new int[] {0})); + } +} diff --git a/src/test/java/com/thealgorithms/strings/RabinKarpTest.java b/src/test/java/com/thealgorithms/strings/RabinKarpTest.java new file mode 100644 index 000000000000..6dfd099e9bca --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/RabinKarpTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class RabinKarpTest { + + @Test + public void testNullInputs() { + assertEquals(List.of(), RabinKarp.search(null, "A")); + assertEquals(List.of(), RabinKarp.search("A", null)); + assertEquals(List.of(), RabinKarp.search(null, null)); + } + + @Test + public void testHashCollision() { + // 'a' = 97. (char)198 % 101 = 97. + // For length 1, h = 1. p = 97. t = 198 % 101 = 97. + // Collision occurs, loop checks characters: 198 != 97, breaks. + char collisionChar = (char) 198; + String text = String.valueOf(collisionChar); + String pattern = "a"; + assertEquals(List.of(), RabinKarp.search(text, pattern)); + } + + @Test + public void testSearchWithCustomQ() { + // Using a different prime + assertEquals(List.of(0, 1), RabinKarp.search("AAAA", "AAA", 13)); + } + + @Test + public void testRabinKarpSearch() { + assertEquals(List.of(0, 1), RabinKarp.search("AAAAABAAABA", "AAAA")); + assertEquals(List.of(0, 3), RabinKarp.search("ABCABC", "ABC")); + assertEquals(List.of(10), RabinKarp.search("ABABDABACDABABCABAB", "ABABCABAB")); + assertEquals(List.of(), RabinKarp.search("ABCDE", "FGH")); + assertEquals(List.of(), RabinKarp.search("A", "AA")); + assertEquals(List.of(0, 1, 2), RabinKarp.search("AAA", "A")); + assertEquals(List.of(0), RabinKarp.search("A", "A")); + assertEquals(List.of(), RabinKarp.search("", "A")); + assertEquals(List.of(), RabinKarp.search("A", "")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/RemoveStarsTest.java b/src/test/java/com/thealgorithms/strings/RemoveStarsTest.java new file mode 100644 index 000000000000..3beb2e83399b --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/RemoveStarsTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class RemoveStarsTest { + + @Test + void testExampleCase() { + assertEquals("lecoe", RemoveStars.removeStars("leet**cod*e")); + } + + @Test + void testAllStars() { + assertEquals("", RemoveStars.removeStars("abc***")); + } + + @Test + void testNoStars() { + assertEquals("hello", RemoveStars.removeStars("hello")); + } + + @Test + void testSingleCharacter() { + assertEquals("", RemoveStars.removeStars("a*")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java b/src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java new file mode 100644 index 000000000000..42b2d04ff265 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/TopKFrequentWordsTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class TopKFrequentWordsTest { + + @ParameterizedTest + @MethodSource("validTestCases") + void testFindTopKFrequentWords(String[] words, int k, List expected) { + assertEquals(expected, TopKFrequentWords.findTopKFrequentWords(words, k)); + } + + static Stream validTestCases() { + return Stream.of(Arguments.of(new String[] {"i", "love", "leetcode", "i", "love", "coding"}, 2, List.of("i", "love")), Arguments.of(new String[] {"the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"}, 4, List.of("the", "is", "sunny", "day")), + Arguments.of(new String[] {"bbb", "aaa", "bbb", "aaa", "ccc"}, 2, List.of("aaa", "bbb")), Arguments.of(new String[] {"one", "two", "three"}, 10, List.of("one", "three", "two")), Arguments.of(new String[] {}, 3, List.of()), Arguments.of(new String[] {"x", "x", "y"}, 0, List.of())); + } + + @ParameterizedTest + @MethodSource("invalidTestCases") + void testFindTopKFrequentWordsInvalidInput(String[] words, int k) { + assertThrows(IllegalArgumentException.class, () -> TopKFrequentWords.findTopKFrequentWords(words, k)); + } + + static Stream invalidTestCases() { + return Stream.of(Arguments.of((String[]) null, 1), Arguments.of(new String[] {"a", null, "b"}, 2), Arguments.of(new String[] {"a"}, -1)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java deleted file mode 100644 index 411b11e743b8..000000000000 --- a/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.thealgorithms.strings; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -public class ValidParenthesesTest { - - @ParameterizedTest(name = "Input: \"{0}\" β†’ Expected: {1}") - @CsvSource({"'()', true", "'()[]{}', true", "'(]', false", "'{[]}', true", "'([{}])', true", "'([)]', false", "'', true", "'(', false", "')', false", "'{{{{}}}}', true", "'[({})]', true", "'[(])', false", "'[', false", "']', false", "'()()()()', true", "'(()', false", "'())', false", - "'{[()()]()}', true"}) - void - testIsValid(String input, boolean expected) { - assertEquals(expected, ValidParentheses.isValid(input)); - } - - @Test - void testNullInputThrows() { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> ValidParentheses.isValid(null)); - assertEquals("Input string cannot be null", ex.getMessage()); - } - - @ParameterizedTest(name = "Input: \"{0}\" β†’ throws IllegalArgumentException") - @CsvSource({"'a'", "'()a'", "'[123]'", "'{hello}'", "'( )'", "'\t'", "'\n'", "'@#$%'"}) - void testInvalidCharactersThrow(String input) { - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> ValidParentheses.isValid(input)); - assertTrue(ex.getMessage().startsWith("Unexpected character")); - } -} diff --git a/src/test/java/com/thealgorithms/strings/WordLadderTest.java b/src/test/java/com/thealgorithms/strings/WordLadderTest.java index 221953411da7..c029940abfb0 100644 --- a/src/test/java/com/thealgorithms/strings/WordLadderTest.java +++ b/src/test/java/com/thealgorithms/strings/WordLadderTest.java @@ -24,7 +24,7 @@ public class WordLadderTest { public void testWordLadder() { List wordList1 = Arrays.asList("hot", "dot", "dog", "lot", "log", "cog"); - assertEquals(WordLadder.ladderLength("hit", "cog", wordList1), 5); + assertEquals(5, WordLadder.ladderLength("hit", "cog", wordList1)); } /** @@ -39,7 +39,7 @@ public void testWordLadder() { public void testWordLadder2() { List wordList2 = Arrays.asList("hot", "dot", "dog", "lot", "log"); - assertEquals(WordLadder.ladderLength("hit", "cog", wordList2), 0); + assertEquals(0, WordLadder.ladderLength("hit", "cog", wordList2)); } /** @@ -54,7 +54,7 @@ public void testWordLadder2() { public void testWordLadder3() { List wordList3 = emptyList(); - assertEquals(WordLadder.ladderLength("hit", "cog", wordList3), 0); + assertEquals(0, WordLadder.ladderLength("hit", "cog", wordList3)); } @ParameterizedTest diff --git a/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java b/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java index 2cbbfe3d2dd8..9bf118c9b844 100644 --- a/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java +++ b/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java @@ -9,8 +9,8 @@ public class ZigZagPatternTest { public void testZigZagPattern() { String input1 = "HelloWorldFromJava"; String input2 = "javaIsAProgrammingLanguage"; - Assertions.assertEquals(ZigZagPattern.encode(input1, 4), "HooeWrrmalolFJvlda"); - Assertions.assertEquals(ZigZagPattern.encode(input2, 4), "jAaLgasPrmgaaevIrgmnnuaoig"); + Assertions.assertEquals("HooeWrrmalolFJvlda", ZigZagPattern.encode(input1, 4)); + Assertions.assertEquals("jAaLgasPrmgaaevIrgmnnuaoig", ZigZagPattern.encode(input2, 4)); // Edge cases Assertions.assertEquals("ABC", ZigZagPattern.encode("ABC", 1)); // Single row Assertions.assertEquals("A", ZigZagPattern.encode("A", 2)); // numRows > length of string