C++ 给定一个排序数组和一个参数k,求线性时间内大于或等于k的两个数之和的计数

C++ 给定一个排序数组和一个参数k,求线性时间内大于或等于k的两个数之和的计数,c++,arrays,algorithm,sum,C++,Arrays,Algorithm,Sum,我试图找到一个和等于k的数组中的所有对。我当前的解决方案需要O(n*log(n))时间(下面的代码片段)。有人能帮我找到更好的解决方案吗,O(n)或O(lgn)(如果存在的话) map-mymap; 对它进行迭代器; cin>>n>>k; 对于(int i=0;i>a; if(mymap.find(a)!=mymap.end()) mymap[a]++; 其他的 mymap[a]=1; } for(it=mymap.begin();it!=mymap.end();it++){ int val=i

我试图找到一个和等于k的数组中的所有对。我当前的解决方案需要O(n*log(n))时间(下面的代码片段)。有人能帮我找到更好的解决方案吗,O(n)或O(lgn)(如果存在的话)

map-mymap;
对它进行迭代器;
cin>>n>>k;
对于(int i=0;i>a;
if(mymap.find(a)!=mymap.end())
mymap[a]++;
其他的
mymap[a]=1;
}
for(it=mymap.begin();it!=mymap.end();it++){
int val=it->first;
if(mymap.find(k-val)!=mymap.end()){
cnt+=min(it->second,mymap.find(k-val)->second);
它->秒=0;
}
}

库特@Drew Dorman-谢谢你的评论

用两个指针遍历数组<代码>左侧
右侧

假设
left
是小的一面,从
left
位置
0
开始,然后
right
向左移动,直到最后一次
a[left]+a[right]>=k

当达到此目标时,则
total_count+=(a.size-right+1)

然后你向左移动一步,
right
需要(可能)向它移动。重复这个直到他们相遇


当这项工作完成后,让我们假设他们在位置
x
相遇,然后
totla_count+=choose(2,a.size-x)
存在一种非常简单的
O(n)
方法,使用所谓的“两个指针”或“两个迭代器”方法。关键的思想是在同一个数组上运行两个迭代器(不一定是C++迭代器,索引也会这样),这样如果第一迭代器指向值<代码> x<代码>,那么第二迭代器指向数组中的最大元素,该元素小于<>代码kx。p> 我们将增加第一个迭代器,同时我们还将更改第二个迭代器以维护此属性。注意,随着第一个指针的增加,第二个指针的相应位置只会减少,因此在每次迭代中,我们可以从上一次迭代停止的位置开始;我们永远不需要增加第二个指针。这就是我们实现时间的方法

代码是这样的(没有测试这个,但是想法应该很清楚)

向量a;//给定数组 int r=a.size()-1; 对于(int l=0;l=0)和&(a[r]>=k-a[l])) r--; //现在r是a中的最大位置,因此a[r]
另一种编写代码可能更简单,但速度稍慢的方法是使用二进制搜索的
O(nlogn)
。对于数组的每个元素
a[l]
,您可以找到最大位置
r
,这样
a[r]就可以完成以下工作:

void count(int A[], int n) //n being the number of terms in array
{ int i, j, k, count = 0;
  cin>>k;

  for(i = 0; i<n; i++)
   for(j = 0; j<n; j++)
     if(A[i] + A[j] >= k)
      count++ ;

  cout<<"There are "<<count<<" such numbers" ;
} 
void count(int A[],int n)//n是数组中的项数
{int i,j,k,count=0;
cin>>k;
对于(i=0;i
  • 对阵列排序(n个日志n个)
  • 对于(i=1到n)
    • 从根本上开始
    • 如果[i]+curr\u节点>=k,则向左并匹配=indexof(curr\u节点)e
    • 否则,向右走
    • 如果curr_node=leaf node,则将[match]之后的所有节点添加到具有[i]的有效对列表中

  • 步骤2还需要O(n logn)。for循环运行n次。在循环中,我们对每个节点执行二进制搜索,即logn步骤。因此,该算法的总体复杂度为O(n logn)。

    另一种方法是,对于正数,在最佳情况下取O(logn),在最差情况下取O(nlogn):

  • 在数组中查找等于k/2的元素,或者如果它不存在,则查找大于k/2的最小值。我们会对使用此元素和所有较大元素的所有组合感兴趣,因为当p>=k/2和s>=k/2时,p+s>=k。数组已排序,因此可以使用经过一些修改的二进制搜索。此步骤将需要O(日志n)时间
  • 所有小于k/2+元素大于或等于“镜像元素”(根据中位数k/2)的元素也会引起我们的兴趣,因为当p=k/2-t和s>=k/2+t时,p+s>=k。这里我们需要通过小于k/2的元素循环,找到它们的镜像元素(二进制搜索)。如果镜像元素大于最后一个数组,则应停止循环
  • 例如,我们有数组{1,3,5,8,11}和k=10,所以在第一步我们将有k/2=5和对{5,7},{8,11},{8,11}。这些对的计数将通过公式l*(l-1)/2计算,其中l=元素计数>=k/2。在我们的例子中,l=3,因此计数=3*2/2=3

    在第二步中,3号镜像元素将是7(5-2=3和5+2=7),因此对{3,8}和{3,11}将感兴趣。因为1号镜像将是9(5-4=1和5+4=9),所以{1,11}是我们要寻找的

    所以,如果k/2<第一个数组元素,这个算法将是O(logn)


    对于负数,算法将稍微复杂一点,但也可以用相同的复杂度来求解。

    可能会有所帮助。在处理之前,您可能会混淆复杂度O(f(n))中的n你可以考虑重命名这个参数。我认为复杂性取决于参数,让我们把它命名为K。当数组被排序时,当你添加<代码>第一个/代码>和<代码>最后< /代码>,结果太大,哪个迭代器要移动,如果它低,该移动哪个?@ Pura-这不是我所说的。ant.我需要总和>=k的对(不仅仅是总和=k)“我试图找到数组中总和等于n的所有对”,这两个对都与标题相矛盾,并且可能在(预期的)线性时间内。如果
    a[left]+a[right]
    然后将
    向右移动
    靠近左侧,仍然会得到比
    n
    @NathanOliver-更小的结果
    vector<int> a; // the given array
    int r = a.size() - 1; 
    for (int l=0; l<a.size(); l++) {
        while ((r >= 0) && (a[r] >= k - a[l]))
            r--;
        // now r is the maximal position in a so that a[r] < k - a[l]
        // so all elements right to r form a needed pair with a[l]
        ans += a.size() - r - 1; // this is how many pairs we have starting at l
    }
    
    void count(int A[], int n) //n being the number of terms in array
    { int i, j, k, count = 0;
      cin>>k;
    
      for(i = 0; i<n; i++)
       for(j = 0; j<n; j++)
         if(A[i] + A[j] >= k)
          count++ ;
    
      cout<<"There are "<<count<<" such numbers" ;
    }