Ruby works great for LeetCode. The syntax stays out of your way. Built-in methods handle the heavy lifting. Here is what I have learned grinding through problems.
Why Ruby Shines for Algorithm Practice
Ruby's Enumerable module is your best friend. Methods like each_with_index, select, map, and reduce replace boilerplate loops. Hash lookups run in O(1). Arrays behave like stacks out of the box.
The tradeoff? Ruby runs slower than C++ or Java. LeetCode's time limits usually accommodate this. Focus on getting the algorithm right first.
Pattern 1: Hash Maps for O(1) Lookups
The classic Two Sum problem shows why hashes matter.
Problem: Find two numbers in an array that add up to a target. Return their indices.
def two_sum(nums, target)
seen = {}
nums.each_with_index do |num, i|
complement = target - num
return [seen[complement], i] if seen.key?(complement)
seen[num] = i
end
end
two_sum([2, 7, 11, 15], 9) # => [0, 1]The trick: store each number as you go. Check if its complement exists. One pass, O(n) time.
Use key? instead of checking for nil. It handles edge cases where the value itself is nil.
Pattern 2: Stacks for Matching Problems
Parentheses validation comes up everywhere. Stacks make it simple.
Problem: Check if brackets are balanced and properly nested.
def valid_parentheses?(s)
stack = []
pairs = { ')' => '(', '}' => '{', ']' => '[' }
s.each_char do |char|
if pairs.key?(char)
return false if stack.pop != pairs[char]
else
stack.push(char)
end
end
stack.empty?
end
valid_parentheses?("()[]{}") # => true
valid_parentheses?("(]") # => falseRuby arrays work as stacks. push and pop do what you expect. No need for a separate Stack class.
The hash maps closing brackets to openers. Clean and readable.
Ruby Tricks That Save Time
Guard clauses with trailing conditionals:
return [] if nums.empty?
return [0] if nums.length == 1Destructuring in blocks:
hash.each { |key, value| puts "#{key}: #{value}" }Default hash values:
counter = Hash.new(0)
nums.each { |n| counter[n] += 1 }Ranges for slicing:
nums[1..-1] # everything except first
nums[0...-1] # everything except lastCommon Gotchas
Integer division: Ruby 2.x divides integers correctly. 7 / 2 gives 3, not 3.5. Use 7.0 / 2 or 7.fdiv(2) when you need floats.
Mutable default arguments: Never use mutable objects as defaults.
# Bad
def add_item(item, list = [])
list << item
end
# Good
def add_item(item, list = nil)
list ||= []
list << item
endOff-by-one errors: Ruby's times, upto, and downto help avoid these.
5.times { |i| puts i } # 0, 1, 2, 3, 4
1.upto(5) { |i| puts i } # 1, 2, 3, 4, 5When Ruby Falls Short
Some problems need raw speed. Bit manipulation problems sometimes hit time limits. Heavy recursion can stack overflow earlier than in other languages.
For those cases, consider translating your working Ruby solution to Python or Java. Getting the logic right matters more than the language.
Quick Reference
| Pattern | Ruby Method |
|---|---|
| Frequency count | Hash.new(0) |
| Find duplicates | array.tally |
| Sort by custom key | `array.sort_by { |
| Group elements | `array.group_by { |
| First n elements | array.take(n) |
| All match condition | `array.all? { |
| Any match condition | `array.any? { |
Ruby makes algorithm practice enjoyable. The code reads like pseudocode. That clarity helps you focus on the actual problem-solving.
Start with easy problems. Build your pattern recognition. The language will feel natural within a week.