当前位置: 首页> 汽车> 报价 > 武汉城乡建设网_烟台网络推广公司_广告关键词有哪些类型_链网

武汉城乡建设网_烟台网络推广公司_广告关键词有哪些类型_链网

时间:2025/8/8 10:27:00来源:https://blog.csdn.net/2301_79108772/article/details/143586015 浏览次数: 0次
武汉城乡建设网_烟台网络推广公司_广告关键词有哪些类型_链网

1.IndexTree

        IndexTree解决的问题是什么呢?可以从求前缀和入手这个问题。

1.1前缀和数组

        简单封装一个前缀和数组:

package com.xinghai.arr;import java.util.Arrays;/*** 前缀和数组*/
public class PrefixSumArr {// 存储前缀和数据private int[] prefixSumArr;// 存储前缀和数组的长度private final int N;// 构造前缀和public PrefixSumArr(int[] origin) {N = origin.length;prefixSumArr = new int[N];for (int index = 0; index < origin.length; index++) {for (int idx = 0; idx <= index; idx++) {prefixSumArr[index] += origin[idx];}}}// 获取范围求和数据public int getSum(int left, int right) {if (left < 0 || right >= N) {throw new ArrayIndexOutOfBoundsException("索引越界!!!");}if (left > right) {throw new RuntimeException("无效索引");}return prefixSumArr[right] - (left == 0 ? 0 : prefixSumArr[left - 1]);}public int getSum(int right) {return getSum(0, right);}// 操作前缀和数据// 获取前缀和数组中index位置的数据public int get(int index) {return prefixSumArr[index];}// 为前缀和数组中index位置设置对应数据public void set(int index, int value) {if (index >= N || index < 0) {throw new ArrayIndexOutOfBoundsException("索引越界!!!");}// 获取原来索引对应的数据int rawData = prefixSumArr[index] - ((index > 0) ? prefixSumArr[index - 1] : 0);for (int idx = index; idx < N; idx++) {prefixSumArr[idx] += (value - rawData);}}// 为前缀和数组中index位置的数据增加一定值public void add(int index, int value) {if (index >= N || index < 0) {throw new ArrayIndexOutOfBoundsException("索引越界!!!");}for (int idx = index; idx < N; idx++) {prefixSumArr[idx] += value;}}// 打印前缀和数public void printPrefixSumArr() {System.out.println(Arrays.toString(prefixSumArr));}}

        分析前缀和数组的时间复杂度。

        前缀和数组操作的复杂度主要是分析前缀和数组中的set和add操作。

        假设前缀和数组需要更新index位置的数据,那么需要变动的数据量是(N - index)个,即时间复杂度为O(N - index) => 即为O(N),这个时间复杂度还是比较高的,那么有没有一种方式可以降低前缀和数组的时间复杂度,还能达到前缀和数组的效果吗?

        Index树就达成这种效果的。

        即Index树是为了解决范围求和O(1)的同时,更新数据也能做到O(Log N)的数据结构。

1.2IndexTree是什么?

        IndexTree也是使用数组模拟出的树结构(类似于堆结构,线段树)。

        这种结构主要的特性是可以完成和前缀和数组一样的效果,并且单点数据更新的时间复杂度为O(log N)

2.解析IndexTree数据结构

        掌握IndexTree一定不要尝试去理解发明这个数据结构的人心理路程是怎么样的,因为IndexTree整体设计的比较巧妙,里面的设计很难理解是因为为了发明IndexTree所以诞生这种处理机制,还是因为想到了这种处理机制从而诞生了IndexTree,掌握具体实现有时候比掌握原理其实都重要。

2.1IndexTree的构成

        IndexTree构成用语言描述非常复杂,使用手写画图举例的方式可以研究明白IndexTree的构成:

2.2解析单点更新和前缀求和

        IndexTree的单点更新和前缀求和会让你感觉十分巧妙,但是不会感觉十分难以理解。

        这里我也使用手写的方式呈现出IndexTree是如何构建以及单点更新,范围求和是如何实现的:

        其实看完手写笔记,用一句话总结IndexTree就是:IndexTree每个位置的数据都是从自己当前位置index开始向前累加到(index - index最右边的1 + 1)位置。

        单点更新也是依赖此实现的,将依赖自己的数据都变量(自己加自己右侧的1,一直加到越界为止)

        前缀求和也是依赖IndexTree的特殊结构,先把自己的管的区域(index - index最右侧的1 + 1)- index位置的数据相加,再从index - index最右侧的1开始递归操作,直至越界。

3.详解代码实现

        从代码的角度再去理解IndexTree结构,单点更新,范围求和。

3.1构建IndexTree

        想要更好的构建IndexTree,必须通透了解IndexTree中每个位置如何借助原数组快速构建IndexTree的。

        从手写图中可以总结出:IndexTree中每个位置的累加数据为(index - (index最右侧的1) + 1)- (index)

        记住这个公式,使用代码表示出来就是,从当前位置出发向前累加到index - index最右侧的1 + 1的位置。

        代码实现:

// 索引树 -> 数组封装
private int[] indexTreeArr;
// IndexTree的长度
private int N;// 初始化Index树
public IndexTree(int[] origin) {N = origin.length + 1;indexTreeArr = new int[N];for (int index = 1; index < N; index++) {int idx = index;idx -= ((idx & -idx) - 1);while (idx <= index) {indexTreeArr[index] += origin[idx++ - 1];}}
}

3.2单点更新

        单点更新的核心操作就是从自己开始,一直相加自己最右侧的1,变量位置,更新数据。

// 单点更新
public void add(int index, int value) {if (index < 1 && index >= N) {throw new ArrayIndexOutOfBoundsException("索引越界");}while (index < N) {indexTreeArr[index] += value;index += index & -index;}
}
public void set(int index, int value) {if (index < 1 && index >= N) {throw new ArrayIndexOutOfBoundsException("索引越界");}int oldVal = indexTreeArr[index];while (index < N) {indexTreeArr[index] += (value - oldVal);index += index & -index;}
}

3.3范围求和

        范围求和就是从right出发,将当前IndexTree对应的right的数据(即原始数组中right到right - right最右侧的1 + 1)累加后,再将right设定为right - right最右侧的1,继续反复操作,直到越过left为止。

// 分段求和
public int sum(int left, int right) {if (left < 1 || right < 1 || left >= N || right >= N) {throw new ArrayIndexOutOfBoundsException("索引越界!!!");}if (left > right) {throw new RuntimeException("索引错误!!!");}int sum = 0;while (right >= left) {sum += indexTreeArr[right];right -= right & -right;}left -= 1;while (left > 0) {sum -= indexTreeArr[left];left -= left & -left;}return sum;
}public int sum(int right) {return sum(1, right);
}

4.复杂度分析

        复杂度分析主要分析单点更新和范围求和即可。

        单点更新是O(LogN)的,因为从当前位置出发,只需要改动加上自己最右侧1的数据,相比于前缀和数组提升了一个量级。

        范围求和也是O(LogN)的,因为从当前位置出发,每次位置变动都是将自己最右侧的1抹去,相比于前缀和数组提升了一个量级。

5.结语

        下一步更新IndexTree在二维数组中的妙用,你的点赞就是我更新的动力!!!

关键字:武汉城乡建设网_烟台网络推广公司_广告关键词有哪些类型_链网

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: