线段树:
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
Lintcode上关于线段树的三道题都采用了递归的思想,解发如下。
Lintcode(201)线段树的构造
Description:
线段树是一棵二叉树,他的每个节点包含了两个额外的属性start和end用于表示该节点所代表的区间。start和end都是整数,并按照如下的方式赋值:
• 根节点的 start和 end 由build方法所给出。
• 对于节点 A的左儿子,有start=A.left,end=(A.left + A.right) / 2。
• 对于节点 A的右儿子,有start=(A.left +A.right) / 2 + 1, end=A.right。
• 如果 start等于 end, 那么该节点是叶子节点,不再有左右儿子。
实现一个build方法,接受 start和 end 作为参数, 然后构造一个代表区间[start, end]的线段树,返回这棵线段树的根。
Explanation:
比如给定start=1,end=6,对应的线段树为:
[1, 6]
/ \
[1, 3] [4, 6]
/ \ / \
[1, 2] [3,3] [4, 5] [6,6]
/ \ / \
[1,1] [2,2] [4,4] [5,5]
Solution:
/**
* Definition of SegmentTreeNode:
* public class SegmentTreeNode {
* public int start, end;
* public SegmentTreeNode left, right;
* public SegmentTreeNode(int start, int end) {
* this.start = start, this.end = end;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
*@param start, end: Denote an segment / interval
*@return: The root of Segment Tree
*/
public SegmentTreeNode build(int start, int end) {
// write your code here
if(start > end) return null;
SegmentTreeNode head = new SegmentTreeNode(start , end);
if(start < end){
head.left = build(start , (start + end) / 2);
head.right = build((start + end) / 2 + 1 , end);
};
return head;
}
}
Lintcode(202)线段树的查询
Description:
对于一个有n个数的整数数组,在对应的线段树中,根节点所代表的区间为0-n-1,每个节点有一个额外的属性max,值为该节点所代表的数组区间start到end内的最大值。
为SegmentTree设计一个query的方法,接受3个参数root,start和end,线段树root所代表的数组中子区间[start, end]内的最大值。
Explanation:
对于数组[1, 4, 2, 3],对应的线段树为:
[0, 3, max=4]
/ \
[0,1,max=4] [2,3,max=3]
/ \ / \
[0,0,max=1] [1,1,max=4] [2,2,max=2],[3,3,max=3]
query(root, 1, 1), return 4
query(root, 1, 2), return 4
query(root, 2, 3), return 3
query(root,0, 2), return 4
Solution:
/**
* Definition of SegmentTreeNode:
* public class SegmentTreeNode {
* public int start, end, max;
* public SegmentTreeNode left, right;
* public SegmentTreeNode(int start, int end, int max) {
* this.start = start;
* this.end = end;
* this.max = max
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
*@param root, start, end: The root of segment tree and
* an segment / interval
*@return: The maximum number in the interval [start, end]
*/
public int query(SegmentTreeNode root, int start, int end) {
// write your code here
if(root == null) return 0;
if(root.start >= start && root.end <= end){
return root.max;
}
int mid = root.start + (root.end - root.start)/2;
int leftValue = query(root.left , start , Math.min(mid , end));
int rightValue = query(root.right , Math.max(mid , start) , end);
return Math.max(leftValue , rightValue);
}
}
Lintcode(203)线段树的修改
Description:
对于一棵最大线段树,每个节点包含一个额外的max属性,用于存储该节点所代表区间的最大值。
设计一个modify的方法,接受三个参数root、index和value。该方法将 root 为跟的线段树中 [start, end] = [index, index]的节点修改为了新的 value,并确保在修改后,线段树的每个节点的 max属性仍然具有正确的值。
Explanation:
对于线段树:
[1, 4, max=3]
/ \
[1, 2, max=2] [3, 4, max=3]
/ \ / \
[1,1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3]
如果调用modify(root, 2, 4),返回:
[1, 4, max=4]
/ \
[1, 2, max=4] [3, 4, max=3]
/ \ / \
[1,1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3]
或调用modify(root, 4, 0),返回:
[1, 4, max=2]
/ \
[1, 2, max=2] [3, 4, max=0]
/ \ / \
[1, 1, max=2], [2, 2, max=1], [3, 3,max=0], [4, 4, max=0]
Solution:
/**
* Definition of SegmentTreeNode:
* public class SegmentTreeNode {
* public int start, end, max;
* public SegmentTreeNode left, right;
* public SegmentTreeNode(int start, int end, int max) {
* this.start = start;
* this.end = end;
* this.max = max
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
*@param root, index, value: The root of segment tree and
*@ change the node's value with [index, index] to the new given value
*@return: void
*/
public void modify(SegmentTreeNode root, int index, int value) {
// write your code here
if(root.start == index && root.end == index){
root.max = value;
return;
}
int mid = root.start + (root.end - root.start)/2;
if(index <= mid){
modify(root.left , index , value);
}else{
modify(root.right , index , value);
}
root.max = Math.max(root.left.max , root.right.max);
}
}