Skip to content

Conversation

@Manan-09
Copy link
Owner

  • I have read CONTRIBUTING.md.
  • This pull request is all my own work -- I have not plagiarized it.
  • All filenames are in PascalCase.
  • All functions and variable names follow Java naming conventions.
  • All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.

Manan-09 and others added 8 commits September 23, 2023 10:26
…on (TheAlgorithms#4394)

* Enhance Minimum sum partition problem implementation

* Linter resolved

* Linter resolved

* Code review comments

* Code review comments

* Add validation for non-negative numbers

* Linter resolved

* style: fix formiatting

---------

Co-authored-by: Piotr Idzik <65706193+vil02@users.noreply.github.com>
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
Copy link
Owner Author

@Manan-09 Manan-09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR introduces significant improvements across multiple files, focusing on code quality, best practices, and efficiency. The refactoring to use generics, space optimization in dynamic programming problems, and clearer code structure are commendable.

Here's a detailed review:

General Feedback

This is an excellent PR. The major themes of the changes are:

  1. Generics: The MedianOfRunningArray class has been refactored to be generic, making it reusable for various numeric types while correctly handling type-specific operations like averaging. This is a significant design improvement.
  2. Space Optimization: MinimumPathSum and MinimumSumPartition have been optimized to use O(N) or O(sum/2) space, respectively, instead of O(M*N) or O(N*sum), which is a great improvement in efficiency for DP problems.
  3. Code Structure and Best Practices:
    • Utility classes (MinimumPathSum, MinimumSumPartition, FindMax, FindMin) are now declared final and have private constructors, enforcing their role.
    • Test methods have been correctly moved out of main algorithm classes.
    • Input validation (e.g., non-negative numbers in MinimumSumPartition) and handling of empty arrays are improved.
    • Use of final keyword for method parameters and local variables where appropriate.
  4. Test Coverage: New test cases have been added for the refactored and new functionalities, especially for MinimumSumPartition and the generic MedianOfRunningArray. Existing tests are also updated to reflect new behaviors (e.g., integer division for MedianOfRunningArrayInteger).

File-Specific Feedback

src/main/java/com/thealgorithms/dynamicprogramming/MinimumPathSum.java

  • Code Quality & Efficiency:

    • Space Optimization: The change from a O(mn) 2D dp array to an O(n) 1D dp array is a fantastic optimization. The logic for updating the 1D dp array (using dp[col - 1] for left and dp[col] for top) is correctly implemented.
    • The problem description was updated, removing "All numbers given are positive." This is consistent with the new test case involving negative numbers.
  • Best Practices:

    • Making the class final and adding a private constructor for a utility class is excellent.
    • Using final for method parameters (final int[][] grid) is good practice.
    • Descriptive variable names (numRows, numCols instead of m, n) improve readability.
  • Potential Bugs / Edge Cases:

    • The method correctly handles numCols == 0 (e.g., {{}}).
    • Recommendation: Add checks for grid == null or grid.length == 0. Currently, grid.length or grid[0].length would throw NullPointerException or ArrayIndexOutOfBoundsException respectively in these cases. While the problem statement often implies valid input, robust methods should handle these explicitly. Returning 0 or throwing an IllegalArgumentException would be appropriate depending on whether an empty grid should imply a path sum of 0 or an invalid operation. For this problem, returning 0 might be acceptable for an empty path.
    public static int minimumPathSum(final int[][] grid) {
        if (grid == null || grid.length == 0) {
            // Or throw new IllegalArgumentException("Grid cannot be null or empty.");
            return 0; 
        }
        int numRows = grid.length;
        int numCols = grid[0].length; // This would throw AIOOBE if grid = {{}}, so the next check is crucial.
        if (numCols == 0) { // Handles {{}} case where numRows=1, numCols=0
            return 0;
        }
        // ... rest of the code
    }

src/test/java/com/thealgorithms/dynamicprogramming/MinimumPathSumTest.java

  • Test Coverage: The new test testMinimumPathSumWithNegativeNumberGrid is a good addition, verifying the algorithm's behavior with negative numbers.

src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java

  • Code Quality & Efficiency:

    • Space Optimization: The rewrite of the dynamic programming algorithm from O(N*sum) space to O(sum/2) space is a significant improvement, demonstrating good understanding of subset sum variations.
    • The closestPartitionSum logic and the final return calculation sum - (2 * closestPartitionSum) are correct for finding the minimum difference.
  • Best Practices:

    • Making the class final and adding a private constructor is good.
    • Using final for method parameters.
    • The new throwIfInvalidInput helper method enforces the "non-negative integers" constraint, which is excellent input validation.
    • Using Arrays.stream(array).sum() for total sum is concise and readable.
  • Potential Bugs / Edge Cases:

    • The method handles an empty array ({}) correctly, returning 0.
    • Recommendation: Update throwIfInvalidInput to also check for null input array. Currently, Arrays.stream(array).sum() would throw a NullPointerException if array is null.
    private static void throwIfInvalidInput(final int[] array) {
        if (array == null) {
            throw new IllegalArgumentException("Input array cannot be null.");
        }
        if (Arrays.stream(array).anyMatch(a -> a < 0)) {
            throw new IllegalArgumentException("Input array should not contain negative number(s).");
        }
    }

src/test/java/com/thealgorithms/dynamicprogramming/MinimumSumPartitionTest.java (New File)

  • Test Coverage: This new test class provides excellent and comprehensive test cases, covering various scenarios (even/odd sum, single element, large numbers, empty array, negative numbers leading to exception). This is a strong addition.

src/main/java/com/thealgorithms/maths/FindMax.java and src/main/java/com/thealgorithms/maths/FindMin.java

  • Code Quality & Efficiency:
    • Initializing max/min with array[0] and iterating from i = 1 is slightly more efficient and robust than using Integer.MIN_VALUE/Integer.MAX_VALUE, especially if the array could contain only Integer.MIN_VALUE (for FindMax) or Integer.MAX_VALUE (for FindMin) values.
  • Best Practices:
    • Making classes final with private constructors and removing the main driver code are good refactorings.
    • Using final for method parameters.
  • Potential Bugs / Edge Cases:
    • Recommendation: Similar to the above, add a null check at the beginning of findMax and findMin methods. Currently, array.length would throw a NullPointerException if array is null. The existing IllegalArgumentException for empty arrays is good.

src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java

  • Code Quality & Best Practices:
    • Generics: Refactoring the class to be generic (abstract class MedianOfRunningArray<T extends Number & Comparable<T>>) is a fantastic design choice. It makes the median calculation reusable and type-safe for different numeric types.
    • The introduction of the calculateAverage abstract method correctly delegates type-specific averaging logic to concrete implementations, which is key for handling int, long, float, double correctly.
    • Updating e < minHeap.peek() to e.compareTo(minHeap.peek()) < 0 for generic comparability is correct.
    • Removing * 1.0 and / 2.0 in the median method ensures it returns type T directly when an odd number of elements are present, and uses the abstract calculateAverage method for even counts.
  • Readability & Style: The changes greatly enhance the flexibility and clarity of the design.

src/main/java/com/thealgorithms/misc/MedianOfRunningArray*.java (New Files)

  • Code Quality & Best Practices:
    • These concrete implementations (e.g., MedianOfRunningArrayInteger, MedianOfRunningArrayDouble, etc.) correctly extend the abstract base class and implement calculateAverage according to their respective numeric types (e.g., integer division for Integer, floating-point for Float/Double).
    • Using final and private constructors for these concrete utility classes is consistent.

src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java

  • Test Coverage:
    • The tests have been correctly updated to use specific MedianOfRunningArrayInteger (or other types), and assertions are adjusted for integer division where applicable (e.g., assertEquals(-1, stream.median()) instead of -1.5). This highlights the effectiveness of the generic design.
    • The new tests for Float, Byte, Long, and Double implementations are excellent, ensuring the generic solution works as expected across different number types and their specific average calculations.
    • The usage of assertEquals(Double.valueOf(11734567.83), stream.median(), .01); for doubles is appropriate for testing floating-point numbers.

Summary

This PR is exceptionally well-done. The changes demonstrate a strong understanding of Java best practices, design patterns (generics, utility classes), and algorithmic optimizations. The improved test coverage significantly increases confidence in the correctness of the new and refactored code.

The only minor recommendations are to add explicit null checks for input arrays in MinimumPathSum, MinimumSumPartition, FindMax, and FindMin for maximum robustness, but this does not detract from the high quality of the PR.

Overall Rating: Excellent.

Recommendation: Approve with minor suggestions (null checks).

@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09
Copy link
Owner Author

Here's a review of the pull request:


1. Summary of Code Changes

This PR introduces several improvements across various dynamic programming, maths, and miscellaneous utilities:

  • MinimumPathSum.java: Refactored to use O(n) space complexity, converted to a final utility class, and removed inline test methods. Added a test case for negative grid values.
  • MinimumSumPartition.java: Refactored to use O(sum/2) space complexity, converted to a final utility class, added input validation for negative numbers, and improved comments. A new dedicated test class MinimumSumPartitionTest.java was added.
  • FindMax.java / FindMin.java: Converted to final utility classes and removed inline test methods. Optimized loop iteration slightly.
  • MedianOfRunningArray.java: Major refactoring to use Java Generics (<T extends Number & Comparable<T>>), changing it to an abstract class. This allows type-safe median calculation for different numeric types.
  • New MedianOfRunningArray* classes: Introduced concrete implementations (Byte, Double, Float, Integer, Long) extending MedianOfRunningArray to handle type-specific average calculations.
  • Test files: Test cases for MinimumPathSum and MedianOfRunningArray were updated and expanded, and a new comprehensive test class was created for MinimumSumPartition.

2. Potential Issues and Suggested Fixes

  • MinimumPathSum.java - Insufficient Input Validation:
    • The current if (numCols == 0) check does not cover all invalid input scenarios. If grid is null or an empty array (new int[0][]) or an array with empty rows (e.g., new int[1][0]), it will lead to NullPointerException or ArrayIndexOutOfBoundsException.
    • Suggested Fix: Add more robust validation at the beginning of minimumPathSum method:
      public static int minimumPathSum(final int[][] grid) {
          if (grid == null || grid.length == 0 || grid[0].length == 0) {
              // Or throw IllegalArgumentException("Input grid cannot be null or empty.");
              return 0; // Assuming 0 for empty grid, or throw exception based on requirements.
          }
          int numRows = grid.length;
          int numCols = grid[0].length;
          // ... rest of the code
      }
    • Follow-up: Corresponding test cases should be added to MinimumPathSumTest.java to verify this validation.

3. Final Verdict

This PR significantly improves the code quality, robustness, and adherence to best practices across multiple files. The generic refactoring of MedianOfRunningArray is particularly well-executed, showcasing good object-oriented design.

Needs Changes. Address the input validation in MinimumPathSum.java and add corresponding test cases. Once that's done, this PR is ready to be approved.

@Manan-09 Manan-09 closed this Oct 19, 2025
@Manan-09 Manan-09 reopened this Oct 19, 2025
@Manan-09
Copy link
Owner Author

Hello team,

I've reviewed the attached PR. Overall, the changes are positive, improving code quality, consistency, and addressing some algorithmic efficiencies.

Summary of Code Changes

The PR introduces several improvements across multiple dynamic programming and mathematical utility classes:

  1. MinimumPathSum.java:

    • Optimized space complexity from O(mn) to O(n) by using a 1D DP array.
    • Removed test methods, indicating a move towards JUnit tests.
    • Added final modifier to the class and a private constructor, making it a utility class.
    • Removed the "All numbers given are positive" constraint and added a test for negative numbers.
  2. MinimumSumPartition.java:

    • Refactored the dynamic programming approach to be more concise, using a 1D DP array.
    • Added input validation to throw IllegalArgumentException for negative numbers.
    • Updated Javadoc comments for clarity and examples.
    • Made the class final with a private constructor.
    • A new JUnit test class MinimumSumPartitionTest.java was added.
  3. FindMax.java & FindMin.java:

    • Refactored to initialize max/min with array[0] instead of Integer.MIN_VALUE/Integer.MAX_VALUE, which is more robust and slightly more efficient.
    • Removed main methods containing direct test calls.
    • Made classes final with private constructors.
  4. MedianOfRunningArray.java:

    • Generalized the class to be type-agnostic using generics (<T extends Number & Comparable<T>>).
    • Introduced an abstract calculateAverage(T a, T b) method to handle type-specific averaging (e.g., integer division for Integer, floating-point division for Float).
    • Updated insert and median logic to use compareTo and the new calculateAverage method.
  5. New MedianOfRunningArray* classes:

    • Added concrete implementations (MedianOfRunningArrayByte, MedianOfRunningArrayDouble, MedianOfRunningArrayFloat, MedianOfRunningArrayInteger, MedianOfRunningArrayLong) that extend MedianOfRunningArray and provide specific implementations for calculateAverage.
  6. Test files:

    • MinimumPathSumTest.java: Added a test case for grids with negative numbers.
    • MinimumSumPartitionTest.java: New test class covering various scenarios including empty arrays and validation of negative inputs.
    • MedianOfRunningArrayTest.java: Updated to use MedianOfRunningArrayInteger and added comprehensive tests for different number types (Float, Byte, Long, Double).

Potential Issues and Suggested Fixes

  1. MinimumPathSum.java - Edge Case Handling for Empty Grid:

    • Issue: The current minimumPathSum method assumes grid is non-empty when accessing grid[0].length. If grid is new int[0][] (an empty array of rows), grid[0] will throw an ArrayIndexOutOfBoundsException.
    • Suggestion: Add a check for an empty grid at the beginning of the method.
    public static int minimumPathSum(final int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            // Return 0 as per existing behavior for numCols == 0, or throw an IllegalArgumentException.
            // Given the original context returns 0 for an empty column, returning 0 for an empty grid seems consistent.
            return 0;
        }
        int numRows = grid.length;
        int numCols = grid[0].length;
        // ... rest of the code
    }
  2. MedianOfRunningArray.java - Median Definition for Integer Types:

    • Issue: The median for an even number of elements is typically the average of the two middle elements, which can result in a non-integer value (e.g., median of {5, 10} is 7.5). The original MedianOfRunningArray returned a double for this case. The new generic design, particularly MedianOfRunningArrayInteger, performs integer division ((a + b) / 2), effectively truncating the median (e.g., median of {5, 10} becomes 7). While this is consistent with returning type T, it changes the mathematical definition of the median for integer inputs. The updated tests for Integer reflect this new behavior.
    • Suggestion: Clarify this design choice. If the intent is to always return a mathematically precise median (which may be a floating-point number even for integer inputs), median() should consistently return Double. If the intent is for median() to return T and truncate for integer types, this should be explicitly documented in the Javadoc of the MedianOfRunningArray class and its integer-specific implementations as a deviation from standard median definition. For a general "Algorithms" library, adhering to the standard mathematical definition is usually preferred.
    • Recommended Fix (if precise median is desired):
      • Change public T median() to public double median().
      • Modify calculateAverage in MedianOfRunningArray to return double and override it in specific implementations. E.g., for Integer: return (a.doubleValue() + b.doubleValue()) / 2.0d;.
      • Adjust maxHeap.peek() and minHeap.peek() calls to doubleValue() when calculating the median directly for odd counts, or when only one heap has an element.
  3. MinimumSumPartition.java - Minor Optimization:

    • Issue: The closestPartitionSum = Math.max(closestPartitionSum, j); update inside the nested loop is correct but slightly less efficient than finding the closestPartitionSum after the DP table dp is fully populated.
    • Suggestion: Move the closestPartitionSum calculation outside the loops.
    // ... after the nested loops for filling dp ...
    int closestPartitionSum = 0;
    for (int j = sum / 2; j >= 0; j--) { // Iterate backwards from sum/2 to 0
        if (dp[j]) {
            closestPartitionSum = j;
            break; // Found the largest possible sum for subset1 <= sum/2
        }
    }
    return sum - (2 * closestPartitionSum);

