繁体   English   中英

数组中有多少个数小于或等于 x?

[英]How many number are less than or equal than x in an array?

给定一个 integer n和数组a ,我需要找到每个i , 1≤ in ,左边有多少个元素小于或等于a i

例子:

    5 
    1 2 1 1 2

Output

    0 1 1 2 4

我可以在 O( N 2 ) 中做到这一点,但我想问是否有任何方法可以更快地做到这一点,因为N非常大( N ≤ 10 6 )?

您可以使用分段树,您只需要使用称为范围树的修改版本。 范围树允许矩形查询,因此您可以将维度设为索引和值,并询问“什么值大于 x,索引介于 1 和 n 之间?” 假设某些常见的优化,查询可以在 O(log n) 中完成。

无论哪种方式,O(N^2) 在 N < 10^6 时都完全没问题。

是的,与 O(N^2) 即 O(NlogN) 相比,它可以以更好的时间复杂度完成。 我们可以使用分治算法和树的概念。

想看上面提到的两种算法的源码??? 访问这里

我认为 O(N^2) 应该是最坏的情况。 在这种情况下,我们必须至少遍历数组两次。 我尝试过 O(N^2):

import java.io.*; import java.lang.*;

public class GFG {

public static void main (String[] args) {
    int a[]={1,2,1,1,2};
   
   
   int i=0;
   int count=0;
   int b[]=new int[a.length];
   for(i=0;i<a.length;i++)
   {
       for(int c=0;c<i;c++)
       {
       if(a[i]>=a[c])
       {
            count++;
       }
         }
       b[i]=count;
       count=0;
   }
   for(int j=0;j<b.length;j++)
        System.out.print(b[j]+" ");
}`

我喜欢考虑一个更大的数组来解释,所以让我们考虑下面的数组,

2, 1, 3, 4, 7, 6, 5, 8, 9, 10, 12, 0, 11, 13, 8, 9, 12, 20, 30, 60

天真的方法是将一个元素与其左侧的所有元素进行比较。 朴素的方法具有 O(n^2) 的复杂性,这使得它对大数组没有用处。

如果你仔细观察这个问题,你会发现其中有一个模式,并且模式Rather than comparing with each left element of an element we can compare first and last value of a range! . 等一下,这里的范围是多少?

这些数字可以被视为范围,并且可以从数组中从左到右遍历来创建范围。 范围如下,

[2] , [1, 3, 4, 7] , [6] , [5, 8, 9, 10, 12] , [0, 11, 13] , [8, 9, 12, 20, 30, 60]

让我们开始从左到右遍历数组,看看我们如何创建这些范围,以及这些范围如何减少在元素左侧查找所有小或相等元素的工作量。

索引0在其左侧没有要比较的元素,因此我们从索引1开始,此时我们没有任何范围。 现在我们比较索引1和索引0的值。 1不小于或等于2 ,所以这是非常重要的比较,由于这种比较,我们知道之前的范围应该在这里结束,因为现在数字不是按顺序排列的,此时我们得到第一个范围[2] ,它仅包含单个元素,并且小于或等于索引1处元素左侧的元素数zero

继续在索引2处从左到右遍历,我们将它与索引1处的前一个元素进行比较,现在值1 <= 3这意味着一个新的范围没有在这里盯着,我们仍然在从索引1开始的相同范围内。 因此,要找到小于或等于多少元素,我们必须首先计算当前范围[1, 3)中有多少元素,在这种情况下只有一个元素,此时我们只有一个已知范围[2] ,它有一个小于3的元素,因此索引2处元素左侧的小于或等于元素的总数为 = 1 + 1 = 2 对于元素的 rest ,这可以以类似的方式完成,我想直接跳转到索引6 ,即数字5

在索引6 ,我们已经准备好发现三个范围[2][1, 3, 4, 7][6]但只需要考虑两个范围[2][1, 3, 4, 7] 我如何提前知道范围[6]没有比较是没有用的,将在本说明的末尾进行说明。 要在左侧找到小于或等于元素的数量,我们可以看到第一个范围[2]只有一个元素并且它小于5 ,第二个范围有第一个元素1小于5但最后一个元素是7并且它是大于5 ,所以我们不能考虑范围的所有元素,而是我们必须在这个范围内找到upper bound才能找到我们可以考虑多少个元素,并且可以通过binary search找到upper bound ,因为range is sorted的,所以这个范围包含三个元素1, 3, 4小于或等于5 两个范围中小于或等于5的元素总数为4 ,索引6是当前范围的第一个元素,并且在当前范围的左侧没有元素,因此总计数 = 1 + 3 + 0 = 4

这个解释的最后一点是,我们必须store rangestree structure中,它们的first value作为keynodevalue应该是array of pair of first and last index of range 我将在这里使用std::map 这种树结构是必需的,这样我们就可以通过找到upper bound ,在logarithmic时间内找到first元素小于或等于当前元素的所有范围。 这就是原因,当我比较索引6处的元素时,我提前知道所有三个已知时间范围都不可观,其中只有两个是可观的。
解决方案的复杂性是,

  1. O(n)在数组中从左到右移动, plus
  2. O(n (m + log m))用于在std::map中找到每个元素的upper bound并比较 m 个范围的最后一个值,这里m是在特定时间知道的范围数, plus
  3. 如果 rage 最后一个元素大于数字,则O(log q)用于查找范围内的upper bound ,这里q是特定范围内的元素数(可能需要也可能不需要)
#include <iostream>

#include <map>
#include <vector>

#include <iterator>
#include <algorithm>

unsigned lessThanOrEqualCountFromRage(int num, const std::vector<int>& numList,
                                      const std::map<int,
                                      std::vector<std::pair<int, int>>>& rangeMap){

    using const_iter = std::map<int, std::vector<std::pair<int, int>>>::const_iterator;

    unsigned count = 0;
    const_iter upperBoundIt = rangeMap.upper_bound(num);

    for(const_iter it = rangeMap.cbegin(); upperBoundIt != it; ++it){

        for(const std::pair<int, int>& range : it->second){

            if(numList[range.second] <= num){
                count += (range.second - range.first) + 1;
            }
            else{
                auto rangeIt = numList.cbegin() + range.first;

                count += std::upper_bound(rangeIt, numList.cbegin() +
                                          range.second, num) - rangeIt;
            }
        }
    }

    return count;
}


std::vector<unsigned> lessThanOrEqualCount(const std::vector<int>& numList){

    std::vector<unsigned> leftCountList;
    leftCountList.reserve(numList.size());

    leftCountList.push_back(0);

    std::map<int, std::vector<std::pair<int, int>>> rangeMap;

    std::vector<int>::const_iterator rangeFirstIt = numList.cbegin();

    for(std::vector<int>::const_iterator it = rangeFirstIt + 1, endIt = numList.cend();
        endIt != it;){

        std::vector<int>::const_iterator preIt = rangeFirstIt;

        while(endIt != it && *preIt <= *it){

            leftCountList.push_back((it - rangeFirstIt) +
                                    lessThanOrEqualCountFromRage(*it,
                                                                 numList, rangeMap));

            ++preIt;
            ++it;
        }

        if(endIt != it){

           int rangeFirstIndex = rangeFirstIt - numList.cbegin();
           int rangeLastIndex = preIt - numList.cbegin();

           std::map<int, std::vector<std::pair<int, int>>>::iterator rangeEntryIt =
                   rangeMap.find(*rangeFirstIt);

           if(rangeMap.end() != rangeEntryIt){

               rangeEntryIt->second.emplace_back(rangeFirstIndex, rangeLastIndex);
           }
           else{

               rangeMap.emplace(*rangeFirstIt, std::vector<std::pair<int, int>>{
                                    {rangeFirstIndex,rangeLastIndex}});
           }

           leftCountList.push_back(lessThanOrEqualCountFromRage(*it, numList,
                                                                rangeMap));

           rangeFirstIt = it;
           ++it;
        }
    }

    return leftCountList;
}

int main(int , char *[]){

    std::vector<int> numList{2, 1, 3, 4, 7, 6, 5, 8, 9, 10, 12,
                             0, 11, 13, 8, 9, 12, 20, 30, 60};

    std::vector<unsigned> countList = lessThanOrEqualCount(numList);

    std::copy(countList.cbegin(), countList.cend(),
              std::ostream_iterator<unsigned>(std::cout, ", "));

    std::cout<< '\n';
}

Output
0, 0, 2, 3, 4, 4, 4, 7, 8, 9, 10, 0, 11, 13, 9, 11, 15, 17, 18, 19,

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM