Skip to content

Commit c7a0556

Browse files
Merge branch 'master' into feat/concurrent-merge-sort
2 parents fc8ab59 + f0b7778 commit c7a0556

8 files changed

Lines changed: 551 additions & 9 deletions

File tree

.github/workflows/codeql.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ jobs:
3030
distribution: 'temurin'
3131

3232
- name: Initialize CodeQL
33-
uses: github/codeql-action/init@v4.37.1
33+
uses: github/codeql-action/init@v4.37.3
3434
with:
3535
languages: 'java-kotlin'
3636

3737
- name: Build
3838
run: mvn --batch-mode --update-snapshots verify
3939

4040
- name: Perform CodeQL Analysis
41-
uses: github/codeql-action/analyze@v4.37.1
41+
uses: github/codeql-action/analyze@v4.37.3
4242
with:
4343
category: "/language:java-kotlin"
4444

@@ -55,12 +55,12 @@ jobs:
5555
uses: actions/checkout@v7
5656

5757
- name: Initialize CodeQL
58-
uses: github/codeql-action/init@v4.37.1
58+
uses: github/codeql-action/init@v4.37.3
5959
with:
6060
languages: 'actions'
6161

6262
- name: Perform CodeQL Analysis
63-
uses: github/codeql-action/analyze@v4.37.1
63+
uses: github/codeql-action/analyze@v4.37.3
6464
with:
6565
category: "/language:actions"
6666
...

