3Sum算法问题O(N ^ 2 logn)

时间:2017-03-09 20:51:53

标签: ruby algorithm binary-search

使用以下算法:

  

给定n个整数nums和target的数组,找到数字   索引三元组i,j,k,其中0 <= i <1。 j&lt; k&lt;满足的   条件nums [i] + nums [j] + nums [k]&lt;目标

     

例如,给定nums = [-2,0,1,3]和target = 2.

     

返回2.因为有两个三元组,其总和小于2:

def three_sum_smaller(nums, target)
    nums.sort!
    i = 0 
    triplet_count = 0
    while i < nums.length
        j = i + 1
        while j < nums.length
            pair_sum = nums[i] + nums[j]
            val = target - pair_sum
            k = binary_search(nums, val) #if val in it, we want all indices between j and k. else all indices j and k including k.
            (nums[k] == val) ? triplet_count += (j + 1...k).size : triplet_count += (j + 1..k).size #ensures k > j.
            j += 1
        end 
        i += 1
    end

    triplet_count            
end

def binary_search(arr, val)
  b_search(arr, val, 0, arr.length - 1)
end

def b_search(arr, val, low, high)
  while low < high 
    mid = (low + high) / 2
    if arr[mid] > val #left half 
      high = mid - 1
    elsif arr[mid] < val 
      low = mid + 1 
    else 
      #go left until it's no longer k and return this.
      until arr[mid] != val 
        mid -= 1 
      end 
      return mid 
    end 
  end 

  low
end

对于以下测试用例,我的代码为1:

arr = [-3,4,-4,1,-1,-2,-1,-1,-5]
target = -3

Correct output: 48
My output: 59

该方法的工作原理如下。对于每个可能的对,执行二进制搜索,查找使得对的总和==目标的值。如果此值不存在,则返回小于它的索引。如果是,则返回该值的第一个匹配项。然后,如果找到该值,则仅将j和k之间的指数(excl k)作为三元组完成。如果未找到该值,则将索引包含j作为三元组完成。

这适用于200个左右的测试用例,之后失败了。我不确定出了什么问题......

2 个答案:

答案 0 :(得分:1)

愚蠢的问题:鉴于200个测试用例通过,但有一个失败,您确定给出的测试输出是否正确?

从另一个角度来看:我对这个算法的解释(如果我误解了,那就道歉了!)就是你要找到产生总和少的三元组的总数比目标。三元组不能重复使用相同的数字,否则可以按任何顺序重复。

碰巧,Ruby使用validator生成长度为 n 的组合非常容易(https://ruby-doc.org/core-2.2.0/Array.html#method-i-combination

使用它,我们可以生成所有独特的三元组。然后,对每一个进行求和是微不足道的,看它是否符合标准。

因此,沿着这些方向的替代解决方案可能如下所示:

validator(blacklist: list=['heck', 'muffins']) -> ???:

根据您提供的输入,这支持您对48的回答:

combination

答案 1 :(得分:1)

问题在于在将范围添加到triplet_count之前进行等式检查。您需要检查>=而不是==,如下所示:

def three_sum_smaller(nums, target)
  nums.sort!
  i = 0
  triplet_count = 0
  while i < nums.length
    j = i + 1
    while j < nums.length
      pair_sum = nums[i] + nums[j]
      val = target - pair_sum
      k = binary_search(nums, val)
      (nums[k] >= val) ? triplet_count += ((j + 1)...k).size : triplet_count += ((j + 1)..k).size
      j += 1
    end
    i += 1
  end

  triplet_count
end

当您检查相等性时,您错过了位置k上的数字可能大于val的事实,因此它不代表有效的三元组。

无论如何,如果我们使用以下想法,解决方案可以降低到O(N ^ 2):

我们可以修复索引(idx),然后使用两个指针(startfinish)迭代搜索从idx + 1nums.length - 1的子数组找到有效范围以完成三元组。代码如下所示:

def three_sum_smaller_without_search(nums, target)
  nums.sort!
  triplet_count = 0
  nums.each_with_index do |val, idx|
    start = idx + 1
    finish = nums.length - 1

    while start < finish
      if val + nums[start] + nums[finish] >= target
        finish -= 1
      elsif val + nums[start] + nums[finish] < target
        triplet_count += finish - start
        start += 1
      end
    end
  end

  triplet_count
end