Index Tree概念
Index Tree 是一个满二叉树。修改与查询复杂度为O(logN)
大数据量级别的数据,重复被计算使用时,优先考虑index Tree 的数据结构。
Index Tree 的特点
- 有且只有一个根节点
- 每个父节点都有一个左子节点和右子节点
- Index Tree每一层节点个数满足2^n
- 左子节点满足偶数特性,右边满足奇数特性
- 父节点=子节点/2
- 节点总数为 2n-1 ,n为最底层叶子节点的数量
Index Tree 的构造
Index Tree可使用一维数组进行存储,通过确定最后一层节点个数来确定树的大小。
题目一般给定N的个数: int [] Tree = new int Tree[2*cnt]
最后一层节点的个数同时也是,最后一层第一个节点 Tree[] 数组下标。
int cnt = 1;while(cnt < N){cnt<<=1; //通过N 来确定最底层叶子节点的个数}Tree = new int[2*cnt]; // 2*cont 开辟满树的大小数组for(int i=0;i<2*cnt;i++){Tree[i]=0; //Tree 数组的初始化}
Index Tree解题思路
- 第一步 定义tree的存储类型,数组或list
- 第二步 读入数据 计算出节点数量
- 第三步 通过节点数量计算出 满二叉树的最底层节点的个数 n
- 第四步 通过满二叉树的最底层节点数量 创建满二叉树的一维数组 2n-1 的大小,同时也确定了最底层节点第一个节点的一维数组的下标也为n
- 第五步 排序
- 第六步 实现算法(查找树,更新树)query 方法查询 记录数据, upde 方法更新树的结构 一遍下次使用
- 输出
练习题目:
描述:
草原上有一群牛,每个牛都有自己的吃草区域[S,E],
假如两头牛的区域分别为[Si,Ei],[Sj,Ej]; 如果Si<=Sj,并且Ej <= Ei, 那么i牛就比j牛厉害
现在题目中给出N个牛,请计算比自己厉害的牛的个数
输入
N 牛的个数 (1<=N<=100000)
下面N行给出区域
N (1 <= N <= 105),
E(0 <= S < E <= 105), S 和 E(0 <= S < E <= 100000)
输入结束行是 0.
输出
输入比自己厉害的个数
输入
4
1 2
0 3
3 4
2 5
0
答案:
1 0 1 0分析
1.排序:
如果S相等,E按降序排列
如果S不相等,按照S升序排列
——>因为已按开始点进行排序,早出现区域的开始点肯定是小于等于当前区域的开始点,因此只需要判断结束点就ok了。
此题目需要注意,存在相同的区域,相同区域的两个牛之间不进行比较
代码
package indexTree;import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Arrays;import java.util.Comparator;import java.util.StringTokenizer;/**** @author XA-GDD*/public class cow {static int N;static int sumtree[] = new int[100001*4];static class info{int idx, s, e;info(int idx, int s, int e){this.idx = idx; //数据原始的 id 坐标this.s = s; //开始时间this.e = e; //结束时间}}static info D[] = new info[100001]; //对象数组static int ans[] = new int[100001]; //结果数组static class comp implements Comparator<info>{@Overridepublic int compare(info a, info b) {if(a.s != b.s) return a.s-b.s;else return b.e-a.e;}}//更新树的节点static void update(int node){int cur=node;while(cur>0){sumtree[cur]++;cur>>=1;}}// 11-10 15//树的查询static int query(int start, int end){int ret=0;int s=start;int e=end;while(s<=e){if(s%2==1) ret += sumtree[s];if(e%2==0) ret += sumtree[e];s = (s+1)>>1;e = (e-1)>>1;}return ret;}public static void main(String[] args) throws Exception{BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StringTokenizer st;while(true){N = Integer.parseInt(br.readLine());if(N==0) break;int Nidx = 1;//100000 代表的是 S,E的最大值while(Nidx<100000) {Nidx<<=1; //计算出最下层节点需要的叶子节点的个数//同时也是最下一层叶子节点的第一个Tree 的下标} // 2*n 代表的是 tree 的最打节点个数 2*n-1// Arrays.fill(sumtree, 0); //fill 的实际处理也是通过for 循环填充的了整个数组的值, 每次的循环次数为 100001*4 效率相对低for(int i=0;i<Nidx*2;i++) {sumtree[i]=0; // 初始化数组的值 将数组的值清空}// Arrays.fill(ans, 0); //for 循环填充的了整个数组的值 每次的循环次数为 100001for(int i=0,s,e;i<N;i++){st = new StringTokenizer(br.readLine());s = Integer.parseInt(st.nextToken());e = Integer.parseInt(st.nextToken());D[i] = new info(i,s,e);ans[i]=0; //结果的数组 初始化}Arrays.sort(D,0,N,new comp());for(int i=0,scnt=0;i<N;i++){if(i>0 && D[i-1].s == D[i].s && D[i-1].e == D[i].e) {scnt++;}else {scnt = 0;}// 11 - 10 15ans[D[i].idx] = query(Nidx+D[i].e,Nidx+100000)-scnt;update(Nidx+D[i].e);}for(int i=0;i<N;i++) {System.out.print(ans[i]+" ");}System.out.println();}}}
