【问题标题】: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给定一个有序数组和一个参数 k,求两个大于或等于 k ​​的数之和在线性时间内的计数
【发布时间】:2015-06-17 13:22:27
【问题描述】:

我正在尝试在总和等于 k ​​的数组中查找所有对。我当前的解决方案需要 O(n*log(n)) 时间(下面的代码 sn-p)。谁能帮我找到更好的解决方案,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;

【问题讨论】:

  • This 可能会有所帮助。
  • 在处理之前,您可能会混淆复杂度 O(f(n)) 的 n 和给定的 n 参数。您可以考虑重命名参数。另外我认为复杂度取决于参数,我们将其命名为 k。
  • 由于数组是排序的,当你添加firstlast的时候结果太大了,移动哪个迭代器,如果小了移动哪个迭代器?
  • @PuRaK - 这不是我想要的。我需要总和 >= k 的对(不仅仅是 sum = k)
  • “我试图在数组中找到总和等于 n 的所有对”这两者都与标题相矛盾,并且在(预期的)线性时间内是可能的。

标签: c++ arrays algorithm sum


【解决方案1】:

另一种方法,在最好的情况下采用 O(log n),在最坏的情况下采用 O(nlog n) 的正数可以通过这种方式完成:

  1. 在数组中找到等于 k/2 的元素,或者如果它不存在,则找到大于 k/2 的最小值。我们会对这个元素和所有更大元素的所有组合感兴趣,因为当 p>= k/2 和 s>=k/2 时 p + s >= k。数组已排序,因此可以使用经过一些修改的二进制搜索。此步骤将花费 O(log n) 时间。
  2. 所有小于 k/2 + 的元素大于或等于“镜像元素”(根据中值 k/2)的所有元素也会对我们感兴趣,因为当 p=k/2- 时 p + s >= k t 和 s>= k/2+t。在这里,我们需要遍历小于 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,所以 count = 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} 就是我们要寻找的。​​p>

所以,如果 k/2

对于负数,算法会稍微复杂一些,但也可以以相同的复杂度求解。

【讨论】:

    【解决方案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]&lt;k-a[l](这与第一个算法中的r 相同)。

    【讨论】:

    • 题目要求数组的两个数之和大于k,而不是数组元素与k之和。我认为条件应该是 (a[r]+a[l]
    • @gear14195,哦,是的,我的错。将修复答案。
    • @gear14195,如果你的意思是 while 条件,那么我认为我的是正确的。 r 从右到左,所以它应该随着a[l]+a[r]&gt;=k 减少,这是我的代码中写的。
    • 明白。不过,您可能想在 while 条件中添加缺少的括号:)
    • @gear14195,不,这就是重点!该数组已排序,因此在增加l 的同时,相应的r 只会减少。如果我每次都重新初始化它,它会是O(N^2),但是利用r只会减少的事实,我们把它变成O(N)
    【解决方案3】:

    @Drew Dormann - 感谢您的评论。

    用两个指针遍历数组。 leftright.
    假设left 是小边,从left 位置0 开始,然后right 向左移动直到最后一次a[left]+a[right] &gt;= k
    当这实现时,然后total_count += (a.size - right + 1).
    然后您将left 向前移动一步,right 需要(可能)朝它移动。重复这个直到他们相遇。

    完成后,假设他们在x 的位置相遇,然后是totla_count += choose(2, a.size - x)

    【讨论】:

    • 如果a[left]+a[right] &lt; n 则将right 向左移动仍然会给您带来小于n 的结果。
    • @NathanOliver - a[left]+a[right] 大于 n。当left 向右移动一步时,right 可能会向左移动而仍然保持a[left]+a[right] &gt;= n
    【解决方案4】:
    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)。

    【讨论】:

    • 数组已经排序。你想告诉我造一棵树吗?
    • 您不必这样做。 (虽然你可以)。你可以做的是让内部循环从 count = 0 运行到 log n 并且在每一步你都会根据满足的条件执行 i + 2^count 或 i - 2^count。
    【解决方案5】:

    这应该可以完成工作:

    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" ;
    } 
    

    【讨论】:

    • 这是一个n平方的方法;一种蛮力的方法。它可以更有效地完成。
    • 她已经提到她需要一种比蛮力更好的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 1970-01-01
    • 2020-04-17
    • 2018-07-31
    相关资源
    最近更新 更多