zoukankan      html  css  js  c++  java
  • 树状数组求区间最大值

    这个算法只支持单点修改和区间查询最值。每一次维护和查询的时间复杂度都是O((logn)^2),但这是满打满算的时间复杂度。

    假设是要维护和查询区间的最大值(最小值将max改成min 就好了)

    这个算法和树状数组维护和查询区间和的方法很相似:

    一、数组的含义

    1、在维护和查询区间和的算法中,h[x]中储存的是[x,x-lowbit(x)+1]中每个数的和,

    2、在求区间最值的算法中,h[x]储存的是[x,x-lowbit(x)+1]中每个数的最大值。

    求区间最值的算法中还有一个a[i]数组,表示第i个数是多少。

    二、单点修改后的更新

    1、在维护区间和的算法中,是这样维护单点修改的

    void updata(int i, int val)
    {
    	while (i <= n)
    	{
    		h[i] += val;
    		i += lowbit(i);
    	}
    }
    

    2、在来看维护区间最大值的算法,我们先看一整段区间[1,n]都需要初始化的情况。(即 h[] 数组都为0,现在需要用 a[] 数组更新 h[] 数组)

    void updata(int i, int val)
    {
    	while (i <= n)
    	{
    		h[i] = max(h[i], val);
    		i += lowbit(i);
    	}
    }
    

    这样是可行,于是我们就有了一个O(n*logn)的维护方法,即当要更新一个数的时候,把 h[] 数组清零, 然后用数组 a[] 去更新 h[] 数组。

    但这个复杂度显然太高了。

    可以发现:对于x,可以转移到x的只有,x-2^0,x-2^1,x-2^2,.......,x-2^k (k满足2^k < lowbit(x)且2^(k+1)>=lowbit(x))

    举例:

    若 x = 1010000

    = 1001000 + lowbit(1001000) = 1001000 + 1000 = 1001000 + 2^3

    = 1001100 + lowbit(1001100) = 1001100 + 100 = 1001100 + 2^2

    = 1001110 + lowbit(1001110) = 1001110 + 10 = 1001110 + 2^1

    = 1001111 + lowbit(1001111) = 1001111 + 1 = 1001111 + 2^0

    所以对于每一个h[i],在保证h[1...i-1]都正确的前提下,要重新计算h[i]值的时间复杂度是O(logn),具体方法如下:

    	h[x] = a[x];
    	lx = lowbit(x);
    	for (i=1; i<lx; i<<=1)
    	h[x] = max(h[x], h[x-i]);
    	x += lowbit(x);
    

    这样,我们就可以得到一个和树状数组维护区间合算法很像的算法

    void updata(int x)
    {
    	int lx, i;
    	while (x <= n)
    	{
    		h[x] = a[x];
    		lx = lowbit(x);
    		for (i=1; i<lx; i<<=1)
    			h[x] = max(h[x], h[x-i]);
    		x += lowbit(x);
    	}		
    }
    

     

    这个算法的时间复杂度是O((logn)^2),所以现在就可以在O((logn)^2)的时间内完成最值的区间维护了。

    三、区间查询
    1、树状数组求区间合的算法是这样子的:

    int query(int i)
    {
    	int ans = 0;
    	while (i > 0)
    	{
    		ans += h[i];
    		i -= lowbit(i);
    	}
    	return ans;
    }
    

    2、树状数组求区间最大值:

    直接照搬求区间合的方法显然是不行的。

    因为区间合中,要查询[x,y]的区间合,是求出[1,x-1]的合与[1,y]的合,然后相减就得出了[x,y]区间的合。

    而区间最值是没有这个性质的,所以只能够换一个思路。

    设query(x,y),表示[x,y]区间的最大值

    因为h[y]表示的是[y,y-lowbit(y)+1]的最大值。

    所以,可以这样求解:

    若y-lowbit(y) > x ,则query(x,y) = max( h[y] , query(x, y-lowbit(y)) );

    若y-lowbit(y) <=x,则query(x,y) = max( a[y] , query(x, y-1);

    这个递归求解是可以求出解的,且可以证明这样求解的时间复杂度是O((logn)^2)

    具体代码:

    int query(int x, int y)
    {
    	int ans = 0;
    	while (y >= x)
    	{
    		ans = max(a[y], ans);
    		y --;
    		for (; y-lowbit(y) >= x; y -= lowbit(y))
    			ans = max(h[y], ans);
    	}
    	return ans;
    }
    

      

    时间复杂度的证明:(换成二进制来看)

    因为y经过Logn次变换以后,其与x不同的最高位至少下降了1位,所以最多进行(logn)^2次变换

    举例:

    y = 1010000

    x = 1000001

    1010000

    => 1001111 => 1001110 =>1001100 =>1001000

    =>1000111 => 1000110 => 1000100

    => 1000011 = > 1000010

    =>1000001

    =>1000000 < 1000001

  • 相关阅读:
    redis单机安装以及简单redis集群搭建
    Linux中JDK安装教程
    微信公众号开发(一)
    easyui多图片上传+预览切换+支持IE8
    mybatis动态sql之foreach标签
    java List递归排序,传统方式和java8 Stream优化递归,无序的列表按照父级关系进行排序(两种排序类型)
    java钉钉通讯录同步
    java使用poi生成导出Excel(新)
    java 图片转base64字符串、base64字符串转图片
    Spring事务mysql不回滚:mysql引擎修改
  • 原文地址:https://www.cnblogs.com/bytebull/p/8469495.html
Copyright © 2011-2022 走看看