Final Verdict

Needs Changes.

The PR introduces good quality-of-life improvements and performance optimizations. However, the change in the MedianOfRunningArray's median() calculation for integer types potentially alters the algorithm's mathematical correctness for an even number of elements. This requires either a clear documentation of the design choice to truncate, or an adjustment to return a double for precision. Additionally, the edge case for an empty grid in MinimumPathSum should be handled.

Please address these points, especially the MedianOfRunningArray behavior, for approval.

@Manan-09
Copy link
Owner Author

MinimumPathSum.java - Edge Case Handling for Empty Grid:

Issue: The current minimumPathSum method assumes grid is non-empty when accessing grid[0].length. If grid is new int[0][] (an empty array of rows), grid[0] will throw an ArrayIndexOutOfBoundsException.
Suggestion: Add a check for an empty grid at the beginning of the method.
public static int minimumPathSum(final int[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) {
// Return 0 as per existing behavior for numCols == 0, or throw an IllegalArgumentException.
// Given the original context returns 0 for an empty column, returning 0 for an empty grid seems consistent.
return 0;
}
int numRows = grid.length;
int numCols = grid[0].length;
// ... rest of the code
}

@Manan-09 Manan-09 closed this Oct 20, 2025
@Manan-09 Manan-09 reopened this Oct 20, 2025
@Manan-09 Manan-09 closed this Oct 20, 2025
@Manan-09 Manan-09 reopened this Oct 20, 2025
@Manan-09 Manan-09 closed this Oct 20, 2025
@Manan-09 Manan-09 reopened this Oct 20, 2025
@Manan-09 Manan-09 closed this Oct 20, 2025
@Manan-09 Manan-09 reopened this Oct 20, 2025
@Manan-09 Manan-09 closed this Oct 20, 2025
@Manan-09 Manan-09 reopened this Oct 20, 2025
@Manan-09 Manan-09 closed this Oct 20, 2025
@Manan-09 Manan-09 reopened this Oct 20, 2025
@Manan-09
Copy link
Owner Author

