青训营刷题-28

167 阅读3分钟

28 打点计数器

小明想发明一台打点计数器,这个计数器具有以下功能:

  • 它可以接收一个递增的数据范围(形如 [3, 9]),其中第一个数字代表起始,第二个数字代表结束
  • 这个数据范围中包含的数字数量,计数器就会打出相应数量的点。
  • 在传入的多组数据范围中,如果出现了范围的重叠,机器则不会重复打点。

你需要帮助小明计算,在不同的情况下,计数器会打出多少个点。

输入描述

输入为一个二维数组,每个子数组包含两个整数,表示一个数据范围 [start, end]

限制条件

  • 数字范围 [-10^9, 10^9]
  • 数组长度 < 2^16

输出描述

输出一个整数,表示在给定的输入数组情况下,计数器打出的点数。

示例

示例 1

输入

[1,4],
[7, 10],
[3, 5]

输出

7

说明

  • 第一个范围 [1,4] 包含数字 1, 2, 3
  • 第二个范围 [7,10] 包含数字 7, 8, 9
  • 第三个范围 [3,5] 包含数字 3, 4

合并重叠范围后,得到两个不重叠的范围:

  • [1,5] 包含 1, 2, 3, 4,长度为 5 - 1 = 4
  • [7,10] 包含 7, 8, 9,长度为 10 - 7 = 3

总计打点数为 4 + 3 = 7

示例 2

输入

[1,2],
[6, 10],
[11, 15]

输出

9

说明

  • 第一个范围 [1,2] 包含数字 1,长度为 2 - 1 = 1
  • 第二个范围 [6,10] 包含数字 6, 7, 8, 9,长度为 10 - 6 = 4
  • 第三个范围 [11,15] 包含数字 11, 12, 13, 14,长度为 15 - 11 = 4

这些范围之间没有重叠,总计打点数为 1 + 4 + 4 = 9

解题思路

本问题要求计算多个数据范围合并后的总长度,确保重叠部分不被重复计算。这实际上是一个经典的区间合并问题。解决该问题的步骤如下:

1. 排序区间

首先,将所有区间按照起始点进行排序。这样可以方便地按顺序合并重叠的区间。

2. 合并区间

遍历排序后的区间列表,依次合并重叠的区间:

  • 如果当前区间的起始点大于上一个合并区间的结束点,则说明它们不重叠,直接将当前区间加入结果列表。
  • 如果当前区间与上一个合并区间重叠或相连,则更新上一个合并区间的结束点为两者的最大结束点。

3. 计算总长度

在合并所有重叠的区间后,计算每个合并区间的长度(end - start),并将它们累加起来,得到总的打点数。

4. 处理边界情况

  • 空输入:如果输入数组为空,则总打点数为 0
  • 单个区间:如果只有一个区间,直接计算其长度。

5. 时间与空间复杂度

  • 时间复杂度O(n log n),主要来自于对区间的排序,其中 n 是区间的数量。
  • 空间复杂度O(n),用于存储排序后的区间和合并后的结果。

算法实现

import java.util.*;

public class Main {
    public static int solution(int[][] inputArray) {
        // Please write your code here
        if(inputArray == null || inputArray.length == 0){ return 0; }
        
        // 按起始点排序
        Arrays.sort(inputArray, (x,y) -> Integer.compare(x[0], y[0]));
        
        // 初始化第一个区间
        int start = inputArray[0][0];
        int rightBound = inputArray[0][1];
        int res = 0;
        
        for(int i=1;i<inputArray.length;i++){
            if(inputArray[i][0] > rightBound){
                // 不重叠,累加当前区间长度
                res += rightBound - start;
                start = inputArray[i][0];
                rightBound = inputArray[i][1];
            }else{
                // 重叠,更新
                rightBound = Math.max(rightBound, inputArray[i][1]);
            }
        }
        res += rightBound - start;
        // System.out.println(res);
        return res;
    }

    public static void main(String[] args) {
        //  You can add more test cases here
        int[][] testArray1 = {{1, 4}, {7, 10}, {3, 5}};
        int[][] testArray2 = {{1, 2}, {6, 10}, {11, 15}};

        System.out.println(solution(testArray1) == 7);
        System.out.println(solution(testArray2) == 9);
    }
}