Facebook Pixel

1854. Maximum Population Year

Problem Description

You are given information about the birth and death years of multiple people in a 2D array called logs. Each entry logs[i] = [birth_i, death_i] tells you when the i-th person was born and when they died.

Your task is to find which year had the highest population alive. When counting the population for any given year x:

  • A person is counted as alive in year x if x is within the range [birth_i, death_i - 1]
  • This means a person is alive from their birth year up to (but not including) their death year
  • A person who dies in year x is NOT counted in that year's population

If multiple years have the same maximum population, return the earliest year.

For example, if a person has [birth=1950, death=1960], they are counted as alive in years 1950, 1951, 1952, ..., 1959, but NOT in 1960.

The solution uses a difference array technique to efficiently track population changes. Since all years fall within the range [1950, 2050], we can use an array of size 101 where index i represents year 1950 + i. For each person, we mark +1 at their birth year and -1 at their death year. By computing the prefix sum of this array, we can find the population at each year and identify the year with maximum population.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The naive approach would be to check each year from 1950 to 2050 and count how many people were alive in that year by iterating through all the logs. This would take O(100 * n) time where n is the number of people.

However, we can think about this problem differently. Instead of counting the population for each year individually, we can track the changes in population. When someone is born, the population increases by 1. When someone dies, the population decreases by 1.

This leads us to the key insight: we only need to mark the events (births and deaths) and then calculate the running population. If we mark +1 for each birth year and -1 for each death year, then the population at any year is simply the sum of all the marks up to that year.

For example, if person A lives from 1950-1955 and person B lives from 1952-1957:

  • At year 1950: +1 (A born)
  • At year 1952: +1 (B born)
  • At year 1955: -1 (A dies)
  • At year 1957: -1 (B dies)

The population at any year is the cumulative sum up to that point:

  • 1950-1951: population = 1
  • 1952-1954: population = 2
  • 1955-1956: population = 1
  • 1957 onwards: population = 0

This technique is called a "difference array" because we're storing the differences (changes) rather than the actual values. By computing the prefix sum of these differences, we reconstruct the actual population values efficiently in just one pass.

Learn more about Prefix Sum patterns.

Solution Approach

Following the difference array approach, here's how we implement the solution:

  1. Initialize the difference array: Create an array d of size 101 to cover all possible years from 1950 to 2050. Each index i represents year 1950 + i. Initialize all values to 0.

  2. Mark population changes: For each person in logs:

    • Convert birth and death years to array indices by subtracting 1950 (the offset)
    • Increment d[birth - 1950] by 1 (person becomes alive)
    • Decrement d[death - 1950] by 1 (person dies)
  3. Calculate running population: Traverse the array d while maintaining:

    • s: the running sum (current population)
    • mx: the maximum population seen so far
    • j: the index (year) with maximum population

    For each index i:

    • Add d[i] to the running sum s
    • If current population s exceeds mx, update both mx and j
  4. Return the result: Add 1950 back to index j to get the actual year

The code implementation:

d = [0] * 101                    # Difference array for years 1950-2050
offset = 1950
for a, b in logs:
    a, b = a - offset, b - offset # Convert to array indices
    d[a] += 1                     # Birth: population increases
    d[b] -= 1                     # Death: population decreases

s = mx = j = 0                    # Running sum, max population, best year
for i, x in enumerate(d):
    s += x                        # Update running population
    if mx < s:                    # Found new maximum
        mx, j = s, i              # Update max and year
return j + offset                 # Convert index back to actual year

This solution runs in O(n) time where n is the number of people, and uses O(1) space since the array size is fixed at 101.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with 3 people:

  • Person A: [1952, 1956] (alive in years 1952, 1953, 1954, 1955)
  • Person B: [1954, 1958] (alive in years 1954, 1955, 1956, 1957)
  • Person C: [1953, 1955] (alive in years 1953, 1954)

Step 1: Initialize difference array We create an array d of size 101 (for years 1950-2050). Initially all zeros.

Step 2: Mark population changes For each person, we mark births (+1) and deaths (-1):

  • Person A: d[1952-1950] += 1d[2] = 1, and d[1956-1950] -= 1d[6] = -1
  • Person B: d[1954-1950] += 1d[4] = 1, and d[1958-1950] -= 1d[8] = -1
  • Person C: d[1953-1950] += 1d[3] = 1, and d[1955-1950] -= 1d[5] = -1

Our difference array now looks like (showing only relevant indices):

Index: 0  1  2  3  4  5  6  7  8
Value: 0  0  1  1  1 -1 -1  0 -1
Year:  50 51 52 53 54 55 56 57 58

Step 3: Calculate running population We traverse the array, maintaining a running sum:

  • Index 0-1: sum = 0 (no one alive)
  • Index 2: sum = 0 + 1 = 1 (Person A born, population = 1)
  • Index 3: sum = 1 + 1 = 2 (Person C born, population = 2)
  • Index 4: sum = 2 + 1 = 3 (Person B born, population = 3) ← Maximum!
  • Index 5: sum = 3 + (-1) = 2 (Person C dies, population = 2)
  • Index 6: sum = 2 + (-1) = 1 (Person A dies, population = 1)
  • Index 7: sum = 1 + 0 = 1 (no change)
  • Index 8: sum = 1 + (-1) = 0 (Person B dies, population = 0)

The maximum population is 3, occurring at index 4.

Step 4: Return result Index 4 corresponds to year 1950 + 4 = 1954

We can verify: In 1954, all three people are alive (A: 1952-1955, B: 1954-1957, C: 1953-1954), giving us the maximum population of 3.

Solution Implementation

1class Solution:
2    def maximumPopulation(self, logs: List[List[int]]) -> int:
3        # Create a difference array to track population changes
4        # Index represents years from 1950 to 2050 (101 years total)
5        population_changes = [0] * 101
6        base_year = 1950
7      
8        # Mark population changes in the difference array
9        # +1 for birth year, -1 for death year
10        for birth_year, death_year in logs:
11            # Convert absolute years to array indices (0-based)
12            birth_index = birth_year - base_year
13            death_index = death_year - base_year
14          
15            # Person is alive starting from birth year
16            population_changes[birth_index] += 1
17            # Person is no longer alive starting from death year
18            population_changes[death_index] -= 1
19      
20        # Find the year with maximum population using prefix sum
21        current_population = 0
22        max_population = 0
23        year_with_max_population = 0
24      
25        # Iterate through the difference array to calculate running population
26        for year_index, population_change in enumerate(population_changes):
27            # Update current population with the change
28            current_population += population_change
29          
30            # Check if we found a new maximum population
31            # Using < ensures we return the earliest year in case of ties
32            if max_population < current_population:
33                max_population = current_population
34                year_with_max_population = year_index
35      
36        # Convert array index back to actual year
37        return year_with_max_population + base_year
38
1class Solution {
2    public int maximumPopulation(int[][] logs) {
3        // Array to track population changes for years 1950-2050 (101 years)
4        int[] populationChanges = new int[101];
5        final int YEAR_OFFSET = 1950;
6      
7        // Mark birth and death years using difference array technique
8        for (int[] personLog : logs) {
9            int birthYearIndex = personLog[0] - YEAR_OFFSET;
10            int deathYearIndex = personLog[1] - YEAR_OFFSET;
11          
12            // Increment population at birth year
13            populationChanges[birthYearIndex]++;
14            // Decrement population at death year (person is not alive in death year)
15            populationChanges[deathYearIndex]--;
16        }
17      
18        // Find the year with maximum population using prefix sum
19        int currentPopulation = 0;
20        int maxPopulation = 0;
21        int maxPopulationYearIndex = 0;
22      
23        for (int yearIndex = 0; yearIndex < populationChanges.length; yearIndex++) {
24            // Update current population by adding the change for this year
25            currentPopulation += populationChanges[yearIndex];
26          
27            // Update maximum population and corresponding year if current is greater
28            if (currentPopulation > maxPopulation) {
29                maxPopulation = currentPopulation;
30                maxPopulationYearIndex = yearIndex;
31            }
32        }
33      
34        // Convert index back to actual year and return
35        return maxPopulationYearIndex + YEAR_OFFSET;
36    }
37}
38
1class Solution {
2public:
3    int maximumPopulation(vector<vector<int>>& logs) {
4        // Array to track population changes at each year (1950-2050)
5        // Using difference array technique to efficiently track population changes
6        int populationDelta[101]{};
7        const int BASE_YEAR = 1950;
8      
9        // Mark birth and death years using difference array
10        // Increment at birth year, decrement at death year
11        for (auto& log : logs) {
12            int birthYearIndex = log[0] - BASE_YEAR;
13            int deathYearIndex = log[1] - BASE_YEAR;
14            ++populationDelta[birthYearIndex];   // Person becomes alive
15            --populationDelta[deathYearIndex];   // Person is no longer alive
16        }
17      
18        // Find the year with maximum population using prefix sum
19        int currentPopulation = 0;
20        int maxPopulation = 0;
21        int maxPopulationYearIndex = 0;
22      
23        for (int yearIndex = 0; yearIndex < 101; ++yearIndex) {
24            // Calculate current population by accumulating changes
25            currentPopulation += populationDelta[yearIndex];
26          
27            // Update maximum population and corresponding year if needed
28            // Using < ensures we get the earliest year in case of ties
29            if (maxPopulation < currentPopulation) {
30                maxPopulation = currentPopulation;
31                maxPopulationYearIndex = yearIndex;
32            }
33        }
34      
35        // Convert index back to actual year
36        return maxPopulationYearIndex + BASE_YEAR;
37    }
38};
39
1/**
2 * Finds the year with the maximum population based on birth and death logs
3 * @param logs - Array of [birthYear, deathYear] pairs where a person is alive from birthYear to deathYear-1
4 * @returns The earliest year with the maximum population
5 */
6function maximumPopulation(logs: number[][]): number {
7    // Create a difference array to track population changes for years 1950-2050
8    // Index 0 represents year 1950, index 100 represents year 2050
9    const populationChanges: number[] = new Array(101).fill(0);
10    const yearOffset: number = 1950;
11  
12    // Mark population changes: increment at birth year, decrement at death year
13    for (const [birthYear, deathYear] of logs) {
14        populationChanges[birthYear - yearOffset]++;
15        populationChanges[deathYear - yearOffset]--;
16    }
17  
18    // Find the year with maximum population using prefix sum
19    let maxPopulationYearIndex: number = 0;
20    let currentPopulation: number = 0;
21    let maxPopulation: number = 0;
22  
23    for (let yearIndex = 0; yearIndex < populationChanges.length; yearIndex++) {
24        // Update current population by adding the change for this year
25        currentPopulation += populationChanges[yearIndex];
26      
27        // Update maximum population and corresponding year if current is greater
28        if (maxPopulation < currentPopulation) {
29            maxPopulation = currentPopulation;
30            maxPopulationYearIndex = yearIndex;
31        }
32    }
33  
34    // Convert index back to actual year by adding the offset
35    return maxPopulationYearIndex + yearOffset;
36}
37

