算法Tips-2 差分数组

差分数组技巧

简单回顾Tips 1

提示:先回顾一下Tips1的前缀和
前缀和解决的问题是原始数组不会被修改的情况下,频繁的查询某个区间的累加和

核心代码如下:

class PreSum {
	private int [] preSum;
	//初始化数组
	public PreSum(int[] nums){
	preSum = new int [nums.length+1];
	//构造查询数组
	for(int i=1 ; i<nums.lengths ;i++){
		preSum[i] = preSum[i-1]+nums[i-1];
	}
	//返回结果
	public int Search(int start ,int end){
		return preSum[end+1] - preSum[start];
	}
}

而接下来介绍的差分数组是解决频繁对原始数组进行的某个区间内的元素进行频繁的增减操作


一、什么是差分数组呢?

一个数组nums,现在对nums[i…j]区间内的元素全部+3.再对nums[m…n]的元素-2,不会真的有人写for循环两次吧,不会吧!哈哈哈哈哈,还真有,就是我了,虽然我很笨,但这个代码我还是写的出来的

无脑遍历代码,时间复杂度O(N):

	int[] count(int start,int end,int val,int[]nums){
	
		if(end>nums.length||start<0) return nums;
		
		for(int i=strat ; i<=end ;i++){
			nums[i]+=val;
		}
		return nums;
	}

那现在讲一下用差分数组技巧如何优化:

构造一个diff数组,记录nums[i] 与nums [i-1]的差 ,即diff[i] = nums[i] - nums[i-1]
核心代码:

//构造差分数组:
	int [] diff = new int[nums.length];
	diff[0] = nums[0];
	for(int i=1 ;i<nums.length;i++){
		diff[i] = nums[i]-nums[i-1];
	}
//差分数组反推原数组:
	int[] res = new int [diff.length];
	res[0] = diff[0];
	for(int i=1;i<diff.length;i++){
		res[i] = res[i]+diff[i-1];
	}
  • 所有频繁对数组区间的加减操作,都使用差分数组进行,最后反推回原数组就行

二、差分数组为什么高效?

优点:

对差分数组操作时间复杂度为O(1)

  • 对数组nums[i…j] 全部+3 ,即diff[i]+=3 ; diff [j+1]-=3

diff[i]+=3 意味着从i开始diff数组后面所有的元素全部加了3,即原数组从i-1位置开始全部加了3 ; 我们的目的是 [i,j] 区间 ,所以把 diff[j] 后面的元素再减去3就相当于没操作 ,达到了目的 。

当然倒推回原数组累加的步骤是在最后一次对数组进行操作完之后进行的

也就是说用差分数组diff O(1)的时间复杂度解决了原数组O(N)的时间复杂度!!!


核心代码:

static class Difference{
        private int [] diff;
        //构造差分数组
        public Difference(int[]nums){
            assert nums.length>0; //断言,如果nums.length>0就不会抛出错误

            diff = new int[nums.length];
            diff[0] = nums[0];
            for(int i=1;i<nums.length;i++){
                diff[i] = nums[i] - nums[i-1];
            }
        }
        //对差分数组进行操作
        public void increment(int i,int j ,int[]diff,int val){
            diff[i]+=val ;
            if(j+1<diff.length)     //如果j+1==diff的长度就是对整个数组的操作,不用再减了
                diff[j+1]-=val;
        }

        //返回结果
        public int[] result(int[] diff){
            int[] res = new int[diff.length];
            res[0] = diff[0];
            for(int i=1;i<diff.length;i++)
                res[i] = res[i-1]+diff[i];
            return res;
        }
        
    }

实战案例:区间加法

题目:
在这里插入图片描述

解决这个问题:秒杀~!

static public void ModifyArray(int length , int [][] updates){
        int[] nums = new int[length];
        // 使用刚刚封装好的差分数组类
        Difference df = new Difference(nums);

        for(int[] update :updates){
            int start = update[0];
            int end = update[1];
            int val = update[2];
            df.increment(start, end, val); //对差分数组进行操作
        }
        df.result();	//获得结果
    } 

实战案例二:

在这里插入图片描述

  • trips 中包含的就是对差分数组的操作 , capacity就是数组中所有的元素都要小于它
static boolean  BusPooling(int[][] trips,int capacity){
        //最多1000个车站
        int[] nums = new int[1001];
        
        Difference df = new Difference(nums);
        for(int[] trip:trips){
            int passenager = trip[0];
            int add = trip[1];
            int multiply = trip[2]-1;
            //在 add和multiply区间内车上新加有passenager个乘客
            df.increment(add, multiply, passenager); 
        }
        int [] res = df.result();

        //检查是否超载
        for(int i=0 ; i<res.length;i++){
            if(res[i]>capacity)
                return false;
        }
        return true;
    }

总结

小小的总结一下:

  • 差分数组解决的问题,就是对区间数组进行频繁的加或者减,可以极大的极高算法的效率
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值