Problem overview
The method MaxValNumOfOccurrences takes an int[] named nums and returns a two-element int[]: the largest value in the array, then how many times it appears.
For nums = [2, 7, 11, 8, 11, 8, 3, 11] the maximum is 11, and it shows up at three positions, so the result is [11, 3].
The statement asks for O(n) time and O(1) extra space, and the follow-up asks for a single pass over the array.
Approach
Start with the easier question: what is the maximum? Keep a variable maxVal holding the largest value seen so far, begin with the first element, and replace it whenever a bigger element appears. This is the same idea as picking the smallest of three numbers, applied to a loop.
The simple way to get the count is a second loop: after maxVal is known, walk the array again and count the elements equal to it. That is two passes, still O(n).
One pass is possible because the count only depends on what has happened so far. For each element val there are three cases:
valis greater thanmaxVal. A new maximum. Every earlier count belonged to a smaller value, so reset the count to 1.valequalsmaxVal. Another copy of the current maximum, so add 1 to the count.valis smaller. Ignore it.
On [2, 7, 11, 8, 11, 8, 3, 11] the pair (maxVal, count) goes (2, 1), (7, 1), (11, 1), then 8 changes nothing, (11, 2), 8 and 3 change nothing, and the last 11 gives (11, 3).
Step by step
- Declare both variables in one statement.
maxValstarts atnums[0], so it is always a real element. Starting at 0 would be wrong for an array of negative numbers.countstarts at 0, not 1, because the loop will visit the first element too and count it through the "equal" case. - Loop over the values with
foreach. The loop needs no index, only each element in turn. - Test for a larger value with a strict
>. An equal value is not a new maximum. - Handle the new maximum. Store
valinmaxValand setcountto 1. - Add an
else iffor equality. It incrementscount. Because it is anelse if, a value that just replaced the maximum is not counted a second time. - Return both numbers in an array, the maximum first.
The solution in C#
class Solution {
public int[] MaxValNumOfOccurrences(int[] nums) {
int maxVal = nums[0], count = 0;
foreach (int val in nums) {
if (val > maxVal) {
maxVal = val;
count = 1;
} else if (val == maxVal) {
count++;
}
}
return new int[]{maxVal, count};
}
}
The C# details worth noting:
- Several variables in one declaration.
int maxVal = nums[0], count = 0;declares twointvariables, each with its own initializer. foreach. It reads each element of the array in order. The loop variablevalis read-only, so you can't assign to it inside the loop, which is fine here because the code only reads it.- The first element is visited twice. It initializes
maxValand thenforeachvisits it as well. It is not greater than itself, so it lands in theelse ifbranch and gets counted once. That is whycountstarts at 0. - Returning two values.
new int[]{maxVal, count}creates an array of length 2 and fills it in one expression. The array has a fixed length, which suits a pair like this.
The two-pass version, for comparison
If you find the single-pass logic hard to trust at first, write the two-pass version and compare results. It separates the two jobs, finding the maximum and counting it:
int maxVal = nums[0];
foreach (int val in nums) {
if (val > maxVal) {
maxVal = val;
}
}
int count = 0;
foreach (int val in nums) {
if (val == maxVal) {
count++;
}
}
return new int[]{maxVal, count};
It gives [11, 3] for the example, just like the one-pass solution. Both are O(n), and the one-pass version walks the array only once. The reason it works is the reset: when a bigger value arrives, everything counted so far was counted for a smaller maximum, so the count restarts at 1. After the last element, count describes only the final maxVal.
Note that the two foreach loops can both declare a variable named val. The variable belongs to its loop, so the second loop's val is a new one, not a redeclaration.
Complexity
Time: O(n). The loop visits each element once and does a constant amount of work for each.
Space: O(1). Two integers, plus the two-element result array, whose size does not depend on n.
Edge cases and common mistakes
- All negative numbers.
[-4, -4, -9]returns[-4, 2]. It works becausemaxValstarts from an element, not from 0. - One element.
[5]returns[5, 1]. - All elements equal.
[3, 3, 3]returns[3, 3]. - Starting
countat 1. The first element would then be counted twice, giving[11, 4]for the example. - Forgetting the reset. If
count = 1is missing when a new maximum appears, occurrences of smaller values leak into the answer. In[1, 2, 3]the result would be 3 instead of 1. - Using
>=in the first test. Equal values would then reset the count to 1 instead of adding to it. - An empty array.
nums[0]throws anIndexOutOfRangeException. The lesson's tests don't use an empty array, but real code would checknums.Lengthfirst.
Testing
[2, 7, 11, 8, 11, 8, 3, 11]gives[11, 3].[-4, -4, -9]gives[-4, 2].[5]gives[5, 1].[1, 2, 3]gives[3, 1].[3, 3, 3]gives[3, 3].