为 Q 查询在给定价格范围内查找具有最大权重的元素
给定一个大小为N的数组arr[] ,其中每个元素表示(price, weight)形式的一对,表示每个项目的价格和重量。给定表示价格范围的[X, Y]形式的Q查询。任务是在每个查询的给定价格范围内找到权重最高的元素。
例子:
Input: arr[][] = {{24, 6}, {30, 8}, {21, 7}},
queries[][] = {{10, 24}, {20, 30}}
Output: [7, 8]
Explanation: The following are the items chosen for the above range
For first query: There are two items with given range [10, 24] -> {24, 6} and {21, 7} . Highest weight is 7.
For second query: There are two items with given range [20, 30] -> {24, 6}, {21, 7} and {30, 8}. Highest weight is 8.
Therefore, answer is [7, 8].
Input: arr[][] = {{1000, 300}, {1100, 400}, {1300, 200}, {1700, 500}, {2000, 600}},
queries[][] = {{1000, 1400}, {1700, 500}, {2000, 600}}
Output: [400, 500, 600]
天真的方法:一个简单的解决方案是为价格范围运行一个循环并找到每个查询的最大权重。
时间复杂度: O(Q*N)。
辅助空间: O(1)
有效方法:一种有效的方法是对任何i和 j 在价格范围[i, j]中存储最大权重进行预处理。在适当的时间内使用 Segment Tree 进行预处理和查询。
Representation of Segment trees
- Leaf Nodes are the weight corresponding to elements of the input array.
- Each internal node represents the maximum weight of all leaves under it.
An array representation of a tree is used to represent Segment Trees. For each node at index i, the left child is at index 2*i+1, the right child is at 2*i+2 and the parent is at ⌊(i – 1) / 2⌋.
可以通过将方法分为两部分来详细说明解决方案:
从给定数组构造段树:
- 从段[0 . . . N-1] 。并且每次将当前段分成两半(如果它还没有变成长度为 1 的段)。
- 然后在两半上调用相同的过程,并且对于每个这样的段,将最大值存储在段树节点中。
这里的每个节点代表给定细分指数之间给定价格范围的最大权重。
注意:除了最后一层之外,所构建的段树的所有层都将被完全填满。此外,这棵树将是一个完整的二叉树,因为段在每一层都分为两半。由于构造的树始终是具有N个叶子的完整二叉树。
查询给定范围的最小值:一旦树被构建,如何使用构建的段树进行范围最大值查询。以下是获得最大值的算法。
- 如果节点的价格范围与查询的给定价格范围相同,则返回节点中的值。
- 如果范围完全超出给定范围,则返回一个极高的值或说无限值。
- 否则,为左右孩子调用递归函数,并返回从递归调用中接收到的最大值。
请参阅下图以了解给定输入的线段树的形成。
请参阅以下算法以更好地理解。
// qs –> query start price, qe –> query end price
int RMQ(node, qs, qe)
{
if price range of node is within qs and qe
return value in node
else if price range of node is completely outside qs and qe
return INFINITE
else
return max( RMQ(node’s left child, qs, qe), RMQ(node’s right child, qs, qe) )
}
下面是上述方法的实现。
Java
// Java code to implement above approach
import java.io.*;
import java.util.*;
class GFG {
static int[] segmentTree;
// Function to get mid
public static int getMid(int start,
int end)
{
return start + (end - start) / 2;
}
// Function to fill segment tree
public static void fillSegmentTree(int[][] arr)
{
Arrays.sort(arr, new Comparator() {
@Override
public int compare(int[] o1,
int[] o2)
{
return o1[0] - o2[0];
}
});
int n = arr.length;
int maxHeight
= (int)Math.ceil(Math.log(n)
/ Math.log(2));
int maxSize
= 2 * (int)Math.pow(2, maxHeight) - 1;
segmentTree = new int[maxSize];
fillSegmentTreeUtil(segmentTree, arr,
0, n - 1, 0);
}
// Function to utilise the segment tree
public static int
fillSegmentTreeUtil(int[] segmentTree,
int[][] arr,
int start, int end,
int currNode)
{
if (start == end) {
segmentTree[currNode]
= arr[start][1];
return segmentTree[currNode];
}
int mid = getMid(start, end);
segmentTree[currNode] = Math.max(
fillSegmentTreeUtil(segmentTree,
arr, start,
mid, currNode
* 2
+ 1),
fillSegmentTreeUtil(segmentTree,
arr, mid + 1,
end, currNode
* 2
+ 2));
return segmentTree[currNode];
}
// Function to find the maximum rating
public static int findMaxRating(int[][] arr,
int[] query)
{
int n = arr.length;
return findMaxRatingUtil(segmentTree,
arr, 0, n - 1,
query[0],
query[1], 0);
}
// Function to utilise the
// maxRating function
public static int
findMaxRatingUtil(int[] segmentTree,
int[][] arr,
int start, int end,
int qStart,
int qEnd, int currNode)
{
if (qStart <= arr[start][0]
&& qEnd >= arr[end][0]) {
return segmentTree[currNode];
}
if (qStart > arr[end][0] || qEnd < arr[start][0]) {
return -1;
}
int mid = getMid(start, end);
return Math.max(
findMaxRatingUtil(segmentTree,
arr, start, mid,
qStart, qEnd,
currNode * 2 + 1),
findMaxRatingUtil(segmentTree,
arr, mid + 1,
end, qStart, qEnd,
currNode * 2 + 2));
}
// Driver code
public static void main(String[] args)
{
int[][] arr = { { 1000, 300 },
{ 1100, 400 },
{ 1300, 200 },
{ 1700, 500 },
{ 2000, 600 } };
fillSegmentTree(arr);
int[][] queries = { { 1000, 1400 },
{ 1700, 1900 },
{ 0, 3000 } };
for (int[] query : queries) {
System.out.println(
findMaxRating(arr, query));
}
}
}
400
500
600
时间复杂度: O(N log N + Q*log N)
辅助空间: O(N)
分析时间复杂度:对给定输入数组进行排序的时间复杂度为O(N * LogN)
树构造的时间复杂度是O(N) 。一共有2N-1个节点,每个节点的值在建树时只计算一次。
每个查询的时间复杂度为O(LogN) 。要查询一个范围最大值,每个级别最多处理两个节点,级别数为O(LogN) 。