当前位置: 首页 > article >正文

差分数组-实现区间强度算法

package org.example;

import java.util.Map;
import java.util.TreeMap;

public class IntensitySegments {
    // 使用 TreeMap 来存储每个区间的起始位置和对应的强度
    private TreeMap<Integer, Integer> segments;

    public IntensitySegments() {
        segments = new TreeMap<>();
    }

    /**
     * 将指定区间 [from, to] 的强度增加 amount。
     *
     * @param from 起始点
     * @param to 结束点
     * @param amount 要增加的强度
     */
    public void add(int from, int to, int amount) {
        // 增加区间起始位置的强度
        segments.put(from, segments.getOrDefault(from, 0) + amount);
        // 减少区间结束位置的强度
        segments.put(to, segments.getOrDefault(to, 0) - amount);
    }

    /**
     * 将指定区间 [from, to] 的强度设置为 amount。
     *
     * @param from 起始点
     * @param to 结束点
     * @param amount 要设置的强度
     */
    public void set(int from, int to, int amount) {
        // 删除该范围内的旧的强度
        segments.entrySet().removeIf(entry -> entry.getKey() >= from && entry.getKey() <= to);

        // 将区间设置为新的强度
        segments.put(from, amount);
        segments.put(to, 0);  // 设置结束点为 0,确保在该点后不再有强度
    }

    /**
     * 将区间的强度信息转换为字符串表示。
     *
     * @return 字符串表示
     */
    @Override
    public String toString() {
        StringBuilder result = new StringBuilder("[");
        int currentIntensity = 0;

        // 遍历 TreeMap,计算每个区间的强度
        for (Map.Entry<Integer, Integer> entry : segments.entrySet()) {
            currentIntensity += entry.getValue();
            result.append("[").append(entry.getKey()).append(",").append(currentIntensity).append("],");
        }

        // 去掉最后一个逗号,并加上 "]"
        if (result.length() > 1) {
            result.setLength(result.length() - 1);
        }
        result.append("]");

        return result.toString();
    }

    // 测试用例
    public static void main(String[] args) {
        IntensitySegments segments = new IntensitySegments();

        System.out.println(segments.toString()); // 输出: []
        segments.add(10, 30, 1);
        System.out.println(segments.toString()); // 输出: [[10,1],[30,0]]
        segments.add(20, 40, 1);
        System.out.println(segments.toString()); // 输出: [[10,1],[20,2],[30,1],[40,0]]
        segments.add(10, 40, -2);
        System.out.println(segments.toString()); // 输出: [[10,-1],[20,0],[30,-1],[40,0]]


        IntensitySegments segments2 = new IntensitySegments();
        System.out.println(segments2.toString()); // 输出: []
        segments2.add(10, 30, 1);
        System.out.println(segments2.toString()); // 输出: [[10,1],[30,0]]
        segments2.add(20, 40, 1);
        System.out.println(segments2.toString()); // 输出: [[10,1],[20,2],[30,1],[40,0]]
        segments2.add(10, 40, -1);
        System.out.println(segments2.toString()); // 输出: [[10,0],[20,1],[30,0],[40,0]]
        segments2.add(10, 40, -1);
        System.out.println(segments2.toString()); // 输出: [[10,-1],[20,0],[30,-1],[40,0]]
    }
}

### 差分数组(Difference Array)概念

差分数组是一种常用于高效处理区间更新和查询的技巧。它通过记录**区间增量**而不是直接存储区间内的值,来优化更新操作。差分数组的核心思想是:对于一个区间 `[L, R]`,我们只记录区间的**开始增量**和**结束增量**,而不直接对区间内每个元素进行逐一更新。这使得区间更新变得非常高效。

### 差分数组的工作原理:

假设我们有一个长度为 `n` 的数组 `arr[]`,我们要对区间 `[L, R]` 中的所有元素加上一个值 `x`。如果直接修改区间内的每个元素,会有较高的时间复杂度(`O(R-L+1)`)。但是,如果使用差分数组,我们可以通过**记录增量**来高效处理这个问题,具体步骤如下:

1. **创建一个差分数组 `diff[]`**,其大小与原数组相同(或比原数组多 1)。
   
2. 对于区间 `[L, R]`:
   - 在 `diff[L]` 位置增加 `x`,表示从 `L` 位置开始,数组值增加 `x`。
   - 在 `diff[R + 1]` 位置减少 `x`,表示从 `R + 1` 位置开始,数组值停止增加 `x`。

3. **构造原数组**:通过差分数组 `diff[]`,我们可以通过累加得到原数组的最终值。

### 举例说明:

假设我们有一个数组 `arr[]`,并且我们要对区间 `[L, R]` 的每个元素加 `x`。