Time and Space Complexity

The time complexity is O(n), where n is the length of the array logs. The algorithm iterates through the logs array once to populate the difference array d with O(n) operations, then iterates through the fixed-size array d of length 101 with O(101) = O(1) operations. Since O(n) + O(1) = O(n), the overall time complexity is O(n).

The space complexity is O(C), where C is the range size of the years, calculated as 2050 - 1950 + 1 = 101. The algorithm creates a fixed-size array d of length 101 to track population changes across all possible years in the given range. Additional variables (s, mx, j, offset) use O(1) space. Therefore, the total space complexity is O(C) = O(101) = O(1) in terms of absolute complexity, but more precisely expressed as O(C) to reflect the dependency on the year range.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Death Year Handling

The most common mistake is misunderstanding when a person is considered alive. Many developers incorrectly count a person as alive during their death year.

Wrong approach:

# INCORRECT: Person counted as alive in death year
population_changes[birth_index] += 1
population_changes[death_index + 1] -= 1  # Wrong! Goes beyond array bounds

Correct approach:

# Person is NOT alive in their death year
population_changes[birth_index] += 1
population_changes[death_index] -= 1  # Correct: person dies at start of death year

2. Array Index Out of Bounds

When dealing with edge cases like death year 2050, attempting to mark death_index + 1 would cause an index error since our array only goes up to index 100.

Solution: The problem constraints ensure death years are within [1950, 2050], and since people aren't alive in their death year, we mark the death year itself (not death_year + 1), avoiding boundary issues.

3. Tie-Breaking Logic Error

When multiple years have the same maximum population, using <= instead of < in the comparison would return the latest year instead of the earliest.

Wrong approach:

if max_population <= current_population:  # Returns latest year
    max_population = current_population
    year_with_max_population = year_index

Correct approach:

if max_population < current_population:  # Returns earliest year
    max_population = current_population
    year_with_max_population = year_index

4. Forgetting the Base Year Offset

A subtle bug occurs when forgetting to add back the base year (1950) when returning the result, leading to returning an index instead of the actual year.

Wrong: return year_with_max_population
Correct: return year_with_max_population + base_year

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More