📌  相关文章
📜  查找第 k 个最小元素和点更新的查询:C++ 中的有序集

📅  最后修改于: 2021-09-06 06:23:22             🧑  作者: Mango

给定一个大小为 N 的数组 arr[] 和一个包含 M 个查询的集合 Q[][] ,任务是在给定的数组上执行查询,这样可以有两种类型的查询:

  • 类型 1: [i, x] – 将i 个索引处的元素更新为 x。
  • 类型 2: [k] – 查找数组中第k 个最小的元素。

例子:

朴素的方法:这个问题的朴素的方法是在恒定时间内更新数组中的第i 个元素,并使用排序找到第 K最小元素。

时间复杂度: O(M * (N * log(N))) 其中 M 是查询的数量,N 是数组的大小。

高效的方法:这个想法是使用类似于集合的基于策略的数据结构。

在这里,基于树的容器用于以排序树的形式存储数组,使得左边的所有节点都小于根,右边的所有节点都大于根。以下是数据结构的属性:

  • 它通过维护节点不变性来索引,其中每个节点包含其子树中的节点计数。
  • 每次我们插入一个新节点或删除一个节点时,我们可以通过冒泡到根来在 O(logN) 时间内保持不变量。
  • 因此,左子树中节点的计数按排序顺序给出该节点的索引,因为左子树的每个节点的值都小于父节点。

因此,我们的想法是对每个查询遵循以下方法:

  1. 类型 1:对于这个查询,我们更新数组的第 i 个元素。因此,我们需要更新数组和数据结构中的元素。为了更新树容器中的值,在树中找到值 arr[i],将其从树中删除,并将更新后的值插入回树中。
  2. 类型 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))

如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live