![](/img/trans.png)
[英]Given a number N how many pairs of numbers have square sum less than or equal to N?
[英]How many number are less than or equal than x in an array?
给定一个 integer n和数组a ,我需要找到每个i , 1≤ i ≤ n ,左边有多少个元素小于或等于a i
5
1 2 1 1 2
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 ranges
在tree structure
中,它们的first value
作为key
, node
的value
应该是array of pair of first and last index of range
。 我将在这里使用std::map
。 这种树结构是必需的,这样我们就可以通过找到upper bound
,在logarithmic
时间内找到first
元素小于或等于当前元素的所有范围。 这就是原因,当我比较索引6
处的元素时,我提前知道所有三个已知时间范围都不可观,其中只有两个是可观的。
解决方案的复杂性是,
O(n)
在数组中从左到右移动, plus
O(n (m + log m))
用于在std::map
中找到每个元素的upper bound
并比较 m 个范围的最后一个值,这里m
是在特定时间知道的范围数, plus
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.