Here's a concise code review:

1. Summary of code changes

This PR introduces significant improvements across multiple dynamic programming and utility classes:

  • MinimumPathSum.java: Implements a space-optimized O(N) solution for minimum path sum, changing the DP table from 2D to 1D. Class made final with a private constructor. Old test methods were removed.
  • MinimumSumPartition.java: Rewrites the algorithm for minimum sum partition using a space-optimized DP approach (O(sum)), and adds input validation for non-negative integers. Class made final with a private constructor.
  • FindMax.java / FindMin.java: Utility methods updated to be final with private constructors. The main methods for testing were removed, and the max/min initialization logic was improved.
  • MedianOfRunningArray.java: Refactors the class to use generics (<T extends Number & Comparable<T>>) to support various numeric types. It introduces an abstract method calculateAverage for type-specific median calculation.
  • New MedianOfRunningArray* classes: Five new concrete implementations (Byte, Double, Float, Integer, Long) are added, extending the generic base class and implementing calculateAverage.
  • Test Updates: Associated test classes were updated to reflect the changes, including new test cases for negative numbers in MinimumPathSum, comprehensive tests for MinimumSumPartition, and generic type testing for MedianOfRunningArray.

2. Potential issues and suggested fixes

  • MinimumPathSum.java - Input Validation:
    • Issue: The current code int numRows = grid.length; int numCols = grid[0].length; will throw an ArrayIndexOutOfBoundsException if the input grid is null or an empty array ({}). While numCols == 0 is handled for {{}}, the grid.length == 0 case is missed.
    • Fix: Add an explicit check at the beginning of the minimumPathSum method:
      public static int minimumPathSum(final int[][] grid) {
          if (grid == null || grid.length == 0) {
              return 0; // Or throw an IllegalArgumentException, depending on desired behavior for empty grid. Returning 0 aligns with original handling for numCols == 0.
          }
          // ... rest of existing code ...
      }

3. Final verdict

Needs changes.

The PR brings significant improvements in terms of efficiency, design, and adherence to best practices. The generic implementation for MedianOfRunningArray and the space optimizations are particularly commendable. The only outstanding issue is the robust handling of null or empty grid input in MinimumPathSum. Once addressed, this PR will be ready for approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants