📜  合并排序树(在给定的行范围内,较小或相等的元素)

📅  最后修改于: 2021-04-17 11:21:42             🧑  作者: Mango

给定一个数组,其中每个元素都是一个向量,其中包含按排序顺序的整数。任务是回答以下查询:

count(start, end, k) : Count the numbers smaller than or equal 
                       to k in range from array index 'start'
                       to 'end'.

为了方便起见,我们考虑一个n * n 2-D数组,其中每一行对应一个整数向量。

例子:

Input : ar[][] = {{2, 4, 5},
                  {3, 4, 9}, 
                  {6, 8, 10}}

        Queries[] = (0, 1, 5)
                    (1, 2, 1)
                    (0, 2, 6)
Output : 5 
         0
         6
Count of elements (smaller than or equal to 5) from
1st row (index 0) to 2nd row (index 1) is 5.
Count of elements (smaller than or equal to 1) from
2nd row to 3nd row is 0
Count of elements (smaller than or equal to 6) from
1st row to 3nd row is 6.

关键思想是构建一个在每个节点上都有一个向量的分段树,并且该向量按排序顺序包含子范围的所有元素。而且,如果我们观察到这种分段树结构,则该结构与合并排序算法期间形成的树有些相似(这就是为什么将其称为合并排序树的原因)

// C++ program to count number of smaller or
// equal to given number and given row range.
#include
using namespace std;
  
const int MAX = 1000;
  
// Constructs a segment tree and stores sTree[]
void buildTree(int idx, int ss, int se, vector a[],
                                  vector sTree[])
{
    /*leaf node*/
    if (ss == se)
    {
        sTree[idx] = a[ss];
        return;
    }
  
    int mid = (ss+se)/2;
  
    /* building left subtree */
    buildTree(2*idx+1, ss, mid, a, sTree);
  
    /* building right subtree */
    buildTree(2*idx+2, mid+1, se, a, sTree);
  
    /* merging left and right child in sorted order */
    merge(sTree[2*idx+1].begin(), sTree[2*idx+1].end(),
          sTree[2*idx+2].begin(), sTree[2*idx+2].end(),
          back_inserter(sTree[idx]));
}
  
// Recursive function to count smaller elements from row
// a[ss] to a[se] and value smaller than or equal to k.
int queryRec(int node, int start, int end, int ss, int se,
         int k, vector a[], vector sTree[])
{
    /* If out of range return 0 */
    if (ss > end || start > se)
        return 0;
  
    /* if inside the range return count */
    if (ss <= start && se >= end)
    {
        /* binary search over the sorted vector
           to return count >= X */
        return upper_bound(sTree[node].begin(),
                           sTree[node].end(), k) -
                           sTree[node].begin();
    }
  
    int mid = (start+end)/2;
  
    /*searching in left subtree*/
    int p1 = queryRec(2*node+1, start, mid, ss, se, k, a, sTree);
  
    /*searching in right subtree*/
    int p2 = queryRec(2*node+2, mid+1, end, ss, se, k, a, sTree);
  
    /*adding both the result*/
    return p1 + p2;
}
  
// A wrapper over query().
int query(int start, int end, int k, vector a[], int n,
                                  vector sTree[])
{
   return queryRec(0, 0, n-1, start, end, k, a, sTree);
}
  
// Driver code
int main()
{
    int n = 3;
    int arr[][3] = { {2, 4, 5},
                     {3, 4, 9},
                     {6, 8, 10}};
  
    // build an array of vectos from above input
    vector a[n];
    for (int i=0; i sTree[MAX];
    buildTree(0, 0, n-1, a, sTree);
  
    /* un-comment to print merge sort tree*/
    /*for (int i=0;i<2*n-1;i++)
    {
        cout << i << "  ";
        for (int j=0;j

输出:

5
0
6

buildTree()分析:构建合并排序树需要O(N log N)时间,与合并排序算法相同。它还需要O(n log n)额外的空间。

query()分析:
从“开始”到“结束”的范围最多可以分为Log(n)个部分,我们将在每个部分执行二进制搜索。二进制搜索需要O(Log n)。因此,总复杂度为O(Log n * Log n)。