5

Given an array of integers, write a method which returns all unique pairs which add up to 100.

Example data:

sample_data = [0, 1, 100, 99, 0, 10, 90, 30, 55, 33, 55, 75, 50, 51, 49, 50, 51, 49, 51]
sample_output = [[1,99], [0,100], [10,90], [51,49], [50,50]]

I was solving this problem this weekend and while my solution seems scalable and efficient, I wanted to determine what the worst case time complexity of my solution is?

Here's my solution:

def solution(arr)
  res = []
  h = Hash.new

  # this seems to be O(N)
  arr.each do |elem|
    h[elem] = true
  end

  # how do I determine what Time complexity of this could be?
  arr.each do |elem|
    if h[100-elem]
      h[100-elem] = false
      h[elem] = false
      res << [elem, 100-elem]
    end
  end
  res 
end

If both the loops are O(N) each, and I add them up: O(N + N), this would equal O(2N) and taking the 2 to be a constant, can I assume my solution is O(N) ?

Dan Grahn
  • 9,044
  • 4
  • 37
  • 74
Jasdeep Singh
  • 3,276
  • 4
  • 28
  • 47
  • 3
    That would be correct. – Dan Grahn Dec 16 '13 at 16:11
  • I think your assumptions are basically correct. That is also assuming that elements can be negative (and over 100) for this to be meaningful - otherwise only de-duplicating the initial input has any scaling cost and everything else could be treated as fixed cost once you filled up all keys 0..100. Technically `h[elem] = true` is not `O(1)` (which a lot of people seem to assume) but `O(log(N))` so your overall complexity is probably `O(Nlog(N))` worst case - you 'd only see that if you pumped in arrays with millions of integers though – Neil Slater Dec 16 '13 at 16:14
  • 1
    @NeilSlater You are incorrect. `h` is a hash map which search is linear time. [Wiki](http://en.wikipedia.org/wiki/Hash_table) – Dan Grahn Dec 16 '13 at 16:24
  • 1
    @screenmutt: I don't see that in benchmarking. e.g. `array = (0..10000000).map { |x| SecureRandom.random_number( 2000000000 ) - 1000000000 }; Benchmark.bm { |bm| h = Hash.new; bm.report(:five) { 100000.times {|i| h[ array[i] ] = true } }; h = Hash.new; bm.report(:six) { 1000000.times {|i| h[ array[i] ] = true } } }` - in fact I see what I would expect for `O(Nlog(N))` - explain? – Neil Slater Dec 16 '13 at 16:36
  • @NeilSlater can you shed some more light on why `h[elem] = true` might be `O(log(N))` ? My assumptions and understanding have been that it's `O(1)` like you mentioned. – Jasdeep Singh Dec 16 '13 at 16:40
  • @NeilSlater Hash maps have an amortized constant insert time. [See this article](http://en.wikipedia.org/wiki/Hash_table). [Or this answer](http://stackoverflow.com/questions/3949217/time-complexity-of-hash-table) – Dan Grahn Dec 16 '13 at 16:42
  • @screenmutt: Thanks for the links. OK, I am incorrect, it is not `O(NlogN)` (although in practice the scaling-up costs when increasing the hash size may occur at geometric intervals, meaning it looks very close to `O(NlogN)` when measured). However, hitting a hash insert really hard in Ruby is not strictly `O(1)` either - it all depends what you want to consider in your "worst case" scenario, and how big an `N` you need your scaling formula to be accurate for. – Neil Slater Dec 16 '13 at 16:54
  • @NeilSlater That's true, generally amortized time is used when calculating Big-O. I've updated my answer to include true worse-case. – Dan Grahn Dec 16 '13 at 17:00
  • If you create the hash with a default value, `h = Hash.new(true)`, you can avoid the first loop. It doesn't alter the big-O complexity, but should still speed things up in practice. – pjs Dec 16 '13 at 17:08

2 Answers2

4

You are correct. Big-O of this code will be O(n) if you consider amortized runtime of hash search/insert.

If you take the true-worst case of hash search/insert (O(n)), then it will be O(n^2).

See Wikipedia on Hash Tables

Dan Grahn
  • 9,044
  • 4
  • 37
  • 74
0

The question may be asking about the time complexity of hash, but for the particular problem, that hash would better implemented as an array of bools indexed by the input 0..sum (100 in this case). That will have best, worst and average case constant time.

That approach has a simpler to compute complexity of O(N).

danh
  • 62,181
  • 10
  • 95
  • 136
  • Yes, that's another way of implementing the solution. I've thought of this approach too. – Jasdeep Singh Dec 16 '13 at 17:30
  • I think you'll find it quicker in practice (and a more certain calculation of complexity). The sparse array trades a little space (hardly any at low N) for speed. – danh Dec 16 '13 at 17:37