给定一个大小为N的数组 arr[]和形式为 [L, R] 的 Q查询,任务是在给定范围内找到该数组中不同值的数量。
例子:
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
朴素的方法:一个简单的解决方案是,对于每个 Query,将数组从 L 迭代到 R 并在集合中插入元素。最后,集合的大小给出了从 L 到 R 的不同元素的数量。
时间复杂度: O(Q * N)
Efficient Approach:思路是使用Merge Sort Tree来解决这个问题。
- 我们将下一次出现的元素存储在一个临时数组中。
- 然后对于从 L 到 R 的每个查询,我们将找到临时数组中值大于 R 在 L 到 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)
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live