给定大小为N且Q个查询的数组arr [] ,其形式为[L,R],任务是在给定范围内找到此数组中不同值的数量。
例子:
Input: arr[] = {4, 1, 9, 1, 3, 3}, Q = {{1, 3}, {1, 5}}
Output:
3
4
Explanation:
For query {1, 3}, elements are {4, 1, 9}. Therefore, count of distinct elements = 3
For query {1, 5}, elements are {4, 1, 9, 1, 3}. Therefore, count of distinct elements = 4
Input: arr[] = {4, 2, 1, 1, 4}, Q = {{2, 4}, {3, 5}}
Output:
3
2
天真的方法:一个简单的解决方案是,对于每个查询,将数组从L迭代到R并将元素插入到集合中。最后,集合的大小给出了从L到R的不同元素的数量。
时间复杂度: O(Q * N)
高效的方法:想法是使用合并排序树来解决此问题。
- 我们将元素的下一次出现存储在临时数组中。
- 然后,对于从L到R的每个查询,我们将在临时数组中找到在L到R范围内值大于R的元素数。
步骤1:取一个数组next_right,其中next_right [i]保存数组a中数字i的下一个右索引。将此数组初始化为N(数组的长度)。
第2步:根据next_right数组创建合并排序树并进行查询。计算从L到R的不同元素数量的查询等效于找到从L到R的元素数量,这些元素的数量大于R。
从给定数组构造合并排序树
- 我们从一个段arr [0开始。 。 。 n-1]。
- 每次将当前段划分为两个半段(如果尚未变成长度为1的段)时,然后在两个半段上调用相同的过程,对于每个这样的段,我们将排序后的数组存储在每个段中,就像合并排序一样。
- 而且,该树将是完整的二叉树,因为我们总是在每个级别将分段分为两半。
- 由于构造的树始终是具有n个叶的完整二叉树,因此将有N-1个内部节点。因此,节点总数将为2 * N – 1。
这是一个例子。假设1 5 2 6 9 4 7 1是一个数组。
|1 1 2 4 5 6 7 9|
|1 2 5 6|1 4 7 9|
|1 5|2 6|4 9|1 7|
|1|5|2|6|9|4|7|1|
next_right数组的构造
- 我们存储每个元素的下一个正确出现的位置。
- 如果元素最后一次出现,则我们存储’N’(数组的长度)
例子:arr = [2, 3, 2, 3, 5, 6]; next_right = [2, 3, 6, 6, 6, 6]
下面是上述方法的实现:
C++
// C++ implementation to find
// count of distinct elements
// in a range L to R for Q queries
#include
using namespace std;
// Function to merge the right
// and the left tree
void merge(vector tree[],
int treeNode)
{
int len1 =
tree[2 * treeNode].size();
int len2 =
tree[2 * treeNode + 1].size();
int index1 = 0, index2 = 0;
// Fill this array in such a
// way such that values
// remain sorted similar to mergesort
while (index1 < len1 && index2 < len2) {
// If the element on the left part
// is greater than the right part
if (tree[2 * treeNode][index1] >
tree[2 * treeNode + 1][index2]) {
tree[treeNode].push_back(
tree[2 * treeNode + 1][index2]
);
index2++;
}
else {
tree[treeNode].push_back(
tree[2 * treeNode][index1]
);
index1++;
}
}
// Insert the leftover elements
// from the left part
while (index1 < len1) {
tree[treeNode].push_back(
tree[2 * treeNode][index1]
);
index1++;
}
// Insert the leftover elements
// from the right part
while (index2 < len2) {
tree[treeNode].push_back(
tree[2 * treeNode + 1][index2]
);
index2++;
}
return;
}
// Recursive function to build
// segment tree by merging the
// sorted segments in sorted way
void build(vector tree[],
int* arr, int start, int end,
int treeNode)
{
// Base case
if (start == end) {
tree[treeNode].push_back(
arr[start]);
return;
}
int mid = (start + end) / 2;
// Building the left tree
build(tree, arr, start,
mid, 2 * treeNode);
// Building the right tree
build(tree, arr, mid + 1, end,
2 * treeNode + 1);
// Merges the right tree
// and left tree
merge(tree, treeNode);
return;
}
// Function similar to query() method
// as in segment tree
int query(vector tree[],
int treeNode, int start, int end,
int left, int right)
{
// Current segment is out of the range
if (start > right || end < left) {
return 0;
}
// Current segment completely
// lies inside the range
if (start >= left && end <= right) {
// as the elements are in sorted order
// so number of elements greater than R
// can be find using binary
// search or upper_bound
return tree[treeNode].end() -
upper_bound(tree[treeNode].begin(),
tree[treeNode].end(), right);
}
int mid = (start + end) / 2;
// Query on the left tree
int op1 = query(tree, 2 * treeNode,
start, mid, left, right);
// Query on the Right tree
int op2 = query(tree, 2 * treeNode + 1,
mid + 1, end, left, right);
return op1 + op2;
}
// Driver Code
int main()
{
int n = 5;
int arr[] = { 1, 2, 1, 4, 2 };
int next_right[n];
// Initialising the tree
vector tree[4 * n];
unordered_map ump;
// Construction of next_right
// array to store the
// next index of occurence
// of elements
for (int i = n - 1; i >= 0; i--) {
if (ump[arr[i]] == 0) {
next_right[i] = n;
ump[arr[i]] = i;
}
else {
next_right[i] = ump[arr[i]];
ump[arr[i]] = i;
}
}
// building the mergesort tree
// by using next_right array
build(tree, next_right, 0, n - 1, 1);
int ans;
// Queries one based indexing
// Time complexity of each
// query is log(N)
// first query
int left1 = 0;
int right1 = 2;
ans = query(tree, 1, 0, n - 1,
left1, right1);
cout << ans << endl;
// Second Query
int left2 = 1;
int right2 = 4;
ans = query(tree, 1, 0, n - 1,
left2, right2);
cout << ans << endl;
}
2
3
时间复杂度: O(Q * log N)