给定一个排序数组和一个参数 k,求出线性时间内大于或等于 k 的两个数字的总和

Given a sorted array and a parameter k, find the count of sum of two numbers greater than or equal to k in linear time

本文关键字:一个 大于 两个 数字 数组 排序 参数 线性 时间      更新时间:2023-10-16

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

  map<int,int> mymap;
  map<int,int>::iterator it;
  cin>>n>>k;
  for( int i = 0 ; i < n ; i++ ){
    cin>>a;
    
    if( mymap.find(a) != mymap.end() )
        mymap[a]++;
    else    
        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 );
        it->second = 0;
        
    }
    
 }
  cout<<cnt;
另一个在

最好的情况下取 O(log n) 和在最坏的情况下取 O(nlog n) 的正数可以这样完成:

    在数组中查找等于 k/2 的
  1. 元素,或者如果不存在,则查找大于 k/2 的最小值。所有与这个元素和所有更大的元素的组合都会对我们感兴趣,因为当 p>= k/2 和 s>=k/2 时,p + s>= k。 数组是排序的,因此可以使用带有一些修改的二进制搜索。此步骤将花费 O(log n) 时间。
  2. 所有小于 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(n)方法,使用所谓的"两个指针"或"两个迭代器"方法。关键思想是让两个迭代器(不一定C++迭代器,索引也可以)在同一个数组上运行,这样如果第一个迭代器指向值 x ,则第二个迭代器指向数组中小于 k-x 的最大元素。

我们将增加第一个迭代器,在执行此操作的同时,我们还将更改第二个迭代器以维护此属性。注意,随着第一个指针的增加,第二个指针的对应位置只会减小,所以每次迭代我们都可以从上一次迭代停止的位置开始;我们永远不需要增加第二个指针。这就是我们实现O(n)时间的方式。

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

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
}

另一种可能更容易编码但速度稍慢的方法是O(n log n)使用二进制搜索。对于数组的每个元素a[l],您可以找到最大位置r以便使用二叉搜索a[r]<k-a[l](这与第一种算法中的r相同)。

@Drew Dormann - 感谢您的评论。

使用两个指针运行数组。 leftright .
假设left是小边,从位置0left开始,然后right向左移动,直到最后一次a[left]+a[right] >= k
实现此目标后,total_count += (a.size - right + 1).
然后你向前移动left一步,right需要(也许)朝着它前进。重复此操作,直到它们相遇。

完成此操作后,假设他们在x地点见面,然后totla_count += choose(2, a.size - x).

  1. 对数组进行排序 (n log n)
  2. 对于 (i = 1 到 n)
    • 从根源开始
    • 如果 a[i] + curr_node>= k,则向左移动并匹配 = indexof(curr_nod)e
    • 否则,向右走
    • 如果 curr_node = 叶节点,则将 a[match] 之后的所有节点添加到带有 a[i] 的有效对列表中

步骤 2 也采用 O(n log n)。for 循环运行 n 次。在循环中,我们对每个节点执行二叉搜索.log即n个步骤。因此,该算法的整体复杂度为 O(n log n)。

这应该可以完成工作:

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" ;
} 
相关文章: