ruby 中最小未占用椅子数的解决方案

The Number of the Smallest Unoccupied Chair solution in ruby

我正在学习ruby并开始练习leetcode的问题,昨天我有一个问题,从昨天开始我无法解决

我在 ruby 中努力做到了,但还做不到。

我试过了

def give_chair(a)
    u = a.uniq
    d = []
    u.each do |i|
        d << i if a.count(i) == 1
    end
    d
end

def smallest_chair(times, target_friend)
  friend = times[target_friend]
  sorted_arrival_times = times.sort
  leave_time_chair = {}
  chair = 0
  chairs_array = []
  uniq_chars_array = []
  sorted_arrival_times.each do |i|
    if leave_time_chair.keys.select { |k| i[0] > k }.empty?
      leave_time_chair[i[1]] = chair
      chair+=1
    else
      all_keys = leave_time_chair.keys.select { |k|  k <= i[0] }
      chairs_array = leave_time_chair.values
      p chairs_array
      if give_chair(chairs_array).empty?
        leave_time_chair[i[1]] = chairs_array.sort.first
      else
        leave_time_chair[i[1]] = give_chair(chairs_array).sort.first
      end
    end
    if i == friend
      p leave_time_chair
      return leave_time_chair[i[1]]
    end
  end
end

# a = [[33889,98676],[80071,89737],[44118,52565],[52992,84310],[78492,88209],[21695,67063],[84622,95452],[98048,98856],[98411,99433],[55333,56548],[65375,88566],[55011,62821],[48548,48656],[87396,94825],[55273,81868],[75629,91467]]
# b = 6
# p smallest_chair(a, b)

但某些测试用例失败了。

我无法为其创建算法。

问题=https://leetcode.com/problems/the-number-of-the-smallest-unoccupied-chair

我的做法:

  1. 首先我根据到达时间对时间数组进行排序。

  2. 然后我遍历每个数组元素

  3. 现在,如果到达时间大于所有之前的离开时间(我正在创建离开时间和给定椅子的键值对),那么我在 leave_time_chair (这是哈希),其中键是当前数组的离开时间,值是给它的椅子。

  4. 然后我增加椅子(椅子+=1)

  5. 否则我得到所有等于或小于当前到达时间的离开时间(all_keys = leave_time_chair.keys.select { |k| k <= i[0] })

  6. 那时候的椅子我都搞定了

  7. 现在我有所有这样的椅子 => [0, 0, 1, 2] 所以我写了一个函数 [ give_chair(a) ] 它给了我那些元素不再重复。像这样 => [1, 2] 然后我将最短的数字(椅子)分配给当前数组的离开时间。等等...

  8. 那如果我的当前数组等于我return的朋友的椅子呢。通过从哈希中提取它 (leave_time_chair) return leave_time_chair[i[1]]

我天真的解决方案(尚未优化),基本上我的想法是我将输入数组平面映射到一个数组中,每个元素都是一对 [time arrive/leave, friend index],然后我将根据时间对该数组进行排序(不在乎到达或离开),如果两对时间相同,那么我将比较 fiend 索引的到达时间。最后,我循环遍历排序数组并评估 minimum free chair index 每一步,每当我遇到 targetFriend 我 return 即 minimum free chair index.

# @param {Integer[][]} times
# @param {Integer} target_friend
# @return {Integer}
def smallest_chair(times, target_friend)
  # times = [[1,2],[4,7],[2,4]]
  # targetFriend = 1

  sit_times = times.each_with_index.inject([]) { |combi, (time, index)| 
    combi += [[time.first, index], [time.last, index]]
  }
  # [[1, 0], [2, 0], [4, 1], [7, 1], [2, 2], [4, 2]]

  sit_times.sort! {|x, y| 
    c = x[0] <=> y[0]
    # [[1, 0], [2, 0], [2, 2], [4, 1], [4, 2], [7, 1]]
    c = times[x[1]][0] <=> times[y[1]][0] if c == 0
    # [[1, 0], [2, 0], [2, 2], [4, 2], [4, 1], [7, 1]]
    c
  }

  chairs = {} # to mark time of friend
  occupied = Array.new(times.size, 0) # occupied chair: 1, otherwise: 0
  min_free = 0 # current minimum not occupied chair
  sit_times.each do |time, friend_index|
    if target_friend == friend_index # check
      return min_free
    end

    sit = chairs[friend_index]
    if sit # leave
      occupied[sit] = 0
      chairs[friend_index] = nil
      min_free = sit if min_free > sit
    else # arrive
      chairs[friend_index] = min_free
      occupied[min_free] = 1
      min_free += 1 until occupied[min_free] == 0 # re-calculate
    end
  end
