给定大小为N的数组arr []和包含M个查询的集合Q [] [],任务是在给定数组上执行查询,以便可以有两种类型的查询:
- 类型1: [i,x] –将第i个索引处的元素更新为x。
- 类型2: [k] –在数组中找到第k个最小的元素。
例子:
Input: arr[] = {4, 3, 6, 2}, Q[][] = {{1, 2, 5}, {2, 3}, {1, 1, 7}, {2, 1}}
Output: 5 2
Explanation:
For the 1st query: arr[] = {4, 5, 6, 2}
For the 2nd query: 3rd smallest element would be 5.
For the 3rd query: arr[] = {7, 5, 6, 2}
For the 4th query: 1st smallest element would be 2.
Input: arr[] = {1, 0, 4, 2, 0}, Q[][] = {{1, 2, 1}, {2, 2}, {1, 4, 5}, {1, 3, 7}, {2, 1}, {2, 5}}
Output: 1 0 7
幼稚的方法:针对此问题的幼稚的方法是在恒定时间内更新数组中的第i个元素,并使用排序找到第K个最小的元素。
时间复杂度: O(M *(N * log(N)))其中,M是查询数,N是数组的大小。
高效方法:想法是使用类似于集合的基于策略的数据结构。
此处,基于树的容器用于以排序树的形式存储数组,以使左侧的所有节点都小于根,而右侧的所有节点都大于根。以下是数据结构的属性:
- 通过保持节点不变来建立索引,其中每个节点在其子树中都包含一个节点数。
- 每次插入一个新节点或删除一个节点时,我们都可以冒泡到根,从而保持O(logN)时间的不变性。
- 因此,节点在其左子树中的计数按排序顺序给出了该节点的索引,因为左子树中每个节点的值都小于父节点。
因此,我们的想法是对每个查询采用以下方法:
- 类型1:对于此查询,我们更新数组的第i个元素。因此,我们需要更新数组和数据结构中的元素。为了更新树容器中的值,在树中找到值arr [i],将其从树中删除,然后将更新后的值重新插入树中。
- 类型2:为了找到第K个最小元素,在树上使用find_by_order(K – 1),因为数据是排序数据。这类似于对排序数组执行二进制搜索操作。
下面是上述方法的实现:
// C++ implementation of the above approach
#include
#include
#include
using namespace std;
using namespace __gnu_pbds;
// Defining the policy based Data Structure
typedef tree,
null_type,
less >,
rb_tree_tag,
tree_order_statistics_node_update>
indexed_set;
// Elements in the array are not unique,
// so a pair is used to give uniqueness
// by incrementing cnt and assigning
// with array elements to insert in mySet
int cnt = 0;
// Variable to store the data in the
// policy based Data Structure
indexed_set mySet;
// Function to insert the elements
// of the array in mySet
void insert(int n, int arr[])
{
for (int i = 0; i < n; i++) {
mySet.insert({ arr[i], cnt });
cnt++;
}
}
// Function to update the value in
// the data structure
void update(int x, int y)
{
// Get the pointer of the element
// in mySet which has to be updated
auto it = mySet.lower_bound({ y, 0 });
// Delete from mySet
mySet.erase(it);
// Insert the updated value in mySet
mySet.insert({ x, cnt });
cnt++;
}
// Function to find the K-th smallest
// element in the set
int get(int k)
{
// Find the pointer to the kth smallest element
auto it = mySet.find_by_order(k - 1);
return (it->first);
}
// Function to perform the queries on the set
void operations(int arr[], int n,
vector > query, int m)
{
// To insert the element in mySet
insert(n, arr);
// Iterating through the queries
for (int i = 0; i < m; i++) {
// Checking if the query is of type 1
// or type 2
if (query[i][0] == 1) {
// The array is 0-indexed
int j = query[i][1] - 1;
int x = query[i][2];
// Update the element in mySet
update(x, arr[j]);
// Update the element in the array
arr[j] = x;
}
else {
int K = query[i][1];
// Print Kth smallest element
cout << get(K) << endl;
}
}
}
// Driver code
int main()
{
int n = 5, m = 6, arr[] = { 1, 0, 4, 2, 0 };
vector > query = { { 1, 2, 1 },
{ 2, 2 },
{ 1, 4, 5 },
{ 1, 3, 7 },
{ 2, 1 },
{ 2, 5 } };
operations(arr, n, query, m);
return 0;
}
1
0
7
时间复杂度:由于每个操作都需要O(Log(N))时间,并且有M个查询,因此总体时间复杂度为O(M * Log(N)) 。