今天,我得到了一个具有给定数组和'target'的任务,该任务是该列表中2个整数的总和。一段时间后,我提出了解决方案草案,但似乎并没有通过所有测试。算法似乎在[0]处考虑了两次整数。
def two_sum(numbers, target)
numbers.combination 2 do |a, b|
if a + b == target
return numbers.index(a), numbers.index(b)
end
end
end
print two_sum([1, 2, 3], 4) # Expected [0, 2] *OK
print two_sum([1234, 5678, 9012], 14690) # Expected [1, 2] *OK
print two_sum([2, 2, 3], 4) # Expected [0, 1]) but I get [0, 0]
我尝试先使用.map而不是.combination(2)方法,但结果完全相同:-/
答案 0 :(得分:5)
def two_sum(numbers, target)
[*0..numbers.size-1].combination(2).find { |i,j| numbers[i] + numbers[j] == target }
end
two_sum([1234, 5678, 9012], 14690)
#=> [1, 2]
two_sum([1234, 5678, 9012], 14691)
#=> nil
这是一种更有效的方法,当数组很大时,可能会有用。
require 'set'
def two_sum(arr, target)
if target.even?
half = target/2
first = arr.index(half)
if first
last = arr.rindex(half)
return [first, last] unless last.nil? || first == last
end
end
a1, a2 = arr.uniq.partition { |n| n <= target/2 }
s = a2.to_set
n = a1.find { |n| s.include?(target-n) }
n.nil? ? nil : [arr.index(n), arr.index(target-n)]
end
如果target
甚至是arr
,我首先检查一下是否有一半至少出现在target
中两次。如果是这样,我们就完成了(确定和返回关联的索引除外)。即使该方法在此步骤之后没有终止,也要求该步骤不会导致在执行下一步之前需要提前终止。
如果arr
为奇数或偶数,但其中一半却在arr
中出现少于两次,则构造一个临时数组,该数组在a1
中包含唯一值,然后将其分区为两个数组target/2
的值不大于a2
,而数组target/2
的值不大于target
。因此,如果两个数字之和等于a1
,则一个数字必须位于a2
中,而另一个数字必须位于a2
中。
为了加快计算速度,我随后将s
转换为集合a1
,然后遍历n
寻找值s
,以使target-n
包含{ {1}}。试试吧。
arr = 100_000.times.map { rand(1_000_000) }
puts "target i1 arr[i1] i2 arr[i2] calc time (secs)"
puts "---------------------------------------------------------"
1000.times do
t = Time.now
target = rand(1_000_000)
i1, i2 = two_sum(arr, target)
print "#{target} -> "
print i1.nil? ? "nil " :
"#{i1} #{arr[i1]} #{i2} #{arr[i2]}"
puts " #{(Time.now-t).round(4)} secs"
end
打印
target i1 arr[i1] i2 arr[i2] calc time (secs)
---------------------------------------------------------
215113 -> 41 90943 11198 124170 0.027
344479 -> 0 78758 63570 265721 0.0237
188352 -> 190 79209 39912 109143 0.0275
457 -> nil 0.0255
923135 -> 78 84600 43928 838535 0.0207
59391 -> 2 5779 5454 53612 0.0289
259142 -> 73 58864 29278 200278 0.0284
364486 -> 8049 182243 89704 182243 0.001
895164 -> 13 205843 7705 689321 0.0228
880575 -> 20 440073 6195 440502 0.021
我们看到arr
不包含两个等于457
的数字。另外,请注意倒数第二行中的时间很短。这是因为target
(364486/2 #=> 182243
)的一小部分在arr
中至少出现了两次。