end

注意:代码通过leetcode测试用例,但性能不佳

更新 这是更好的版本,使用 3 个优先级队列,一个用于到达时间,一个用于离开时间,最后一个用于主席。

优先队列class

class PriorityQueue
  attr_reader :length

  def initialize(opts={}, &comparator)
    order_opt = opts.fetch(:order, :asc)
    @order = order_opt == :asc ? -1 : 1
    @comparator = comparator
    @items = [nil]
    @length = 0
  end

  def push(item)
    @items << item
    @length += 1
    swim(@length)
    true
  end

  def pop
    return nil if empty?
    swap(1, @length) if @length > 1
    @length -= 1
    sink(1) if @length > 0
    @items.pop
  end

  def empty?
    @length == 0
  end

  def swap(i, j)
    temp =  @items[i]
    @items[i] = @items[j]
    @items[j] = temp
  end

  def in_order?(i, j)
    x = @items[i]
    y = @items[j]
    order = @comparator.nil? ? (x <=> y) : @comparator.call(x, y)
    order == @order
  end

  def swim(from)
    while (up = from / 2) >= 1
      break if in_order?(up, from)
      swap(up, from)
      from = up
    end
  end

  def sink(from)
    while (down = from * 2) <= @length
      down += 1 if down < @length && in_order?(down + 1, down)
      break if in_order?(from, down)
      swap(down, from)
      from = down
    end
  end
end

smallest_chair 优先级队列(注意我发现使用 sort 比到达时间队列更快,但基本上想法是一样的)

def smallest_chair_pq(times, target_friend)
  # a_pq = PriorityQueue.new { |x, y|
  #   x[0] <=> y[0]
  # }
  #
  # times.each do |t|
  #   a_pq.push(t)
  # end

  # sort arrive times is faster than a priority queue
  a_pq = times.sort_by(&:first).reverse

  # leave times queue
  l_pq = PriorityQueue.new { |x, y|
    c = x[0] <=> y[0]
    c = x[1] <=> y[1] if c == 0
    c
  }
  # chair-indexes queue
  # consider case a friend come in at arrive-time at1
  # and there's a range chairs with leave times in range lm <= at1 <= ln
  # that mean that friend could pick one of those chairs
  # and according this problem requirement, should pick the minimun chair index
  c_pq = PriorityQueue.new

  target_time = times[target_friend][0]
  last_chair_index = 0
  until a_pq.empty?
    a_top = a_pq.pop
    arrive_time = a_top.first
    if l_pq.empty?
      return 0 if arrive_time == target_time
      l_pq.push([a_top.last, 0])
    else
      l_top = l_pq.pop
      if l_top.first <= arrive_time
        c_pq.push(l_top.last)
        until (l_ntop = l_pq.pop).nil? || arrive_time < l_ntop.first
          c_pq.push(l_ntop.last)
        end
        l_pq.push(l_ntop) unless l_ntop.nil?

        min_chair_index = c_pq.pop
        return min_chair_index if arrive_time == target_time
        l_pq.push([a_top.last, min_chair_index])
      else
        unless c_pq.empty?
          chair_index = c_pq.pop
          return chair_index if arrive_time == target_time
          l_pq.push([a_top.last, chair_index])
        else
          last_chair_index += 1
          return last_chair_index if arrive_time == target_time
          l_pq.push([a_top.last, last_chair_index])
        end
        l_pq.push(l_top)
      end
    end
  end
end