#### 初始情况:

```
arr[] = [0, 0, 0, 0, 0]   (长度为5的数组,初始值为0)
```

#### 假设要对区间 `[1, 3]` 进行加 5 操作:

1. 创建差分数组 `diff[]`(大小为 6,原数组大小 + 1):

```
diff[] = [0, 0, 0, 0, 0, 0]  (初始化为0)
```

2. 对区间 `[1, 3]` 执行加 5 操作:

- 在 `diff[1]` 增加 5,表示从位置 1 开始,增加 5。
- 在 `diff[4]` 减少 5,表示从位置 4 开始,停止增加 5。

更新后的差分数组:

```
diff[] = [0, 5, 0, 0, -5, 0]
```

3. 最后,我们通过差分数组恢复原数组。我们需要从 `diff[]` 数组中累加得到原数组 `arr[]`:

- `arr[0] = diff[0] = 0`
- `arr[1] = arr[0] + diff[1] = 0 + 5 = 5`
- `arr[2] = arr[1] + diff[2] = 5 + 0 = 5`
- `arr[3] = arr[2] + diff[3] = 5 + 0 = 5`
- `arr[4] = arr[3] + diff[4] = 5 + (-5) = 0`

最终的原数组是:

```
arr[] = [0, 5, 5, 5, 0]
```

### 为什么差分数组可以高效处理区间更新?

- **空间复杂度**:差分数组只需要一个额外的数组,它的空间复杂度是 `O(n)`,与原数组大小相同。
- **时间复杂度**:每次对一个区间 `[L, R]` 的操作,只需要 `O(1)` 的时间。更新操作是常数时间复杂度,因此可以高效处理多个区间更新。
  
  - 在原数组上进行更新时,我们需要遍历整个区间,这样的操作是 `O(R - L + 1)`。
  - 使用差分数组时,每次只需要两次操作:`diff[L] += x` 和 `diff[R + 1] -= x`,这两步操作的时间复杂度是 `O(1)`。

### 扩展:多个区间的处理

当你需要执行多个区间的操作时,差分数组的优势更加明显。你可以为每个区间执行差分操作,而不用担心每次操作都要遍历区间内的每个元素。

### 差分数组的逆操作:构建原数组

通过差分数组得到原数组的过程叫做**前缀和**(prefix sum):

```python
def build_array_from_diff(diff):
    arr = [0] * len(diff)
    arr[0] = diff[0]
    for i in range(1, len(diff)):
        arr[i] = arr[i-1] + diff[i]
    return arr
```

### 在 `add(from, to, amount)` 方法中的应用

在 `add(from, to, amount)` 操作中,我们实际上是在使用类似差分数组的思想:

- **`segments.put(from, segments.getOrDefault(from, 0) + amount)`**:在 `from` 点加上 `amount`,表示从 `from` 开始,强度增加 `amount`。
- **`segments.put(to, segments.getOrDefault(to, 0) - amount)`**:在 `to` 点减去 `amount`,表示从 `to` 开始,强度停止增加 `amount`。

这与差分数组的思想类似:我们没有直接去更新区间 `[from, to]` 内的每个位置,而是通过在 `from` 位置加上增量,在 `to` 位置减去增量来间接实现对区间的更新。

### 总结

差分数组是一种高效的区间更新技巧,通过记录区间的增量,避免了逐个元素的操作,极大提高了区间更新的效率。在 `add(from, to, amount)` 的实现中,我们使用了类似差分数组的技巧:在区间起始点加上增量,在结束点的下一个位置减去增量,从而控制增量的作用范围。


http://www.kler.cn/a/395955.html

相关文章:

  • Keil基于ARM Compiler 5的工程迁移为ARM Compiler 6的工程
  • 24.11.15 Vue3
  • Python进程间通讯大揭秘:原理深度剖析与实战案例分享
  • 数据网格能替代数据仓库吗?
  • 差分数组解析
  • golang中rpc
  • jmeter常用配置元件介绍总结之断言
  • 无人机图传系统介绍——CKESC电调小课堂11.0
  • 全面评估ASPICE标准对汽车软件开发的影响与效果
  • Android Studio | 修改镜像地址为阿里云镜像地址,启动App
  • 【云原生系列--Longhorn的部署】
  • 11.11 机器学习-数据集的获取和划分
  • 深度学习--卷积神经网络
  • 2024年11月系统架构设计师考试真题回顾
  • 性能测试|JMeter接口与性能测试项目
  • PCL 点云拟合 基于夹角约束的Ransac拟合平面
  • 十一、拦截器
  • 【Linux网络编程】简单的UDP网络程序
  • Appium配置2024.11.12
  • 高级前端开发工程师--掌握的技术