📜  子数组中的Kth个最小元素

📅  最后修改于: 2021-04-17 12:32:57             🧑  作者: Mango

给定大小为N的数组arr 。任务是在子数组中找到第k个最小的元素(1到r,都包括在内)。

笔记 :

  • 查询类型为query(l,r,k)
  • 1 <= k <= r-l + 1
  • 可能有多个查询。

例子:

Let, S = r - l + 1.

天真的方法:

  • 将子数组复制到其他本地数组中。排序后,找到第k个元素。

    时间复杂度: Slog(S)

  • 使用最大优先级队列“ p”并在子数组中进行迭代。如果’p’的大小小于’k’,则插入元素,否则删除顶部元素,并在完全插入’p’之后将新元素插入到’p’中。

    时间复杂度: Slog(k)

高效方法:想法是使用细分树,更准确地说是使用合并排序细分树。在这里,我们存储的不是已排序元素,而是存储已排序元素的索引。

B为对arr排序后的数组,而seg为我们的段树。 seg的节点c i存储在[st,end]范围内的arr索引的排序顺序。

If arr = {3, 1, 5, 2, 4, 7, 8, 6},
then B is {1, 2, 3, 4, 5, 6, 7, 8}

段树如下所示:

假设seg [ci]-> left包含p个元素。如果p小于或等于k ,则可以找到左子中第k个最小的元素;如果p小于k,则移到右子中并找到(kp)个最小的元素。

可以通过以下方法在元素X和Y之间找到排序数组(A)中的元素数:

下面是上述方法的实现:

// CPP program to find the kth smallest element in a range
#include 
using namespace std;
#define N (int)1e5
  
// Declaring a global segment tree
vector seg[N];
  
// Function to build the merge sort
// segment tree of indices
void build(int ci, int st, int end,
           pair* B)
{
    if (st == end) {
        // Using second property of B
        seg[ci].push_back(B[st].second);
        return;
    }
  
    int mid = (st + end) / 2;
    build(2 * ci + 1, st, mid, B);
    build(2 * ci + 2, mid + 1, end, B);
  
    // Inbuilt merge function
    // this takes two sorted arrays and merge
    // them into a sorted array
    merge(seg[2 * ci + 1].begin(), seg[2 * ci + 1].end(),
          seg[2 * ci + 2].begin(), seg[2 * ci + 2].end(),
          back_inserter(seg[ci]));
}
  
// Function to return the index of
// kth smallest element in range [l, r]
int query(int ci, int st, int end,
          int l, int r, int k)
{
    // Base case
    if (st == end)
        return seg[ci][0];
  
    // Finding value of 'p' as described in article
    // seg[2*ci+1] is left node of seg[ci]
    int p = upper_bound(seg[2 * ci + 1].begin(),
                        seg[2 * ci + 1].end(), r)
            - lower_bound(seg[2 * ci + 1].begin(),
                          seg[2 * ci + 1].end(), l);
  
    int mid = (st + end) / 2;
    if (p >= k)
        return query(2 * ci + 1, st, mid, l, r, k);
    else
        return query(2 * ci + 2, mid + 1, end, l, r, k - p);
}
  
// Driver code
int main()
{
    int arr[] = { 3, 1, 5, 2, 4, 7, 8, 6 };
    int n = sizeof(arr) / sizeof(arr[0]);
  
    pair B[n];
  
    for (int i = 0; i < n; i++) {
        B[i] = { arr[i], i };
    }
  
    // After sorting, B's second property is
    // something upon which we will build our Tree
    sort(B, B + n);
  
    // Build the tree
    build(0, 0, n - 1, B);
  
    cout << "3rd smallest element in range 3 to 7 is: "
         << arr[query(0, 0, n - 1, 2, 6, 3)] << "\n";
}
输出:
3rd smallest element in range 3 to 7 is: 5

时间复杂度:
要构建段树:O(n * log(n))
对于每个查询:O(log(n)* log(n))