.github/workflows/project_structure.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- uses: actions/checkout@v7
19-
- uses: actions/setup-python@v6.3.0
19+
- uses: actions/setup-python@v7.0.0
2020
with:
2121
python-version: '3.13'
2222

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.LinkedList;
4+
import java.util.Queue;
5+
6+
/**
7+
* Multi-source Breadth-First Search (BFS) implementation for the Rotting Oranges problem.
8+
*
9+
* <p>Algorithm explanation:
10+
* https://en.wikipedia.org/wiki/Breadth-first_search
11+
*
12+
* <p>Problem reference:
13+
* https://leetcode.com/problems/rotting-oranges/
14+
*
15+
* <p>Given a grid where:
16+
* <ul>
17+
* <li>0 represents an empty cell</li>
18+
* <li>1 represents a fresh orange</li>
19+
* <li>2 represents a rotten orange</li>
20+
* </ul>
21+
*
22+
* <p>Returns the minimum number of minutes required for all fresh oranges
23+
* to become rotten. Returns {@code -1} if it is impossible.
24+
*
25+
* <p>Time Complexity: O(m × n)
26+
* <br>Space Complexity: O(m × n)
27+
*/
28+
public class RottingOranges {
29+
30+
private static final int[] DEL_ROW = {-1, 0, 1, 0};
31+
private static final int[] DEL_COL = {0, 1, 0, -1};
32+
33+
private static final class Cell {
34+
private final int row;
35+
private final int col;
36+
private final int minute;
37+
38+
Cell(int row, int col, int minute) {
39+
this.row = row;
40+
this.col = col;
41+
this.minute = minute;
42+
}
43+
}
44+
45+
/**
46+
* Executes the Rotting Oranges algorithm.
47+
*
48+
* @param grid the input grid
49+
* @return minimum minutes required to rot all fresh oranges,
50+
* or -1 if impossible
51+
*/
52+
public int run(int[][] grid) {
53+
54+
if (grid == null || grid.length == 0 || grid[0].length == 0) {
55+
return 0;
56+
}
57+
58+
int rows = grid.length;
59+
int cols = grid[0].length;
60+
61+
// Create a copy so original input is not modified
62+
int[][] copy = new int[rows][cols];
63+
64+
for (int i = 0; i < rows; i++) {
65+
copy[i] = grid[i].clone();
66+
}
67+
68+
Queue<Cell> queue = new LinkedList<>();
69+
int freshOranges = 0;
70+
71+
// Find all rotten oranges and count fresh oranges
72+
for (int row = 0; row < rows; row++) {
73+
for (int col = 0; col < cols; col++) {
74+
75+
if (copy[row][col] == 2) {
76+
queue.offer(new Cell(row, col, 0));
77+
} else if (copy[row][col] == 1) {
78+
freshOranges++;
79+
}
80+
}
81+
}
82+
83+
if (freshOranges == 0) {
84+
return 0;
85+
}
86+
87+
int rottedFresh = 0;
88+
int minutes = 0;
89+
90+
// Multi-source BFS
91+
while (!queue.isEmpty()) {
92+
93+
Cell current = queue.poll();
94+
95+
minutes = Math.max(minutes, current.minute);
96+
97+
for (int i = 0; i < 4; i++) {
98+
99+
int newRow = current.row + DEL_ROW[i];
100+
int newCol = current.col + DEL_COL[i];
101+
102+
if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && copy[newRow][newCol] == 1) {
103+
104+
copy[newRow][newCol] = 2;
105+
rottedFresh++;
106+
107+
queue.offer(new Cell(newRow, newCol, current.minute + 1));
108+
}
109+
}
110+
}
111+
112+
return rottedFresh == freshOranges ? minutes : -1;
113+
}
114+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.thealgorithms.machinelearning;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
/**
7+
* Multinomial Naive Bayes classifier.
8+
*
9+
* <p>Suited to discrete, count-based features (e.g. word frequencies in text
10+
* classification). Class priors and feature likelihoods are estimated from
11+
* training data with Laplace (add-alpha) smoothing to avoid zero
12+
* probabilities for unseen feature/class combinations. Predictions are made
13+
* by comparing summed log-probabilities across classes, which avoids the
14+
* numerical underflow that repeated multiplication of small probabilities
15+
* would cause.
16+
*
17+
* <p>Reference: <a href="https://en.wikipedia.org/wiki/Naive_Bayes_classifier">
18+
* Naive Bayes classifier</a>
19+
*
20+
* @author Vraj Prajapati(Rosander0)
21+
*/
22+
public final class MultinomialNaiveBayesClassifier {
23+
24+
private final double alpha;
25+
private final Map<Integer, Double> logPriors;
26+
private final Map<Integer, double[]> logLikelihoods;
27+
private int numFeatures;
28+
29+
/**
30+
* Constructs a classifier with the given Laplace smoothing parameter.
31+
*
32+
* @param alpha smoothing constant; must be greater than 0. A value of 1.0
33+
* corresponds to standard Laplace smoothing.
34+
*/
35+
public MultinomialNaiveBayesClassifier(double alpha) {
36+
if (alpha <= 0) {
37+
throw new IllegalArgumentException("alpha must be greater than 0");
38+
}
39+
this.alpha = alpha;
40+
this.logPriors = new HashMap<>();
41+
this.logLikelihoods = new HashMap<>();
42+
}
43+
44+
/** Constructs a classifier using the standard Laplace smoothing constant of 1.0. */
45+
public MultinomialNaiveBayesClassifier() {
46+
this(1.0);
47+
}
48+
49+
/**
50+
* Fits the classifier on the given feature matrix and labels.
51+
*
52+
* @param features training samples, each row a vector of non-negative
53+
* feature counts
54+
* @param labels class label for each row of {@code features}
55+
*/
56+
public void fit(double[][] features, int[] labels) {
57+
if (features.length == 0 || features.length != labels.length) {
58+
throw new IllegalArgumentException("features and labels must be non-empty and of equal length");
59+
}
60+
logPriors.clear();
61+
logLikelihoods.clear();
62+
numFeatures = features[0].length;
63+
64+
Map<Integer, Integer> classCounts = new HashMap<>();
65+
Map<Integer, double[]> featureSums = new HashMap<>();
66+
Map<Integer, Double> totalFeatureCount = new HashMap<>();
67+
68+
for (int i = 0; i < features.length; i++) {
69+
int label = labels[i];
70+
classCounts.merge(label, 1, Integer::sum);
71+
double[] sums = featureSums.computeIfAbsent(label, k -> new double[numFeatures]);
72+
double total = totalFeatureCount.getOrDefault(label, 0.0);
73+
for (int j = 0; j < numFeatures; j++) {
74+
sums[j] += features[i][j];
75+
total += features[i][j];
76+
}
77+
totalFeatureCount.put(label, total);
78+
}
79+
80+
int totalSamples = features.length;
81+
for (Map.Entry<Integer, double[]> entry : featureSums.entrySet()) {
82+
int label = entry.getKey();
83+
double[] sums = entry.getValue();
84+
int count = classCounts.getOrDefault(label, 0);
85+
double total = totalFeatureCount.getOrDefault(label, 0.0);
86+
87+
logPriors.put(label, Math.log((double) count / totalSamples));
88+
89+
double denom = total + alpha * numFeatures;
90+
double[] logLikelihood = new double[numFeatures];
91+
for (int j = 0; j < numFeatures; j++) {
92+
logLikelihood[j] = Math.log((sums[j] + alpha) / denom);
93+
}
94+
logLikelihoods.put(label, logLikelihood);
95+
}
96+
}
97+
98+
/**
99+
* Predicts the most likely class for a single sample.
100+
*
101+
* @param sample feature vector of non-negative counts
102+
* @return the predicted class label
103+
*/
104+
public int predict(double[] sample) {
105+
if (logPriors.isEmpty()) {
106+
throw new IllegalStateException("classifier has not been fitted");
107+
}
108+
if (sample.length != numFeatures) {
109+
throw new IllegalArgumentException("sample length must match training feature count");
110+
}
111+
112+
int bestLabel = -1;
113+
double bestScore = Double.NEGATIVE_INFINITY;
114+
115+
for (Map.Entry<Integer, double[]> entry : logLikelihoods.entrySet()) {
116+
int label = entry.getKey();
117+
double[] logLikelihood = entry.getValue();
118+
double score = logPriors.getOrDefault(label, Double.NEGATIVE_INFINITY);
119+
for (int j = 0; j < numFeatures; j++) {
120+
score += sample[j] * logLikelihood[j];
121+
}
122+
if (score > bestScore) {
123+
bestScore = score;
124+
bestLabel = label;
125+
}
126+
}
127+
return bestLabel;
128+
}
129+
130+
/**
131+
* Predicts class labels for a batch of samples.
132+
*
133+
* @param samples feature vectors of non-negative counts
134+
* @return predicted class label for each row of {@code samples}
135+
*/
136+
public int[] predict(double[][] samples) {
137+
int[] predictions = new int[samples.length];
138+
for (int i = 0; i < samples.length; i++) {
139+
predictions[i] = predict(samples[i]);
140+
}
141+
return predictions;
142+
}
143+
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
* Find minimum number of perfect squares that sum to given number
66
*
77
* @see <a href="https://en.wikipedia.org/wiki/Lagrange%27s_four-square_theorem">Lagrange's Four Square Theorem</a>
8-
* @author BEASTSHRIRAM
98
*/
109
public final class SumOfSquares {
1110

@@ -16,10 +15,15 @@ private SumOfSquares() {
1615
/**
1716
* Find minimum number of perfect squares that sum to n
1817
*
19-
* @param n the target number
18+
* @param n the target number (must be non-negative)
2019
* @return minimum number of squares needed
20+
* @throws IllegalArgumentException if n is negative
2121
*/
2222
public static int minSquares(int n) {
23+
if (n < 0) {
24+
throw new IllegalArgumentException("Input must be non-negative");
25+
}
26+
2327
if (isPerfectSquare(n)) {
2428
return 1;
2529
}

0 commit comments

Comments
 (0)