Problem overview
The method maxValNumOfOccurrences receives an Array[Int] called nums and returns an Array[Int] with two entries: the largest value in nums, and how many times that value appears.
For nums = Array(2, 7, 11, 8, 11, 8, 3, 11) the largest value is 11, and it appears three times, so the answer is [11, 3]. The statement asks for O(n) time and O(1) extra space, and the follow-up asks whether one pass over the array is enough.
Approach: a running maximum with a running count
Finding only the maximum is a familiar pattern: keep the largest value seen so far and replace it whenever something bigger shows up. Counting how often the maximum occurs could be a second loop after the first one, which is still O(n). But both facts can be maintained together in a single loop.
For each element value, exactly one of three things is true:
valueis greater than the current maximum. The previous maximum, and every occurrence counted for it, no longer matters. The new maximum has been seen once, so the count resets to 1.valueequals the current maximum. This is one more occurrence, so the count goes up by 1.valueis smaller. Nothing changes.
When the loop ends, the pair (maximum, count) describes the whole array.
Tracing the example
On Array(2, 7, 11, 8, 11, 8, 3, 11) the state starts as maxVal = 2, count = 0. The first element, 2, equals the maximum, so the count becomes 1. Then 7 and 11 are each larger, so the maximum becomes 7 and then 11, with the count reset to 1 both times. The 8 is smaller and changes nothing. The next 11 raises the count to 2, the 8 and the 3 change nothing, and the last 11 raises the count to 3. The method returns [11, 3].
The two-pass version, which finds the maximum first and counts it in a second loop, gives the same answer with twice the work. The single pass is what the follow-up asks for.
Step by step
- Declare two
vars:maxValset tonums(0)andcountset to 0. The maximum starts as a real element of the array, which keeps the result right even when every number is negative. The count starts at 0, not 1, because the loop will visit the first element again and count it then; with 1 the first element would be counted twice. Scala indexes arrays with parentheses, not square brackets. - Loop over the array with
for (value <- nums). The arrow reads "for eachvalueinnums". - Test
value > maxValinside the loop. It must be strict: an equal value is another occurrence, not a new maximum. - In that branch, set
maxVal = valueandcount = 1. This discards the occurrences of the old, smaller maximum. - Add
else if (value == maxVal)after the closing brace of the first branch, and increment withcount += 1. Scala has no++operator. - After the loop, return
Array(maxVal, count), in that order.Array(a, b)builds an array from its arguments, and the element typeIntis inferred.
The solution in Scala
class Solution {
def maxValNumOfOccurrences(nums: Array[Int]): Array[Int] = {
var maxVal = nums(0)
var count = 0
for (value <- nums) {
if (value > maxVal) {
maxVal = value
count = 1
} else if (value == maxVal) {
count += 1
}
}
return Array(maxVal, count)
}
}
The Scala details worth a closer look:
varversusval. Both variables change during the loop, so they must bevar. The loop variablevalueis a freshvalon every iteration.- The array is not modified. The loop only reads
nums. The returned array is a new one with two elements. - Printing an array.
println(Array(11, 3))doesn't show the elements; it prints a type tag and a hash code, something like[I@b684286. To see the contents, usemkString:Array(11, 3).mkString("[", ", ", "]")gives[11, 3]. - Explicit
return. Leaving it off, so the last line is justArray(maxVal, count), is the more usual Scala style and means the same thing. - The type is
Int. The values are 32-bit integers, and nothing in this method adds or multiplies them, so there is no overflow to worry about. The count can't exceed the array's length.
Complexity
Time: O(n). The loop visits each element once and does a constant amount of work for it.
Space: O(1). Besides the two-element result, the method keeps two integers, no matter how long nums is.
Edge cases and common mistakes
- All negative numbers. For
Array(-5, -2, -9, -2)the result is[-2, 2]. StartingmaxValat 0 would wrongly report 0 with a count of 0. - One element.
Array(4)gives[4, 1]: the first element is compared with itself, equals the maximum, and is counted once. - All elements equal.
Array(3, 3, 3)gives[3, 3]. - Maximum at the end.
Array(1, 2, 3)gives[3, 1]. The count is reset to 1 when 3 arrives, which is why the earlier counts don't leak into the answer. - Empty array.
nums(0)throws anArrayIndexOutOfBoundsException("Index 0 out of bounds for length 0"). The problem implies at least one element; if empty input were possible, the method would need to checknums.isEmptyfirst. - Forgetting the reset. If the
count = 1line is missing, a new maximum inherits the old count. - Using
>=in the first test. Then equal values would also go through the reset branch and the count would never exceed 1.
Testing
Array(2, 7, 11, 8, 11, 8, 3, 11)gives[11, 3].Array(-5, -2, -9, -2)gives[-2, 2].Array(4)gives[4, 1].Array(3, 3, 3)gives[3, 3].Array(1, 2, 3)gives[3, 1].
