ngoaho91의 답변을 보완합니다.
이 문제를 해결하는 가장 좋은 방법은 세그먼트 트리 데이터 구조를 사용하는 것입니다. 이를 통해 O (log (n))로 이러한 쿼리에 응답 할 수 있습니다. 즉, 알고리즘의 총 복잡도는 O (Q logn)입니다. 여기서 Q는 쿼리 수입니다. 순진한 알고리즘을 사용하는 경우 총 복잡도는 O (Q n)가되며 이는 상당히 느립니다.
그러나 세그먼트 트리 사용에는 단점이 있습니다. 메모리를 많이 차지하지만 속도보다 메모리에 대한 관심이 적습니다.
이 DS에서 사용하는 알고리즘을 간단히 설명하겠습니다.
세그먼트 트리는 이진 검색 트리의 특별한 경우입니다. 여기서 모든 노드는 할당 된 범위의 값을 보유합니다. 루트 노드에는 범위 [0, n]이 할당됩니다. 왼쪽 자식에는 범위 [0, (0 + n) / 2]와 오른쪽 자식 [(0 + n) / 2 + 1, n]이 할당됩니다. 이런 식으로 나무가 만들어집니다.
트리 만들기 :
/*
A[] -> array of original values
tree[] -> Segment Tree Data Structure.
node -> the node we are actually in: remember left child is 2*node, right child is 2*node+1
a, b -> The limits of the actual array. This is used because we are dealing
with a recursive function.
*/
int tree[SIZE];
void build_tree(vector<int> A, int node, int a, int b) {
if (a == b) { // We get to a simple element
tree[node] = A[a]; // This node stores the only value
}
else {
int leftChild, rightChild, middle;
leftChild = 2*node;
rightChild = 2*node+1; // Or leftChild+1
middle = (a+b) / 2;
build_tree(A, leftChild, a, middle); // Recursively build the tree in the left child
build_tree(A, rightChild, middle+1, b); // Recursively build the tree in the right child
tree[node] = max(tree[leftChild], tree[rightChild]); // The Value of the actual node,
//is the max of both of the children.
}
}
쿼리 트리
int query(int node, int a, int b, int p, int q) {
if (b < p || a > q) // The actual range is outside this range
return -INF; // Return a negative big number. Can you figure out why?
else if (p >= a && b >= q) // Query inside the range
return tree[node];
int l, r, m;
l = 2*node;
r = l+1;
m = (a+b) / 2;
return max(query(l, a, m, p, q), query(r, m+1, b, p, q)); // Return the max of querying both children.
}
추가 설명이 필요하면 알려주십시오.
BTW, 세그먼트 트리는 단일 요소 또는 O (log n)의 요소 범위 업데이트도 지원합니다.