每个程序员都应该掌握的算法-差分

340 阅读1分钟

前言

为什么会想到来讲解一下差分,主要还是最近的一场华为周赛最后一题考的就是差分,然而解出来的并不多,所以这篇文章可以来聊一聊差分是什么?

差分解决什么问题

举个例子吧,比如我们有十个区间[l, r],现在我们需要对这十个区间内的数都加一,按照我们最简单粗暴的方法就是遍历十个区间,依次对每个数进行加一操作,在区间数特别少且区间范围不大的情况下这当然可以,但是如果区间数量特别大,如有100000个区间,1 <= l <= r <= 100000,如果在这种情况下,如果我们还是用最暴力的方法来统计,那么复杂度是100000 * 100000,这复杂度已经是非常高了,那么一维差分是如何解决这个问题?

差分算法

给区间[l, r]的数都加一,我们可以简单的表示为 count[l]++, count[r+1]--,我们对所有的区间都执行这种记录,然后遍历count数组,执行count[i] += count[i-1]; 这种策略其实有点类似打表的思维,首先对区间进行打表,然后遍历统计数组count,计算结果。

有一维差分就必然有二维差分

举个简单的例子,我们有很多个矩形,现在我们要统计任意坐标被矩形覆盖的次数,也就是把上边讲的一维变成二维了,解决办法如法炮制,如矩形坐标[x1, y1, x2, y2],表示左上角与右小角,count[x1][y1]++,count[x2+1][y2+1]++,count[x1][y2+1]--,count[x2+1, y1]--; 可能只写出这个公式难以让人理解,可以把count[x][y],理解成以x,y为左上的顶点,它可以覆盖[x-无穷][y-无穷]的区间,那么我们统计矩形[x1, y1, x2, y2],就可以表示为 count[x1][y1] + count[x2+1][y2+1] - count[x1][y2+1] - count[x2+1][y1],自己画一下就能理解了

题目

简单表示就是给定50000个区间[l, r],1 <= l <= r <= 10^9,查询区间中任意数被覆盖次数,查询次数最多为50000次。

解法

有人看到区间范围是10 ^ 9顿时就怕了,觉得无法统计,那么你就错了,我们注意总共才50000个区间,最多查询50000次,那么最多出现150000个不同的数,我们把这些数离散化一下就好了

代码展示

/**
 * 查询任意数被区间覆盖次数
 * @param flowers 区间
 * @param persons 查询具体的数
 * @return
 */
public int[] fullBloomFlowers(int[][] flowers, int[] persons) {
    int[] a = new int[2 * flowers.length + persons.length];
    int cnt = 0;
    for (int[] flower : flowers) {
        a[cnt++] = flower[0];
        a[cnt++] = flower[1];
    }
    for (int person : persons) {
        a[cnt++] = person;
    }
    Arrays.sort(a);
    Map<Integer, Integer> posMap = new HashMap<>();
    for (int i = 0; i < a.length; i++) {
        posMap.put(a[i], i + 1);
    }
    int[] count = new int[a.length + 10];
    for (int[] flower : flowers) {
        count[posMap.get(flower[0])]++;
        count[posMap.get(flower[1]) + 1]--;
    }
    for (int i = 1; i < a.length + 10; i++) {
        count[i] += count[i-1];
    }
    int[] ans = new int[persons.length];
    for (int i = 0; i < persons.length; i++) {
        ans[i] = count[posMap.get(persons[i])];
    }
    return ans;
}