Problem Statement (Asked By Airbnb)
Given a list of integers, write a function to compute the largest sum of numbers such that no two numbers are adjacent in the list. The input may contain zero or negative values.
For example, [2, 4, 6, 2, 5] should return 13, since we pick 2, 6, and 5. [5, 1, 1, 5] should return 10, since we pick 5 and 5.
Can you optimize your solution to run in O(N) time and use constant space?
Problem link: https://leetcode.com/problems/count-univalue-subtrees/description/
Disclaimer: Try solving the problem on your own first! Use this solution only as a reference to learn and improve.
Solution:
This problem appears straightforward initially but contains some complexities. A greedy approach like picking the largest number, then the next largest non-adjacent number, and so on, doesn’t work consistently due to edge cases.
Instead, we can solve this problem recursively. Suppose we have a function that already computes the maximum sum of non-adjacent numbers for smaller arrays. Using this, we can decide between two cases:
- Exclude the first element and compute the result for the subarray starting from the second element.
- Include the first element and compute the result for the subarray starting from the third element.
Thus, the solution can be expressed as:
max(a[1:],a[0]+a[2:])
Approach 1: Recursive Solution (Inefficient, O(2^n)):
public class LargestNonAdjacentSum {
public static int largestNonAdjacent(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return Math.max(0, arr[0]);
}
return Math.max(
largestNonAdjacent(java.util.Arrays.copyOfRange(arr, 1, arr.length)),
arr[0] + largestNonAdjacent(java.util.Arrays.copyOfRange(arr, 2, arr.length))
);
}
public static void main(String[] args) {
int[] arr = {3, 2, 5, 10, 7};
System.out.println("Max Sum: " + largestNonAdjacent(arr)); // Output: 15
}
}
This recursive solution works but is highly inefficient due to redundant calculations.
Approach 2: Optimized Dynamic Programming Solution (O(n)):
We can use dynamic programming to avoid redundant calculations by storing the maximum sum of non-adjacent numbers up to each index in a cache.
public class LargestNonAdjacentSum {
public static int largestNonAdjacent(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return Math.max(0, arr[0]);
}
int[] cache = new int[arr.length];
cache[0] = Math.max(0, arr[0]);
cache[1] = Math.max(cache[0], arr[1]);
for (int i = 2; i < arr.length; i++) {
cache[i] = Math.max(arr[i] + cache[i - 2], cache[i - 1]);
}
return cache[arr.length - 1];
}
public static void main(String[] args) {
int[] arr = {3, 2, 5, 10, 7};
System.out.println("Max Sum: " + largestNonAdjacent(arr)); // Output: 15
}
}
This solution runs in O(n) time but uses O(n) space.
Approach 3: Further Optimized Dynamic Programming (O(n) Time, O(1) Space):
Since we only need the last two values of the cache at any point, we can reduce the space complexity to O(1).
public class LargestNonAdjacentSum {
public static int largestNonAdjacent(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return Math.max(0, arr[0]);
}
int maxExcludingLast = Math.max(0, arr[0]);
int maxIncludingLast = Math.max(maxExcludingLast, arr[1]);
for (int i = 2; i < arr.length; i++) {
int current = Math.max(maxIncludingLast, maxExcludingLast + arr[i]);
maxExcludingLast = maxIncludingLast;
maxIncludingLast = current;
}
return maxIncludingLast;
}
public static void main(String[] args) {
int[] arr = {3, 2, 5, 10, 7};
System.out.println("Max Sum: " + largestNonAdjacent(arr)); // Output: 15
}
}
Explanation of Optimized Approach:
- Base Cases:
- For an empty array, the result is 0.
- For a single-element array, the result is the maximum of 0 or the element itself.
- Iterative Calculation:
maxExcludingLast: Stores the maximum sum excluding the current element.maxIncludingLast: Stores the maximum sum including the current element.- For each element, update these values using the relation:
maxIncludingLast=max(maxIncludingLast,maxExcludingLast+current element)
Time Complexity: O(n)
Space Complexity: O(1)
Did this solution help you understand the concept better? Let me know your thoughts in the comments below! Don’t forget to follow us on Instagram @coderz.py for more DSA tips and solutions to ace your coding interviews.
Leave a Comment
You must be logged in to post a comment.