给定大小为N的数组arr 。任务是在子数组中找到第k个最小的元素(1到r,都包括在内)。
笔记 :
- 查询类型为query(l,r,k)
- 1 <= k <= r-l + 1
- 可能有多个查询。
例子:
Input : arr = {3, 2, 5, 4, 7, 1, 9}, query = (2, 6, 3)
Output : 4
sorted subarray in a range 2 to 6 is {1, 2, 4, 5, 7} and 3rd element is 4
Input : arr = {2, 3, 4, 1, 6, 5, 8}, query = (1, 5, 2)
Output : 2
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)中的元素数:
upper_bound(A.begin(), A.end(), Y)-lower_bound(A.begin(), A.end(), X)
下面是上述方法的实现:
// 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))