Skip to content

Commit 5142aa3

Browse files
authored
Merge branch 'TheAlgorithms:master' into master
2 parents 8bf1a0e + babc762 commit 5142aa3

File tree

8 files changed

+377
-1
lines changed

8 files changed

+377
-1
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
<plugin>
128128
<groupId>com.mebigfatguy.fb-contrib</groupId>
129129
<artifactId>fb-contrib</artifactId>
130-
<version>7.7.2</version>
130+
<version>7.7.3</version>
131131
</plugin>
132132
<plugin>
133133
<groupId>com.h3xstream.findsecbugs</groupId>

src/main/java/com/thealgorithms/maths/PerfectSquare.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ private PerfectSquare() {
1515
* <tt>false</tt>
1616
*/
1717
public static boolean isPerfectSquare(final int number) {
18+
if (number < 0) {
19+
return false;
20+
}
1821
final int sqrt = (int) Math.sqrt(number);
1922
return sqrt * sqrt == number;
2023
}
@@ -27,6 +30,9 @@ public static boolean isPerfectSquare(final int number) {
2730
* {@code false}
2831
*/
2932
public static boolean isPerfectSquareUsingPow(long number) {
33+
if (number < 0) {
34+
return false;
35+
}
3036
long a = (long) Math.pow(number, 1.0 / 2);
3137
return a * a == number;
3238
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.thealgorithms.searches;
2+
3+
import com.thealgorithms.devutils.searches.SearchAlgorithm;
4+
5+
/**
6+
* Searches for a key in a sorted array that has been rotated at an unknown pivot.
7+
*
8+
* <p>
9+
* Example:
10+
* {@code [8, 9, 10, 1, 2, 3, 4, 5, 6, 7]}
11+
*
12+
* <p>
13+
* This is a modified binary search. When the array contains no duplicates, the
14+
* time complexity is {@code O(log n)}. With duplicates, the algorithm still
15+
* works but may degrade to {@code O(n)} in the worst case.
16+
*
17+
* @see <a href="https://en.wikipedia.org/wiki/Search_in_rotated_sorted_array">Search in rotated sorted array</a>
18+
* @see SearchAlgorithm
19+
*/
20+
public final class RotatedBinarySearch implements SearchAlgorithm {
21+
22+
@Override
23+
public <T extends Comparable<T>> int find(T[] array, T key) {
24+
int left = 0;
25+
int right = array.length - 1;
26+
27+
while (left <= right) {
28+
int middle = (left + right) >>> 1;
29+
int cmp = key.compareTo(array[middle]);
30+
if (cmp == 0) {
31+
return middle;
32+
}
33+
34+
// Handle duplicates: if we cannot determine which side is sorted.
35+
if (array[left].compareTo(array[middle]) == 0 && array[middle].compareTo(array[right]) == 0) {
36+
left++;
37+
right--;
38+
continue;
39+
}
40+
41+
// Left half is sorted.
42+
if (array[left].compareTo(array[middle]) <= 0) {
43+
if (array[left].compareTo(key) <= 0 && key.compareTo(array[middle]) < 0) {
44+
right = middle - 1;
45+
} else {
46+
left = middle + 1;
47+
}
48+
} else {
49+
// Right half is sorted.
50+
if (array[middle].compareTo(key) < 0 && key.compareTo(array[right]) <= 0) {
51+
left = middle + 1;
52+
} else {
53+
right = middle - 1;
54+
}
55+
}
56+
}
57+
58+
return -1;
59+
}
60+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.thealgorithms.slidingwindow;
2+
3+
/**
4+
* Counts the number of "nice subarrays".
5+
* A nice subarray is a contiguous subarray that contains exactly k odd numbers.
6+
*
7+
* This implementation uses the sliding window technique.
8+
*
9+
* Reference:
10+
* https://leetcode.com/problems/count-number-of-nice-subarrays/
11+
*
12+
* Time Complexity: O(n)
13+
* Space Complexity: O(n)
14+
*/
15+
public final class CountNiceSubarrays {
16+
17+
// Private constructor to prevent instantiation
18+
private CountNiceSubarrays() {
19+
}
20+
21+
/**
22+
* Returns the count of subarrays containing exactly k odd numbers.
23+
*
24+
* @param nums input array of integers
25+
* @param k number of odd elements required in the subarray
26+
* @return number of nice subarrays
27+
*/
28+
public static int countNiceSubarrays(int[] nums, int k) {
29+
30+
int n = nums.length;
31+
32+
// Left pointer of the sliding window
33+
int left = 0;
34+
35+
// Tracks number of odd elements in the current window
36+
int oddCount = 0;
37+
38+
// Final answer: total number of nice subarrays
39+
int result = 0;
40+
41+
/*
42+
* memo[i] stores how many valid starting positions exist
43+
* when the left pointer is at index i.
44+
*
45+
* This avoids recomputing the same values again.
46+
*/
47+
int[] memo = new int[n];
48+
49+
// Right pointer moves forward to expand the window
50+
for (int right = 0; right < n; right++) {
51+
52+
// If current element is odd, increment odd count
53+
if ((nums[right] & 1) == 1) {
54+
oddCount++;
55+
}
56+
57+
/*
58+
* If oddCount exceeds k, shrink the window from the left
59+
* until oddCount becomes valid again.
60+
*/
61+
if (oddCount > k) {
62+
left += memo[left];
63+
oddCount--;
64+
}
65+
66+
/*
67+
* When the window contains exactly k odd numbers,
68+
* count all possible valid subarrays starting at `left`.
69+
*/
70+
if (oddCount == k) {
71+
72+
/*
73+
* If this left index hasn't been processed before,
74+
* count how many consecutive even numbers follow it.
75+
*/
76+
if (memo[left] == 0) {
77+
int count = 0;
78+
int temp = left;
79+
80+
// Count consecutive even numbers
81+
while ((nums[temp] & 1) == 0) {
82+
count++;
83+
temp++;
84+
}
85+
86+
/*
87+
* Number of valid subarrays starting at `left`
88+
* is (count of even numbers + 1)
89+
*/
90+
memo[left] = count + 1;
91+
}
92+
93+
// Add number of valid subarrays for this left position
94+
result += memo[left];
95+
}
96+
}
97+
return result;
98+
}
99+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.thealgorithms.sorts;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Tournament Sort algorithm implementation.
7+
*
8+
* Tournament sort builds a winner tree (a complete binary tree storing the index
9+
* of the smallest element in each subtree). It then repeatedly extracts the
10+
* winner (minimum) and updates the path from the removed leaf to the root.
11+
*
12+
* Time Complexity:
13+
* - Best case: O(n log n)
14+
* - Average case: O(n log n)
15+
* - Worst case: O(n log n)
16+
*
17+
* Space Complexity: O(n) – additional winner-tree storage
18+
*
19+
* @see <a href="https://en.wikipedia.org/wiki/Tournament_sort">Tournament Sort Algorithm</a>
20+
* @see SortAlgorithm
21+
*/
22+
public class TournamentSort implements SortAlgorithm {
23+
24+
@Override
25+
public <T extends Comparable<T>> T[] sort(T[] array) {
26+
if (array == null || array.length < 2) {
27+
return array;
28+
}
29+
30+
final int n = array.length;
31+
final int leafCount = nextPowerOfTwo(n);
32+
33+
// Winner tree represented as an array:
34+
// - Leaves live at [leafCount .. 2*leafCount)
35+
// - Internal nodes live at [1 .. leafCount)
36+
// Each node stores an index into the original array or -1 for "empty".
37+
final int[] tree = new int[2 * leafCount];
38+
Arrays.fill(tree, -1);
39+
40+
for (int i = 0; i < n; i++) {
41+
tree[leafCount + i] = i;
42+
}
43+
44+
for (int node = leafCount - 1; node >= 1; node--) {
45+
tree[node] = winnerIndex(array, tree[node * 2], tree[node * 2 + 1]);
46+
}
47+
48+
final T[] result = array.clone();
49+
for (int out = 0; out < n; out++) {
50+
final int winner = tree[1];
51+
result[out] = array[winner];
52+
53+
int node = leafCount + winner;
54+
tree[node] = -1;
55+
56+
for (node /= 2; node >= 1; node /= 2) {
57+
tree[node] = winnerIndex(array, tree[node * 2], tree[node * 2 + 1]);
58+
}
59+
}
60+
61+
System.arraycopy(result, 0, array, 0, n);
62+
return array;
63+
}
64+
65+
private static int nextPowerOfTwo(int n) {
66+
int power = 1;
67+
while (power < n) {
68+
power <<= 1;
69+
}
70+
return power;
71+
}
72+
73+
private static <T extends Comparable<T>> int winnerIndex(T[] array, int leftIndex, int rightIndex) {
74+
if (leftIndex == -1) {
75+
return rightIndex;
76+
}
77+
if (rightIndex == -1) {
78+
return leftIndex;
79+
}
80+
81+
// If equal, prefer the left element to keep ordering deterministic.
82+
return SortUtils.less(array[rightIndex], array[leftIndex]) ? rightIndex : leftIndex;
83+
}
84+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.thealgorithms.searches;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
class RotatedBinarySearchTest {
9+
10+
@Test
11+
void shouldFindElementInRotatedArrayLeftSide() {
12+
RotatedBinarySearch search = new RotatedBinarySearch();
13+
Integer[] array = {8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7};
14+
assertEquals(2, search.find(array, 10));
15+
}
16+
17+
@Test
18+
void shouldFindElementInRotatedArrayRightSide() {
19+
RotatedBinarySearch search = new RotatedBinarySearch();
20+
Integer[] array = {8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7};
21+
assertEquals(6, search.find(array, 2));
22+
}
23+
24+
@Test
25+
void shouldFindElementInNotRotatedArray() {
26+
RotatedBinarySearch search = new RotatedBinarySearch();
27+
Integer[] array = {1, 2, 3, 4, 5, 6, 7};
28+
assertEquals(4, search.find(array, 5));
29+
}
30+
31+
@Test
32+
void shouldReturnMinusOneWhenNotFound() {
33+
RotatedBinarySearch search = new RotatedBinarySearch();
34+
Integer[] array = {4, 5, 6, 7, 0, 1, 2};
35+
assertEquals(-1, search.find(array, 3));
36+
}
37+
38+
@Test
39+
void shouldHandleWhenMiddleIsGreaterThanKeyInRightSortedHalf() {
40+
RotatedBinarySearch search = new RotatedBinarySearch();
41+
Integer[] array = {6, 7, 0, 1, 2, 3, 4, 5};
42+
assertEquals(2, search.find(array, 0));
43+
}
44+
45+
@Test
46+
void shouldHandleDuplicates() {
47+
RotatedBinarySearch search = new RotatedBinarySearch();
48+
Integer[] array = {2, 2, 2, 3, 4, 2};
49+
int index = search.find(array, 3);
50+
assertTrue(index >= 0 && index < array.length);
51+
assertEquals(3, array[index]);
52+
}
53+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.thealgorithms.slidingwindow;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class CountNiceSubarraysTest {
8+
@Test
9+
void testExampleCase() {
10+
int[] nums = {1, 1, 2, 1, 1};
11+
assertEquals(2, CountNiceSubarrays.countNiceSubarrays(nums, 3));
12+
}
13+
14+
@Test
15+
void testAllEvenNumbers() {
16+
int[] nums = {2, 4, 6, 8};
17+
assertEquals(0, CountNiceSubarrays.countNiceSubarrays(nums, 1));
18+
}
19+
20+
@Test
21+
void testSingleOdd() {
22+
int[] nums = {1};
23+
assertEquals(1, CountNiceSubarrays.countNiceSubarrays(nums, 1));
24+
}
25+
26+
@Test
27+
void testMultipleChoices() {
28+
int[] nums = {2, 2, 1, 2, 2, 1, 2};
29+
assertEquals(6, CountNiceSubarrays.countNiceSubarrays(nums, 2));
30+
}
31+
32+
@Test
33+
void testTrailingEvenNumbers() {
34+
int[] nums = {1, 2, 2, 2};
35+
assertEquals(4, CountNiceSubarrays.countNiceSubarrays(nums, 1));
36+
}
37+
38+
@Test
39+
void testMultipleWindowShrinks() {
40+
int[] nums = {1, 1, 1, 1};
41+
assertEquals(3, CountNiceSubarrays.countNiceSubarrays(nums, 2));
42+
}
43+
44+
@Test
45+
void testEvensBetweenOdds() {
46+
int[] nums = {2, 1, 2, 1, 2};
47+
assertEquals(4, CountNiceSubarrays.countNiceSubarrays(nums, 2));
48+
}
49+
50+
@Test
51+
void testShrinkWithTrailingEvens() {
52+
int[] nums = {2, 2, 1, 2, 2, 1, 2, 2};
53+
assertEquals(9, CountNiceSubarrays.countNiceSubarrays(nums, 2));
54+
}
55+
}

0 commit comments

Comments
 (0)