数据结构的特点
- 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
- 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。
- 因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
图例说明
代码实现(以求区间和为例)
构建Segment Tree
/**
* 定义树节点
*/
private static class TreeNode {
public int start;
public int end;
public int num;
public TreeNode left;
public TreeNode right;
public TreeNode (int start,int end) {
this.start = start;
this.end = end;
}
public TreeNode (int start,int end,int num) {
this(start, end);
this.num = num;
}
}
/**
* 构建segment tree
* @param arr
* @return
*/
public static TreeNode buildTree(int[] arr) {
if (arr == null || arr.length == 0) {
return null;
}
return buildTree(arr,0,arr.length-1);
}
/**
* 采用递归的方式构建树
* @param arr
* @param start
* @param end
* @return
*/
private static TreeNode buildTree(int[] arr,int start,int end) {
TreeNode node = null;
if (end == start) {
node = new TreeNode(start,start,arr[start]);
}
else {
int mid = start + (end -start) /2;
node = new TreeNode(start,end);
node.left = buildTree(arr,start,mid);
node.right = buildTree(arr,mid+1,end);
node.num = node.left.num + node.right.num;
}
return node;
}
构建线段树的时间复杂度:O(n);
查询区间树的值
递归的查询区间树。
private static int querySum(TreeNode node,int start,int end) {
/**
* 节点为==null,返回Null
*/
if (node == null) {
return 0;
}
int nStart = node.start;
int nEnd = node.end;
/**
* 如果左右区间相等,则返回值
*/
if (nStart == start && nEnd == end) {
return node.num;
}
/**
* 获取区间中间值
*/
int nMid = nStart + (nEnd-nStart) / 2;
/**
* 左区间的[start,mid]
*/
if (nMid >= end) {
return querySum(node.left,start,end);
}
/**
* 右区间[mid+1,end]
*/
else if (nMid < start) {
return querySum(node.right,start,end);
}
else if (nMid >= start && nMid < end) {
return querySum(node.left,start,nMid) + querySum(node.right,nMid+1,end);
}
return 0;
}
查询时间复杂度:O(logn)
线段树的更新
往数组中添加元素,删除元素会让整个线段树发生改变。因此不建议添加或者删除元素。
但可以更新数组中元素的值。
- 计算出需要更新元素的差值:diff =新值-旧值
- 从更元素开始更新元素的sum值: sum = sum + diff
- 依据更新元素的index,递归的更新左子树或者右子树,直到更新到叶子节点。
private static void updateTreeNode(TreeNode node,int index,int diff) {
int start = node.start;
int end = node.end;
int mid = start + (end - start) / 2;
node.num = node.num + diff;
/**
* 左右区间相等,说明是叶子节点
*/
if (start == end) {
return;
}
/**
* 如果更新的index in [start,mid]
*/
else if (index <= mid) {
updateTreeNode(node.left,index,diff);
}
/**
* 如果更新的index in [mid+1,end]
*/
else if (index > mid) {
updateTreeNode(node.right,index,diff);
}
}