数据结构学习——线段树
2015-11-16 20:41
260 查看
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
采用一维数组来存储线段树中的节点元素,所以必须要有一种数学关系将输入的元素(最底层叶子元素)对应到线段树的上层节点。
自底向上建树。程序输入有m个数,这m其实就是线段树的叶子节点元素,但为了便于计算,将其扩大到2的幂,多出来的元素用不会影响程序的值的填充。
在2的基础上自底向上建树,得到的就是一个满二叉树,而根据数学中等比数列和及其通项公式之间的关系:若第k层有n个元素,则前k-1层有n-1个元素。所以,存储整个线段树的数组空间大小应为(2*n-1)。综合2、3,也就是有了常说的“线段树开4倍空间的说法”。
4.最后一层有n个元素,其中前m个是程序输入的数据,最后一层之前的共有n-1个元素,所以若是从0开始编号的话,最后一层自左向右第一个元素的编号为n-1,这也就是在更新函数update中有”k += n-1;”这句话的原因。
5.线段树在进行查询操作时,用query(a,b,0,0,n),注意这里最后一个参数是n而不是m,因为在init操作中最后一层的元素已经由m变成了n个,区间编号0也应该对应[0,n)
线段树,根据节点中维护的数据的不同,线段树可以提供不同的功能。
节点维护区间最小值,用线段树求区间最小值:
note:
1.满二叉树:每一层都是满的(拥有全部元素,对应二叉树的性质——第k层,最多有2^(k-1)个节点)。
2.完全二叉树:树中除了最后一层,其余层都是满的;而且最后一层或者是满的,或者是在右边缺少连续的若干接点,即若对节点编号的话,应与满二叉树相对应。
3.树中父子节点编号的值对应关系(不必刻意去记忆,由于是规律,届时只需要画出二叉树的前两层,即可使用了):
son(n):
当前节点编号为n,若树中节点编号是从0开始的,则其左子树根的节点编号为
2*n+1,右子树根的节点编号为2*n+2 ; 若树中节点编号是从1开始的,若根节点为n,则其左子树根的节点编号为2*n,右子树根的节点编号为2*n+1。
parent(n):
当前节点编号为n,若树中节点编号是从0开始的,则其父节点编号为(n-1)/2;若树中节点编号是从1开始的,则其父节点编号为n/2。
ps:程序中涉及到区间的时候,不论是STL、还是Java中的方法,采用的都是左闭右开区间。借鉴统一使用!
采用一维数组来存储线段树中的节点元素,所以必须要有一种数学关系将输入的元素(最底层叶子元素)对应到线段树的上层节点。
自底向上建树。程序输入有m个数,这m其实就是线段树的叶子节点元素,但为了便于计算,将其扩大到2的幂,多出来的元素用不会影响程序的值的填充。
在2的基础上自底向上建树,得到的就是一个满二叉树,而根据数学中等比数列和及其通项公式之间的关系:若第k层有n个元素,则前k-1层有n-1个元素。所以,存储整个线段树的数组空间大小应为(2*n-1)。综合2、3,也就是有了常说的“线段树开4倍空间的说法”。
4.最后一层有n个元素,其中前m个是程序输入的数据,最后一层之前的共有n-1个元素,所以若是从0开始编号的话,最后一层自左向右第一个元素的编号为n-1,这也就是在更新函数update中有”k += n-1;”这句话的原因。
5.线段树在进行查询操作时,用query(a,b,0,0,n),注意这里最后一个参数是n而不是m,因为在init操作中最后一层的元素已经由m变成了n个,区间编号0也应该对应[0,n)
线段树,根据节点中维护的数据的不同,线段树可以提供不同的功能。
节点维护区间最小值,用线段树求区间最小值:
import java.util.Random; import java.util.Scanner; public class Main { public static int n; public static int[] nums; public static void init(int m){ n = 1; while(n<m) n <<= 1; for(int i=0;i<2*n-1;i++) nums[i] = Integer.MAX_VALUE; } public static void update(int k,int v){ k += n-1;//输入序列中的编号(最后一层)在一维数组中的下标对应 nums[k] = v; while(k>0){ k = (k-1)/2; nums[k] = Math.min(nums[k*2+1], nums[k*2+2]); } } //递归查询 //[a,b)是待查询区间,[l,r)是当前所在区间,k为该区间的编号。 public static int query(int a,int b,int k,int l,int r){ if(a<=l&&r<=b) return nums[k]; else if(l>=b||r<=a) return Integer.MAX_VALUE; else{ int m = l+(r-l)/2; int left = query(a, b, l, m, k*2+1); int right = query(a, b, m, r, k*2+2); return Math.min(left, right); } //return Integer.MAX_VALUE; } public static void show(){ System.out.println("nums:"); for(int i=0;i<2*n-1;i++) System.out.print(nums[i]+" "); System.out.println(); } public static void main(String[] args) { // TODO Auto-generated method stub Scanner in = new Scanner(System.in); Random rand = new Random(); while(in.hasNext()){ int m = in.nextInt(); nums = new int[m*4]; init(m); int t; for(int i=0;i<m;i++){ t = in.nextInt(); update(i, t); } //show(); //明确编号及其对应区间! int ans = query(0 , 5 , 0, 0 , n); System.out.println(ans); } } }
note:
1.满二叉树:每一层都是满的(拥有全部元素,对应二叉树的性质——第k层,最多有2^(k-1)个节点)。
2.完全二叉树:树中除了最后一层,其余层都是满的;而且最后一层或者是满的,或者是在右边缺少连续的若干接点,即若对节点编号的话,应与满二叉树相对应。
3.树中父子节点编号的值对应关系(不必刻意去记忆,由于是规律,届时只需要画出二叉树的前两层,即可使用了):
son(n):
当前节点编号为n,若树中节点编号是从0开始的,则其左子树根的节点编号为
2*n+1,右子树根的节点编号为2*n+2 ; 若树中节点编号是从1开始的,若根节点为n,则其左子树根的节点编号为2*n,右子树根的节点编号为2*n+1。
parent(n):
当前节点编号为n,若树中节点编号是从0开始的,则其父节点编号为(n-1)/2;若树中节点编号是从1开始的,则其父节点编号为n/2。
ps:程序中涉及到区间的时候,不论是STL、还是Java中的方法,采用的都是左闭右开区间。借鉴统一使用!
相关文章推荐
- 数据结构实验题目
- 第八周 数据结构之自建算法库——顺序串【项目5 - 计数的模式匹配】
- 【数据结构】平衡二叉树之AVL树
- 【数据结构】平衡二叉树之AVL树
- 第八周 数据结构实践项目——串【项目4-字符串加密】
- 数据结构(Java)——队列的实现
- 数据结构--链表(java)
- 第八周 数据结构实践——顺序串算法【项目3- 顺序串算法】
- 线索二叉树 – 数据结构和算法48
- 二叉树的建立和遍历算法 – 数据结构和算法47
- 二叉树的遍历 – 数据结构和算法46
- 二叉树2 – 数据结构和算法44
- 二叉树的存储结构 – 数据结构和算法45
- 二叉树 – 数据结构和算法43
- 树的存储结构2 – 数据结构和算法42
- 树的存储结构 – 数据结构和算法41
- 树 – 数据结构和算法40
- KMP算法之最终实现及优化 – 数据结构和算法39
- KMP算法之NEXT数组代码原理分析 – 数据结构和算法38
- KMP算法(养成篇2) – 数据结构和算法37