题目地址:
https://www.lintcode.com/problem/interval-minimum-number/description
给定一个数组,再给定若干次查询,要求返回区间内的最小值。返回这些最小值的列表。
思路是线段树,每个树的节点维护某个区间的最小值,然后树的每一层下去逐次区间长度减半。代码如下:
import java.util.ArrayList;
import java.util.List;
public class Solution {
class SegmentTree {
private int[] tree;
private int n;
public SegmentTree(int[] A) {
n = A.length;
tree = new int[4 * A.length];
// 初始化相当于做n次更新
for (int i = 0; i < A.length; i++) {
update(i, A[i]);
}
}
// 执行A[idx] = val
private void update(int idx, int val) {
update(0, 0, n - 1, idx, val);
}
// 对以treeIdx为树根的子树进行更新,其维护的区间是A[l, r],执行A[idx] = val
private void update(int treeIdx, int l, int r, int idx, int val) {
if (l == r) {
tree[treeIdx] = val;
return;
}
int m = l + (r - l >> 1);
// 看一下在哪个半边,对其进行更新
if (idx <= m) {
update(leftChild(treeIdx), l, m, idx, val);
} else {
update(rightChild(treeIdx), m + 1, r, idx, val);
}
// 更新子树之后递归返回的时候要记得更新树根
tree[treeIdx] = Math.min(tree[leftChild(treeIdx)], tree[rightChild(treeIdx)]);
}
// 查询A[qL, ..., qR]中的最小值
public int queryMin(int qL, int qR) {
return queryMin(0, 0, n - 1, qL, qR);
}
// 对以treeIdx为树根的子树进行查询,其维护的区间是A[l, r],查询A[qL, ..., qR]中的最小值
private int queryMin(int treeIdx, int l, int r, int qL, int qR) {
// 如果树根维护的范围就是要查询的范围,那就不用递归了,直接返回本树根维护的最小值
if (l == qL && r == qR) {
return tree[treeIdx];
}
// 求一下本树根维护的区间的中点
int m = l + (r - l >> 1);
// 若要查询的区间完全在右半边,则只去右半边查询;若完全在左半边,则只去左半边查询
if (qL >= m + 1) {
return queryMin(rightChild(treeIdx), m + 1, r, qL, qR);
} else if (qR <= m) {
return queryMin(leftChild(treeIdx), l, m, qL, qR);
}
// 若与两个半边都有交集,则都查询,然后汇总求个最小值
return Math.min(queryMin(leftChild(treeIdx), l, m, qL, m), queryMin(rightChild(treeIdx), m + 1, r, m + 1, qR));
}
private int leftChild(int treeIdx) {
return 2 * treeIdx + 1;
}
private int rightChild(int treeIdx) {
return 2 * treeIdx + 2;
}
}
/**
* @param A: An integer array
* @param queries: An query list
* @return: The result list
*/
public List<Integer> intervalMinNumber(int[] A, List<Interval> queries) {
// write your code here
List<Integer> res = new ArrayList<>();
SegmentTree segTree = new SegmentTree(A);
for (Interval query : queries) {
res.add(segTree.queryMin(query.start, query.end));
}
return res;
}
}
class Interval {
int start, end;
public Interval(int start, int end) {
this.start = start;
this.end = end;
}
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)(初始化 O ( n log n ) O(n\log n) O(nlogn),每次查询 O ( log n ) O(\log n) O(logn)),空间 O ( n ) O(n) O(n)。