├── README.md ├── decode_ways.rb ├── move_zeroes.rb └── src ├── 4-sum.rb ├── add_binary.rb ├── balanced_parentheses.rb ├── bigger_num_with_same_digits.rb ├── binary_search.rb ├── binary_tree_side_view.rb ├── binary_tree_tamplate.rb ├── brackets_combinations.rb ├── coin-change.rb ├── count_and_say.rb ├── counting_sort.rb ├── decode_string.rb ├── dijkstra.rb ├── facebook-interview-tips.md ├── game_of_life.rb ├── gcd.rb ├── google-interview-tips.md ├── house-robber.rb ├── insertion_sort.rb ├── join_sets.rb ├── kmp.rb ├── knapsack.rb ├── knapsack2.rb ├── kruskal.rb ├── linkedin-interview.md ├── longest_common_subsequence.rb ├── longest_increasing_subsequence.rb ├── longest_increasing_subsequence_nlogn.rb ├── max_stack.rb ├── max_subarray.rb ├── merge_sort.rb ├── min_insertions_for_palindrome.rb ├── mirror.rb ├── monty_hall.rb ├── nested_list_weight_sum_ii.rb ├── permutations.rb ├── phone.rb ├── powerset.rb ├── product-of-array.rb ├── quicksort.rb ├── reverse-linked-list.rb ├── reverse-string-inplace.rb ├── rmq.rb ├── shell_sort.rb ├── simlify_path.rb ├── sq_root.rb ├── stocks.rb ├── suffix_trees.txt ├── sugget_index_in_array.rb ├── target_sum.rb ├── tarjan.rb ├── top-k-elements.rb ├── towers_of_hanoi_with_stack.rb ├── unique_bsts.rb └── wiggle_subsequence.rb /README.md: -------------------------------------------------------------------------------- 1 | Algorithms 2 | ========== 3 | 4 | Algorithms playground for common questions solved in ruby syntax. 5 | In case you want to prepare yourself for a job interview - try to solve it yourself first, then have a look here. 6 | 7 | # Why? 8 | I interviewed with [Google](https://github.com/sagivo/algorithms/blob/master/src/google-interview-tips.md), [Facebook](https://github.com/sagivo/algorithms/blob/master/src/facebook-interview-tips.md), [LinkedIn](https://github.com/sagivo/algorithms/blob/master/src/linkedin-interview.md), Twitter and others. I also interviewed others myself. 9 | Sometimes it looks like they all ask you the same "out of the box" questions that don't really check knowledge but memorization of the same tricks. 10 | This is my way of saying - change your interview style. There are lots of smart people out there, this is not the best way to find them. 11 | 12 | # Problems 13 | 14 | | problem | solution | 15 | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 16 | | [Unique binary search trees](https://leetcode.com/problems/unique-binary-search-trees/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/unique_bsts.rb) | 17 | | [House Robber](https://leetcode.com/problems/house-robber/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/house-robber.rb) | 18 | | [Decode Ways](https://leetcode.com/problems/decode-ways) | [click](https://github.com/sagivo/algorithms/blob/master/src/decode_ways.rb) | 19 | | [Coin change](https://leetcode.com/problems/coin-change/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/coin-change.rb) | 20 | | [Decode Strings](https://leetcode.com/problems/decode-string/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/decode_string.rb) | 21 | | [Wiggle Subsequence](https://leetcode.com/problems/wiggle-subsequence/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/wiggle_subsequence.rb) | 22 | | [Binary Tree Side View](https://leetcode.com/problems/binary-tree-right-side-view/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/binary_tree_side_view.rb) | 23 | | [Unix path](https://leetcode.com/problems/simplify-path/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/simplify_path.rb) | 24 | | [Array Product](https://leetcode.com/problems/product-of-array-except-self/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/product-of-array.rb) | 25 | | [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/top-k-elements.rb) | 26 | | [Sum 4 arrays](https://leetcode.com/problems/4sum-ii/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/4-sum.rb) | 27 | | [Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/reverse-linked-list.rb) | 28 | | [Reverse String In-place](https://leetcode.com/problems/reverse-string/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/reverse-string-inplace.rb) | 29 | | [Nested List Weight Sum II](https://leetcode.com/problems/nested-list-weight-sum-ii/) | [click](https://github.com/sagivo/algorithms/blob/master/src/nested_list_weight_sum_ii.rb) | 30 | | [Symmetric Tree](https://leetcode.com/problems/symmetric-tree/) | [click](https://github.com/sagivo/algorithms/blob/master/src/mirror.rb) | 31 | | [Dijkstra's shortest path between two nodes](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) | [click](https://github.com/sagivo/algorithms/blob/master/src/dijkstra.rb) | 32 | | [Kruskal's minimum spanning tree algorithm](http://en.wikipedia.org/wiki/Kruskal%27s_algorithm) | [click](https://github.com/sagivo/algorithms/blob/master/src/kruskal.rb) | 33 | | [Find the square root of a number](https://en.wikipedia.org/wiki/Newton%27s_method) | [click](https://github.com/sagivo/algorithms/blob/master/src/sq_root.rb) | 34 | | [Add two binary numbers](https://leetcode.com/problems/add-binary/description/) | [click](https://github.com/sagivo/algorithms/blob/master/src/add_binary.rb) | 35 | | [Binary search](https://en.wikipedia.org/wiki/Binary_search_algorithm) | [click](https://github.com/sagivo/algorithms/blob/master/src/binary_search.rb) | 36 | | [Longest increasing subsequence](http://en.wikipedia.org/wiki/Longest_increasing_subsequence) | [click](https://github.com/sagivo/algorithms/blob/master/src/longest_increasing_subsequence.rb) | 37 | | [Find all permutations of array](https://en.wikipedia.org/wiki/Permutation) | [click](https://github.com/sagivo/algorithms/blob/master/src/permutations.rb) | 38 | | [Finding all combinations of well-formed brackets](http://stackoverflow.com/questions/727707/finding-all-combinations-of-well-formed-brackets) | [click](https://github.com/sagivo/algorithms/blob/master/src/brackets_combinations.rb) | 39 | | [Finding the powerset of a set](http://en.wikipedia.org/wiki/Power_set) | [click](https://github.com/sagivo/algorithms/blob/master/src/powerset.rb) | 40 | | [Game of life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) | [click](https://github.com/sagivo/algorithms/blob/master/src/game_of_life.rb) | 41 | | Print all combinations of set joining | [click](https://github.com/sagivo/algorithms/blob/master/src/join_sets.rb) | 42 | | [count and say](https://leetcode.com/problems/count-and-say/) | [click](https://github.com/sagivo/algorithms/blob/master/src/count_and_say.rb) | 43 | | [Telephone number to words](http://www.mobilefish.com/services/phonenumber_words/phonenumber_words.php) | [click](https://github.com/sagivo/algorithms/blob/master/src/phone.rb) | 44 | | [Maximum contiguous subarray](https://leetcode.com/problems/maximum-subarray) | [click](https://github.com/sagivo/algorithms/blob/master/src/max_subarray.rb) | 45 | | [Max Stack](https://leetcode.com/problems/max-stack/submissions/) | [click](https://github.com/sagivo/algorithms/blob/master/src/max_stack.rb) | 46 | | [Find the smallest biggest number that has the same digits](http://stackoverflow.com/questions/9368205/given-a-number-find-the-next-higher-number-which-has-the-exact-same-set-of-digi) | [click](https://github.com/sagivo/algorithms/blob/master/src/bigger_num_with_same_digits.rb) | 47 | | [Find the minimum insertions needed to make a word palindrome](http://www.geeksforgeeks.org/dynamic-programming-set-28-minimum-insertions-to-form-a-palindrome/) | [click](https://github.com/sagivo/algorithms/blob/master/src/min_insertions_for_palindrome.rb) | 48 | | [String matching - Knuth Morris Pratt algorithm KMP](http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) | [click](https://github.com/sagivo/algorithms/blob/master/src/kmp.rb) | 49 | | [Balanced Parentheses](http://stackoverflow.com/questions/14930073/how-to-check-if-a-string-is-balanced) | [click](https://github.com/sagivo/algorithms/blob/master/src/balanced_parentheses.rb) | 50 | | [Quicksort algorithm](http://en.wikipedia.org/wiki/Quicksort) | [click](https://github.com/sagivo/algorithms/blob/master/src/quicksort.rb) | 51 | | [Mergesort algorithm](https://en.wikipedia.org/wiki/Merge_sort) | [click](https://github.com/sagivo/algorithms/blob/master/src/merge_sort.rb) | 52 | | [Max Stocks Profit](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/) | [click](https://github.com/sagivo/algorithms/blob/master/src/stocks.rb) | 53 | | [Countingsort algorithm](http://en.wikipedia.org/wiki/Counting_sort) | [click](https://github.com/sagivo/algorithms/blob/master/src/counting_sort.rb) | 54 | | [Shellsort algorithm](http://en.wikipedia.org/wiki/Shellsort) | [click](https://github.com/sagivo/algorithms/blob/master/src/shell_sort.rb) | 55 | | [Knapsack problem](http://en.wikipedia.org/wiki/Knapsack_problem) | [click](https://github.com/sagivo/algorithms/blob/master/src/knapsack.rb), [click](https://github.com/sagivo/algorithms/blob/master/src/knapsack2.rb) | 56 | | [Move Zeroes](https://leetcode.com/problems/move-zeroes) | [click](https://github.com/sagivo/algorithms/blob/master/src/move_zeroes.rb) | 57 | | [Longest common subsequence problem](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) | [click](https://github.com/sagivo/algorithms/blob/master/src/longest_common_subsequence.rb) , [click](https://github.com/sagivo/algorithms/blob/master/src/longest_increasing_subsequence.rb) | 58 | | [Monty Hall Problem](https://en.wikipedia.org/wiki/Monty_hall_problem) | [click](https://github.com/sagivo/algorithms/blob/master/src/monty_hall.rb) | 59 | | [Eucliden and Extended Eucliden algorithm](http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm) | [click](https://github.com/sagivo/algorithms/blob/master/src/gcd.rb) | 60 | | Suggest index of a number in an array | [click](https://github.com/sagivo/algorithms/blob/master/src/sugget_index_in_array.rb) | 61 | | [Range minimum query sparse table algorithm](http://en.wikipedia.org/wiki/Range_minimum_query) | [click](https://github.com/sagivo/algorithms/blob/master/src/rmq.rb) | 62 | | [Insertion Sort](https://en.wikipedia.org/wiki/Insertion_sort) | [click](https://github.com/sagivo/algorithms/blob/master/src/insertion_sort.rb) | 63 | | [Towers of Hanoi using Stack](https://en.wikipedia.org/wiki/Tower_of_Hanoi) | [click](blob/master/src/towers_of_hanoi_with_stack.rb) 64 | | [Tarjan's strongly connected components finder](https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm) | [click](https://github.com/sagivo/algorithms/blob/master/src/tarjan.rb) | 65 | 66 | 67 | # How? 68 | Simply run `ruby some_file.rb` to execute the algorithm. At the bottom of each file there are some test samples. 69 | example: `ruby brackets_combinations.rb` will print: 70 | `["((()))", "(()())", "(())()", "()(())", "()()()"]`. 71 | 72 | # Contribute 73 | Did you find a bug? any way to do it better? please feel free to pull-request it :) 74 | -------------------------------------------------------------------------------- /decode_ways.rb: -------------------------------------------------------------------------------- 1 | # problem: https://leetcode.com/problems/decode-ways/description/ 2 | # @param {String} s 3 | # @return {Integer} 4 | def num_decodings(s) 5 | return 0 if s.empty? or s.start_with? '0' 6 | close = far = 1 7 | 8 | (2..s.size).each do |i| 9 | cur = 0 10 | cur = close if s[i-1] != '0' 11 | cur += far if s[i-2] == '1' or (s[i-2] == '2' and s[i-1].to_i <= 6) 12 | far = close 13 | close = cur 14 | end 15 | return close 16 | end -------------------------------------------------------------------------------- /move_zeroes.rb: -------------------------------------------------------------------------------- 1 | # problem: https://leetcode.com/problems/move-zeroes 2 | # @param {Integer[]} nums 3 | # @return {Void} Do not return anything, modify nums in-place instead. 4 | def move_zeroes(nums) 5 | i = 0 6 | nums.each do |n| 7 | if n != 0 8 | nums[i] = n 9 | i += 1 10 | end 11 | end 12 | (i...nums.size).each {|indx| nums[indx] = 0} 13 | return nums 14 | end 15 | 16 | p move_zeroes([0,1,0,3,12]) #=> [1,3,12,0,0] -------------------------------------------------------------------------------- /src/4-sum.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} a 2 | # @param {Integer[]} b 3 | # @param {Integer[]} c 4 | # @param {Integer[]} d 5 | # @return {Integer} 6 | 7 | def four_sum_count(a, b, c, d) 8 | return check_4_sum(a, b, c, d) 9 | end 10 | 11 | def create_set(a, b) 12 | h = {} 13 | for ai in a 14 | for bi in b 15 | h[ai + bi] ||= 0 16 | h[ai + bi] += 1 17 | end 18 | end 19 | return h 20 | end 21 | 22 | def check_4_sum(a, b, c, d) 23 | tot = 0 24 | a1 = create_set(a, b) 25 | a2 = create_set(c, d) 26 | a1.each do |k, v| 27 | if a2[-1 * k] 28 | tot += v * a2[-1 * k] 29 | end 30 | end 31 | p a1, a2 32 | return tot 33 | end 34 | 35 | p four_sum_count([-1,-1], [-1,1], [-1, 1], [1,-1]) -------------------------------------------------------------------------------- /src/add_binary.rb: -------------------------------------------------------------------------------- 1 | # problem: https://leetcode.com/problems/add-binary 2 | # @param {String} a 3 | # @param {String} b 4 | # @return {String} 5 | def add_binary(a, b) 6 | i = 0 7 | cerry = 0 8 | res = '' 9 | 10 | while (i < a.size) || (i < b.size) do 11 | a1 = i= 2 ? 1 : 0 16 | i+=1 17 | end 18 | res += '1' if cerry == 1 19 | 20 | return res.reverse 21 | end 22 | 23 | p add_binary('1010','1011') #=> '10101' -------------------------------------------------------------------------------- /src/balanced_parentheses.rb: -------------------------------------------------------------------------------- 1 | def balanced_parentheses?(str) 2 | stack = [] 3 | str.chars.each do |token| 4 | case token 5 | when '(' 6 | stack.push '(' 7 | when ')' 8 | if !stack.empty? 9 | stack.pop 10 | else 11 | return false 12 | end 13 | end 14 | end 15 | stack.empty? 16 | end 17 | 18 | #test 19 | p balanced_parentheses? '( () ( () ) () )' # => true 20 | p balanced_parentheses? '(()' # => false 21 | -------------------------------------------------------------------------------- /src/bigger_num_with_same_digits.rb: -------------------------------------------------------------------------------- 1 | def bigger_with_sam_digits(num) 2 | num = num.to_s.chars.map(&:to_i) #convert int to array of ints 3 | return num.join.to_i if num.size < 2 4 | for left in (num.size-2).downto(0) do 5 | for right in (num.size-1).downto(left+1) do 6 | if num[right]>num[left] 7 | num[left],num[right] = num[right],num[left] 8 | return (num[0..left] + num[left+1..num.size-1].sort).join.to_i 9 | end 10 | end 11 | end 12 | return num.join.to_i 13 | end 14 | 15 | #test 16 | p bigger_with_sam_digits(38627) #38672 -------------------------------------------------------------------------------- /src/binary_search.rb: -------------------------------------------------------------------------------- 1 | #nlogn 2 | def bsearch(arr, key) 3 | min = 0; max = arr.size - 1 4 | while min <= max 5 | mid = min + (max-min)/2 6 | if key < arr[mid] 7 | max = mid - 1 8 | elsif key > arr[mid] 9 | min = mid + 1 10 | else 11 | return mid 12 | end 13 | end 14 | return -1 15 | end 16 | 17 | #test 18 | ar = [23, 45, 67, 89, 123, 568] 19 | p bsearch(ar, 23) #0 20 | p bsearch(ar, 123) #4 21 | p bsearch(ar, 120) #-1 22 | -------------------------------------------------------------------------------- /src/binary_tree_side_view.rb: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode 3 | # attr_accessor :val, :left, :right 4 | # def initialize(val) 5 | # @val = val 6 | # @left, @right = nil, nil 7 | # end 8 | # end 9 | 10 | # @param {TreeNode} root 11 | # @return {Integer[]} 12 | def right_side_view(root) 13 | q = [[root]] 14 | res = [] 15 | while q.any? 16 | nodes = q.shift 17 | new_list = [] 18 | last = nil 19 | nodes.each do |n| 20 | if n 21 | new_list += [n.left,n.right] 22 | last = n 23 | end 24 | end 25 | q << new_list if new_list.any? 26 | res << last.val if last 27 | end 28 | return res 29 | end 30 | 31 | # p right_side_view([1,2,3,null,5,null,4]) # => [1,3,4] 32 | -------------------------------------------------------------------------------- /src/binary_tree_tamplate.rb: -------------------------------------------------------------------------------- 1 | class BinaryTreeNode 2 | attr_accessor :value 3 | attr_reader :left, :right 4 | 5 | def initialize(value) 6 | @value = value 7 | @left = nil 8 | @right = nil 9 | end 10 | 11 | def insert_left(value) 12 | @left = BinaryTreeNode.new(value) 13 | return @left 14 | end 15 | 16 | def insert_right(value) 17 | @right = BinaryTreeNode.new(value) 18 | return @right 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /src/brackets_combinations.rb: -------------------------------------------------------------------------------- 1 | def brackets(pairs, output='', open=0, close=0, arr = []) 2 | arr << output if open == pairs and close == pairs 3 | brackets pairs, output+'(', open+1, close, arr if open am 14 | res[am] = [res[am], 1 + res[am - c]].min 15 | end 16 | end 17 | 18 | res[amount] == max ? -1 : res[amount] 19 | end 20 | 21 | p coin_change([1, 2, 5], 11) # => 3 22 | -------------------------------------------------------------------------------- /src/count_and_say.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer} n 2 | # @return {String} 3 | def count_and_say(n) 4 | return "1" unless n > 1 5 | a = "1" 6 | (n-1).times do 7 | t = ''; last = nil; count = 0 8 | a.chars.each do |c| 9 | if c == last or last.nil? 10 | count +=1 11 | else 12 | t += count.to_s + last 13 | count = 1 14 | end 15 | last = c 16 | end 17 | t += count.to_s + last if count > 0 18 | a = t 19 | end 20 | return a 21 | end 22 | -------------------------------------------------------------------------------- /src/counting_sort.rb: -------------------------------------------------------------------------------- 1 | #all numbers need to be positive 2 | def counting_sort(arr) 3 | max = arr.max 4 | c = Array.new(max+2, 0) # initialize counting array with k+1 zero values 5 | arr.each { |i| c[i] += 1 } # build a histogram / count occurrences of all integer values in array a 6 | (0..max).each { |i| c[i] += c[i-1] } # compute indices for each key 7 | b = Array.new(arr.size, 0) # initialize output array 8 | arr.each { |i| b[c[i]-1] = i; c[i] -= 1 } 9 | b 10 | end 11 | 12 | #test 13 | p counting_sort [7,20,12,3,2,2,2,1,1,0,0] # => [0, 0, 1, 1, 2, 2, 2, 3, 7, 12, 20] -------------------------------------------------------------------------------- /src/decode_string.rb: -------------------------------------------------------------------------------- 1 | # @param {String} s 2 | # @return {String} 3 | def decode_string(s) 4 | strings = [''] 5 | dups = [] 6 | str = '' 7 | i = 0 8 | while i < s.size do 9 | if (s[i].to_i > 0) 10 | j = i+1 11 | j += 1 while s[j] != '[' 12 | num = s[i...j].to_i 13 | dups.push(num) 14 | strings.push(str) 15 | i = j+1 16 | elsif s[i] == ']' 17 | num = dups.pop 18 | str = strings.pop 19 | str *= num 20 | prev = strings.pop 21 | strings.push(prev + str) 22 | str = '' 23 | i+=1 24 | elsif s[i] != '[' 25 | str = strings.pop || '' 26 | str += s[i] 27 | strings.push(str) 28 | str = '' 29 | i+=1 30 | end 31 | end 32 | 33 | return strings.pop 34 | end 35 | 36 | p decode_string('2[abc]3[cd]ef') # => abcabccdcdcdef 37 | -------------------------------------------------------------------------------- /src/dijkstra.rb: -------------------------------------------------------------------------------- 1 | #Dijkstra's_algorithm 2 | #source: http://rosettacode.org/wiki/Dijkstra's_algorithm#Ruby 3 | class Graph 4 | Vertex = Struct.new(:name, :neighbours, :dist, :prev) 5 | 6 | def initialize(graph) 7 | @vertices = Hash.new{|h,k| h[k]=Vertex.new(k,[],Float::INFINITY)} 8 | @edges = {} 9 | graph.each do |(v1, v2, dist)| 10 | @vertices[v1].neighbours << v2 11 | @vertices[v2].neighbours << v1 12 | @edges[[v1, v2]] = @edges[[v2, v1]] = dist 13 | end 14 | @dijkstra_source = nil 15 | end 16 | 17 | def dijkstra(source) 18 | return if @dijkstra_source == source 19 | q = @vertices.values 20 | q.each do |v| 21 | v.dist = Float::INFINITY 22 | v.prev = nil 23 | end 24 | @vertices[source].dist = 0 25 | until q.empty? 26 | u = q.min_by {|vertex| vertex.dist} 27 | break if u.dist == Float::INFINITY 28 | q.delete(u) 29 | u.neighbours.each do |v| 30 | vv = @vertices[v] 31 | if q.include?(vv) 32 | alt = u.dist + @edges[[u.name, v]] 33 | if alt < vv.dist 34 | vv.dist = alt 35 | vv.prev = u.name 36 | end 37 | end 38 | end 39 | end 40 | @dijkstra_source = source 41 | end 42 | 43 | def shortest_path(source, target) 44 | dijkstra(source) 45 | path = [] 46 | u = target 47 | while u 48 | path.unshift(u) 49 | u = @vertices[u].prev 50 | end 51 | return path, @vertices[target].dist 52 | end 53 | 54 | def to_s 55 | "#<%s vertices=%p edges=%p>" % [self.class.name, @vertices.values, @edges] 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /src/facebook-interview-tips.md: -------------------------------------------------------------------------------- 1 | ## Scheduling 2 | Please take a look at your calendar and let me know 3 - 5 dates that you can come on-site. We schedule every day, except Wednesdays, and usually start around 10am/10:30am. 3 | 4 | For quick reference, you can expect the following interviews (in no particular order): 5 | - 2 coding interview (45 mins each) 6 | - 2 design interviews (45 mins each) 7 | - 1 behavior interview (60 mins) 8 | - Lunch (not an interview) 9 | 10 | ## Preparing 11 | ### (Coding) 12 | In order to help you prepare for the coding interviews I recommend the following book/free online document: http://www.valleytalk.org/wp-content/uploads/2012/10/CrackCode.pdf 13 | Be prepared for technical questions involving coding, algorithms, data structures, and runtime complexity 14 | It may also help to review core CS concepts as well as subjects pertaining to the scale of our environment. 15 | For coding questions, you will be asked to produce clean, compilable, efficient code in a reasonable amount of time. 16 | 17 | ## A few helpful hints: 18 | Think out loud if you are working through a solution you are presented with as the Engineer will want to know how you approach and troubleshoot problems. 19 | If the interviewer gives you hints to improve your code, take them and run with them. It is good to adjust and work through the problems with the interviewer to show your thought process and problem solving ability. 20 | The Engineer will also want to ascertain your level of interest in the role. Have some questions prepared, for example, what is involved in the role, day to day tasks, what is the most exciting project they have worked on in Facebook etc 21 | Please research recent news online for talking points and more information about Facebook. 22 | 23 | ## Some Topics & things to Consider: 24 | Data Structures: Arrays and lists (critical), Binary trees (critical), Hash tables (critical), Stacks and queues (critical), Graphs (critical), Trie (nice to have), Heap (nice to have), Set (nice to have), Red-black trees (nice to have) 25 | Algorithms/CS Concepts: Search (iterator, binary, hash - all critical), Sort (merge, quick, bucket - all critical), Graph traversals (BFS, DFS - all critical), Complexity and big O notation (critical), Recursion (critical), Randomized quicksort (nice to have), Heap sort (nice to have), Radix sort (nice to have), Spanning tree and minimum cut (nice to have) 26 | Don't worry about rote memorization such as runtimes or API/native calls. It's always good to know how to figure out approximate runtimes on the fly but the code you write is more important. 27 | Generally avoid solutions that would have lots of edge cases or huge if/elseif/... blocks. Most coding interview questions at most places are designed with semi-elegant solutions so try to identify patterns. Deciding between iteration and recursion is always an important step. 28 | You may be asked in the interview to explain a technically challenging problem you have worked on in the past. Think about and explain how the problem was technically challenging. 29 | 30 | ### (Design) 31 | Please see the attached document for more details on our design interview. In order to help you prepare for the design interview, I recommend a book that a number of our engineers used to help them prepare when they interviewed successfully with us: http://www.amazon.com/Building-Scalable-Web-Sites-Applications/dp/0596102356. This is also a great tutorial on system design questions: http://www.hiredintech.com/app#system-design. 32 | 33 | In addition, the design/architecture interview will focus on designing a system with some product concepts, normally at Facebook scale. A successful interview will show that you: 34 | clearly understand the problem 35 | propose a design for a system or API that breaks the problem down into components that can be built independently 36 | identify the bottlenecks as the system scales and can poke holes in the design 37 | understand how to adapt your solution when requirements are changed 38 | draw diagrams that clearly describe the relationship between the different components in the system 39 | calculate (back-of-the-envelope) the physical resources necessary to make this system work 40 | 41 | ### (Passion/Motivations) 42 | In addition to the technical piece, we are also looking to learn about your passion and motivations. Understanding about the work at Facebook is also important, so try to be ready to address what it is you like about Facebook, our product, the technical challenges we face here, and your feedback on the product. 43 | 44 | Typical questions to be ready for are: 45 | - What are some successes and failures you’ve experienced, and what did you learn? 46 | - How have you overcome a challenging work relationship? 47 | - What features would you improve on Facebook? 48 | - What would you like to do at Facebook? 49 | - What challenging problems have you solved in the past? 50 | - What product or work related project are you most proud of? 51 | - What projects have you driven or how have you lead teams? 52 | 53 | I know this is a lot of information, but we want to make sure you are best prepared for these conversations. -------------------------------------------------------------------------------- /src/game_of_life.rb: -------------------------------------------------------------------------------- 1 | # Alogirthm for Game of Life 2 | # Check more details here - http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life 3 | 4 | class GameOfLife 5 | attr_accessor :matrix, :rows, :columns 6 | 7 | def initialize rows, columns 8 | @rows, @columns = rows, columns 9 | @matrix = [] 10 | rows.times do |row| 11 | @matrix[row] ||= [] 12 | columns.times do |column| 13 | @matrix[row][column] = false 14 | end 15 | end 16 | end 17 | 18 | def next_tick 19 | new_matrix = [] 20 | rows.times do |row| 21 | new_matrix[row] ||= [] 22 | columns.times do |column| 23 | alive_neighbours_count = neighbours(row, column).count(true) 24 | if !self.matrix[row][column] && alive_neighbours_count == 3 25 | new_matrix[row][column] = true 26 | elsif self.matrix[row][column] && alive_neighbours_count != 2 && alive_neighbours_count != 3 27 | new_matrix[row][column] = false 28 | else 29 | new_matrix[row][column] = self.matrix[row][column] 30 | end 31 | end 32 | end 33 | self.matrix = new_matrix 34 | end 35 | 36 | def print_cells 37 | rows.times do |row| 38 | columns.times do |column| 39 | matrix[row][column] ? print("O") : print("-") 40 | end 41 | print "\n" 42 | end 43 | print "\n" 44 | end 45 | 46 | def neighbours row, column 47 | neighbours = [] 48 | rows_limit = matrix.count - 1 49 | columns_limit = matrix[0].count - 1 50 | ([0, row-1].max..[rows_limit, row+1].min).to_a.each do |row_index| 51 | ([0, column-1].max..[columns_limit, column+1].min).to_a.each do |column_index| 52 | neighbours << matrix[row_index][column_index] unless row_index == row && column_index == column 53 | end 54 | end 55 | neighbours 56 | end 57 | end 58 | 59 | game = GameOfLife.new(100, 100) 60 | 61 | # set alive cells 62 | game.matrix[0][0] = true 63 | game.matrix[0][1] = true 64 | game.matrix[1][0] = true 65 | game.matrix[1][3] = true 66 | game.matrix[2][1] = true 67 | game.matrix[2][2] = true 68 | 69 | game.print_cells 70 | 71 | 5.times do 72 | game.next_tick 73 | game.print_cells 74 | end 75 | -------------------------------------------------------------------------------- /src/gcd.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def gcd_rec(x, y) 4 | y == 0 ? x : gcd_rec(y, x%y) 5 | end 6 | 7 | def gcd_iter(x, y) 8 | x, y = y, x%y while y != 0 9 | x 10 | end 11 | 12 | # ax + by = gcd(x, y) 13 | def xgcd_rec(x, y) 14 | if y == 0 15 | [x, 1, 0] 16 | else 17 | gcd, a, b = xgcd_rec(y, x%y) 18 | [gcd, b, a-x/y*b] 19 | end 20 | end 21 | 22 | # ax + by = gcd(x, y) 23 | def xgcd_iter(x, y) 24 | a, next_a = 1, 0 25 | b, next_b = 0, 1 26 | while y != 0 27 | a, next_a = next_a, a-x/y*next_a 28 | b, next_b = next_b, b-x/y*next_b 29 | x, y = y, x%y 30 | end 31 | [x, a, b] 32 | end 33 | 34 | # test 35 | 10.times do 36 | x = Random.rand(100) + 1 37 | y = Random.rand(100) + 1 38 | gcd_res = x.gcd(y) 39 | gcd_rec_res = gcd_rec(x, y) 40 | gcd_iter_res = gcd_iter(x, y) 41 | g1, a1, b1 = xgcd_rec(x, y) 42 | g2, a2, b2 = xgcd_iter(x, y) 43 | puts "#{gcd_res}\t#{gcd_rec_res}\t#{gcd_iter_res}\t#{a1}*#{x}+#{b1}*#{y}=#{g1}\t#{a2}*#{x}+#{b2}*#{y}=#{g2}" 44 | end 45 | -------------------------------------------------------------------------------- /src/google-interview-tips.md: -------------------------------------------------------------------------------- 1 | The interview will include topics such as coding, data structures, algorithms, computer science theory, and systems design. We recommend you spend some time exploring our website to get into the right mind frame. 2 | 3 | Books to assist you in preparing for your interview: 4 | 5 | "The Algorithm Design Manual" 6 | Author: Steven S. Skiena 7 | 8 | "Introduction to Algorithms" 9 | Authors: Cormen, Leiserson, Rivest & Stein 10 | [Also known as the “CLR” textbook] 11 | 12 | “Programming Interviews Exposed: Secrets to Landing Your Next Job" 13 | Authors: John Mongan, Noah Suojanen, and Eric Giguère (Wiley Computer Publishing) 14 | [This book will prepare you for what to expect during an interview. 15 | White board preparation is especially useful for onsite interviews.] 16 | 17 | "Programming Pearls" 18 | Author: Jon Bentley 19 | [Programming questions that get you to start thinking outside of the box-- always useful in a Google interview :) ] 20 | 21 | Also, this blog post should be helpful in letting you know what to expect and how to prepare: http://steve-yegge.blogspot.com/2008/03/get-that-job-at-google.html 22 | 23 | System Design Publications: 24 | Google File System (research.google.com/archive/bigtable-osdi06.pdf) 25 | Google Bigtable (research.google.com/archive/bigtable.html) 26 | Google MapReduce (http://research.google.com/archive/mapreduce.html) 27 | 28 | Please review the following topics: 29 | Big-O notations also known as "the run time characteristic of an algorithm". You may want to refresh hash tables, heaps, binary trees, linked lists, depth-first search, recursion. For more information on Algorithms you can visit: 30 | http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=alg_index 31 | 32 | Coding: You should know at least one programming language really well (preferably C++, Java or Python). You will be expected to write some code in at least some of your interviews. You will be expected to know a fair amount of detail about your preferred programming language and will be asked to do some coding on the whiteboard. 33 | 34 | Sorting: Know how to sort. Don't do bubble-sort. You should know the details of at least one n*log(n) sorting algorithm, preferably two (say, quicksort and merge sort). Merge sort can be highly useful in situations where quick sort is impractical, so take a look at it. 35 | 36 | Hashtables: Arguably the single most important data structure known to mankind. You absolutely should know how they work. Be able to implement one using only arrays in your favorite language, in about the space of one interview. 37 | 38 | Trees: Know about trees; basic tree construction, traversal and manipulation algorithms. Familiarize yourself with binary trees, n-ary trees, and trie-trees. Be familiar with at least one type of balanced binary tree, whether it's a red/black tree, a splay tree or an AVL tree, and know how it's implemented. Understand tree traversal 39 | 40 | Algorithms: BFS and DFS, and know the difference between inorder, postorder and preorder. 41 | Graphs: Graphs are really important at Google. There are 3 basic ways to represent a graph in memory (objects and pointers, matrix, and adjacency list); familiarize yourself with each representation and its pros & cons. You should know the basic graph traversal algorithms: breadth-first search and depth-first search. Know their computational complexity, their tradeoffs, and how to implement them in real code. If you get a chance, try to study up on fancier algorithms, such as Dijkstra and A*. 42 | 43 | Other Data Structures: You should study up on as many other data structures and algorithms as possible. You should especially know about the most famous classes of NP-complete problems, such as traveling salesman and the knapsack problem, and be able to recognize them when an interviewer asks you them in disguise. Find out whatNP-complete means. 44 | 45 | Mathematics: Some interviewers ask basic discrete math questions. This is more prevalent at Google than at other companies because counting problems, probability problems, and other Discrete Math 101 situations surrounds us. Spend some time before the interview refreshing your memory on (or teaching yourself) the essentials of combinatorics and probability. You should be familiar with n-choose-k problems and their ilk – the more the better. 46 | 47 | Operating Systems: Know about processes, threads and concurrency issues. Know about locks and mutexes and semaphores and monitors and how they work. Know about deadlock and livelock and how to avoid them. Know what resources a processes needs, and a thread needs, and how context switching works, and how it's initiated by the operating system and underlying hardware. Know a little about scheduling. The world is rapidly moving towards multi-core, so know the fundamentals of "modern" concurrency constructs. For information on System Design: 48 | http://research.google.com/pubs/DistributedSystemsandParallelComputing.html 49 | 50 | Helpful links & Sites: 51 | - http://www.youtube.com/watch?v=w887NIa_V9w 52 | - http://www.youtube.com/lifeatgoogle 53 | - http://codility.com/ 54 | - http://www.spoj.pl/tutorials/ 55 | - www.hackquest.com 56 | - http://www.ets.org/gre/subject/about/content/computer_science 57 | - http://projecteuler.net/ 58 | 59 | 60 | A few last tips: 61 | Talk through your thought process about the questions you are asked. In all of Google's interviews, our engineers are evaluating not only your technical abilities but also how you approach problems and how you try to solve them. 62 | Ask clarifying questions if you do not understand the problem or need more information. Many of the questions asked in Google interviews are deliberately underspecified because our engineers are looking to see how you engage the problem. In particular, they are looking to see which areas leap to your mind as the most important piece of the technological puzzle you've been presented. 63 | Think about ways to improve the solution you'll present. In many cases, the first answer that springs to mind may not be the most elegant solution and may need some refining. It's definitely worthwhile to talk through your initial thoughts to a qu 64 | Question and take time to compose a more efficient solution. 65 | -------------------------------------------------------------------------------- /src/house-robber.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} nums 2 | # @return {Integer} 3 | def rob(nums) 4 | return 0 if nums.empty? 5 | return foo(nums, 0) 6 | end 7 | 8 | def foo(nums, i, h = {}) 9 | return h[i] if h[i] 10 | return nums[i] if (i == nums.size - 1) 11 | return 0 if (i >= nums.size) 12 | h[i] = [nums[i] + foo(nums, i+2, h), foo(nums, i+1, h)].max 13 | return h[i] 14 | end 15 | 16 | 17 | p rob([15,10,3, 10]) # => 25 -------------------------------------------------------------------------------- /src/insertion_sort.rb: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/Insertion_sort 2 | def insertion_sort a 3 | for i in 1..(a.size - 1) 4 | x = a[i] 5 | j = i 6 | while(j > 0 && a[j-1] >= x) 7 | a[j] = a[j-1] 8 | j -= 1 9 | end 10 | a[j] = x 11 | end 12 | a 13 | end 14 | 15 | #test 16 | p insertion_sort [4,2,3,3,1,2,2,10,1,0] 17 | -------------------------------------------------------------------------------- /src/join_sets.rb: -------------------------------------------------------------------------------- 1 | #prints all combinations of join sets 2 | def join_sets set, i = 0, str = '', result = [] 3 | return result << str if str.size == set.size 4 | for item in set[i] 5 | join_sets set, i.succ, str + item.to_s, result 6 | end 7 | result 8 | end 9 | 10 | p join_sets [ ['A','B'] , ['C','D'], ['E'] ] #["ACE", "ADE", "BCE", "BDE"] 11 | -------------------------------------------------------------------------------- /src/kmp.rb: -------------------------------------------------------------------------------- 1 | #Knuth–Morris–Pratt algorithm (KMP) algorithm, http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm 2 | #find the ptrn (m) in a string (m) in o(n+m) time and o(m) space. 3 | 4 | def kmp str, ptrn 5 | m = i = 0 6 | t = compute_table ptrn 7 | results = [] 8 | 9 | while m + i < str.size 10 | if str[m+i] == ptrn[i] #match 11 | results << m if i == (ptrn.size - 1) 12 | i+=1 13 | else #fallback 14 | if t[i] and t[i] > -1 15 | m = m+1 - t[i] 16 | i = t[i] 17 | else 18 | i = 0 19 | m += 1 20 | end 21 | end 22 | end 23 | results 24 | end 25 | 26 | def compute_table ptrn 27 | arr = Array.new(ptrn.size) 28 | arr[0] = -1; arr[1] = 0; 29 | pos = 2; cnd = 0; 30 | while pos < arr.size 31 | if ptrn[pos-1] == ptrn[cnd] 32 | cnd+=1; arr[pos] = cnd; pos+=1 33 | elsif cnd > 0 34 | cnd = arr[cnd] 35 | else 36 | arr[pos] = 0; pos+=1 37 | end 38 | end 39 | arr 40 | end 41 | 42 | p kmp 'banana', 'na' #[2,4] -------------------------------------------------------------------------------- /src/knapsack.rb: -------------------------------------------------------------------------------- 1 | #taken from http://rosettacode.org/wiki/Knapsack_problem/0-1#Ruby using dynamic programming 2 | KnapsackItem = Struct.new(:name, :cost, :value) 3 | KnapsackProblem = Struct.new(:items, :max_cost) 4 | 5 | def dynamic_programming_knapsack(problem) 6 | num_items = problem.items.size 7 | items = problem.items 8 | max_cost = problem.max_cost 9 | 10 | cost_matrix = zeros(num_items, max_cost+1) 11 | 12 | num_items.times do |i| 13 | (max_cost + 1).times do |j| 14 | if(items[i].cost > j) 15 | cost_matrix[i][j] = cost_matrix[i-1][j] 16 | else 17 | cost_matrix[i][j] = [cost_matrix[i-1][j], items[i].value + cost_matrix[i-1][j-items[i].cost]].max 18 | end 19 | end 20 | end 21 | 22 | cost_matrix 23 | end 24 | 25 | def get_used_items(problem, cost_matrix) 26 | i = cost_matrix.size - 1 27 | currentCost = cost_matrix[0].size - 1 28 | marked = Array.new(cost_matrix.size, 0) 29 | 30 | while(i >= 0 && currentCost >= 0) 31 | if(i == 0 && cost_matrix[i][currentCost] > 0 ) || (cost_matrix[i][currentCost] != cost_matrix[i-1][currentCost]) 32 | marked[i] = 1 33 | currentCost -= problem.items[i].cost 34 | end 35 | i -= 1 36 | end 37 | marked 38 | end 39 | 40 | def get_list_of_used_items_names(problem, cost_matrix) 41 | items = problem.items 42 | used_items = get_used_items(problem, cost_matrix) 43 | 44 | result = [] 45 | 46 | used_items.each_with_index do |item,i| 47 | if item > 0 48 | result << items[i].name 49 | end 50 | end 51 | 52 | result.sort.join(', ') 53 | end 54 | 55 | def zeros(rows, cols) 56 | Array.new(rows) do |row| 57 | Array.new(cols, 0) 58 | end 59 | end 60 | 61 | if $0 == __FILE__ 62 | 63 | items = [ 64 | KnapsackItem.new('map' , 9 , 150) , KnapsackItem.new('compass' , 13 , 35) , 65 | KnapsackItem.new('water' , 153 , 200) , KnapsackItem.new('sandwich' , 50 , 160) , 66 | KnapsackItem.new('glucose' , 15 , 60) , KnapsackItem.new('tin' , 68 , 45) , 67 | KnapsackItem.new('banana' , 27 , 60) , KnapsackItem.new('apple' , 39 , 40) , 68 | KnapsackItem.new('cheese' , 23 , 30) , KnapsackItem.new('beer' , 52 , 10) , 69 | KnapsackItem.new('suntan cream' , 11 , 70) , KnapsackItem.new('camera' , 32 , 30) , 70 | KnapsackItem.new('t-shirt' , 24 , 15) , KnapsackItem.new('trousers' , 48 , 10) , 71 | KnapsackItem.new('umbrella' , 73 , 40) , KnapsackItem.new('waterproof trousers' , 42 , 70) , 72 | KnapsackItem.new('waterproof overclothes' , 43 , 75) , KnapsackItem.new('note-case' , 22 , 80) , 73 | KnapsackItem.new('sunglasses' , 7 , 20) , KnapsackItem.new('towel' , 18 , 12) , 74 | KnapsackItem.new('socks' , 4 , 50) , KnapsackItem.new('book' , 30 , 10) 75 | ] 76 | 77 | problem = KnapsackProblem.new(items, 400) 78 | 79 | cost_matrix = dynamic_programming_knapsack problem 80 | puts 81 | puts 'Dynamic Programming:' 82 | puts 83 | puts 'Found solution: ' + get_list_of_used_items_names(problem, cost_matrix) 84 | puts 'With value: ' + cost_matrix.last.last.to_s 85 | end -------------------------------------------------------------------------------- /src/knapsack2.rb: -------------------------------------------------------------------------------- 1 | INFINITY = (2 ** (0.size * 8 -2) -1) 2 | 3 | def max_duffel_bag_value(cake_arrays, weight_capacity) 4 | 5 | # we make an array to hold the maximum possible value at every 6 | # duffel bag weight capacity from 0 to weight_capacity 7 | # starting each index with value 0 8 | max_values_at_capacities = [0] * (weight_capacity + 1) 9 | 10 | (0..weight_capacity).each do |current_capacity| 11 | 12 | # set a variable to hold the max monetary value so far for current_capacity 13 | current_max_value = 0 14 | 15 | cake_arrays.each do |cake_weight, cake_value| 16 | 17 | # if a cake weighs 0 and has a positive value the value of our duffel bag is infinite! 18 | if (cake_weight == 0 && cake_value != 0) 19 | return INFINITY 20 | end 21 | 22 | # if the current cake weighs as much or less than the current weight capacity 23 | # it's possible taking the cake would give get a better value 24 | if (cake_weight <= current_capacity) 25 | 26 | # so we check: should we use the cake or not? 27 | # if we use the cake, the most kilograms we can include in addition to the cake 28 | # we're adding is the current capacity minus the cake's weight. we find the max 29 | # value at that integer capacity in our array max_values_at_capacities 30 | max_value_using_cake = cake_value + max_values_at_capacities[current_capacity - cake_weight] 31 | 32 | # now we see if it's worth taking the cake. how does the 33 | # value with the cake compare to the current_max_value? 34 | current_max_value = [max_value_using_cake, current_max_value].max 35 | end 36 | end 37 | 38 | # add each capacity's max value to our array so we can use them 39 | # when calculating all the remaining capacities 40 | max_values_at_capacities[current_capacity] = current_max_value 41 | end 42 | 43 | return max_values_at_capacities[weight_capacity] 44 | end 45 | -------------------------------------------------------------------------------- /src/kruskal.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # DisjointSet - Union-Find Data Structure 4 | # - root(x): find the leader of the set where x in 5 | # - connected?(x, y): check if x and y are in the same set 6 | # - union(x, y): union x and y if x and y which are not in the same set 7 | class DisjointSet 8 | def initialize(n) 9 | @parent = Array.new(n, -1) 10 | end 11 | 12 | def root(x) 13 | @parent[x] < 0 ? x : @parent[x] = root(@parent[x]) 14 | end 15 | 16 | def connected?(x, y) 17 | root(x) == root(y) 18 | end 19 | 20 | def union(x, y) 21 | if connected?(x, y) 22 | false 23 | else 24 | rx = root(x) 25 | ry = root(y) 26 | rx, ry = ry, rx if @parent[rx] > @parent[ry] 27 | @parent[rx] += @parent[ry] 28 | @parent[ry] = rx 29 | end 30 | end 31 | end 32 | 33 | Edge = Struct.new(:from, :to, :weight) do 34 | def <=> rhs 35 | self.weight <=> rhs.weight 36 | end 37 | end 38 | 39 | # minimum spanning tree 40 | # the label of the vertex is between 0 and n - 1 41 | def kruskal(n, edges) 42 | ret = [] 43 | dsu = DisjointSet.new(n) 44 | edges.sort.each{ |e| ret << e if dsu.union(e.from, e.to) } 45 | ret 46 | end 47 | 48 | # test 49 | edges = [ 50 | Edge.new(0, 1, 1), Edge.new(1, 2, 2), Edge.new(2, 3, 9), 51 | Edge.new(3, 0, 7), Edge.new(0, 2, 2), Edge.new(1, 3, 6) 52 | ] 53 | 54 | p kruskal(4, edges) # (0, 1, 1) (1, 2, 2) (1, 3, 6) 55 | -------------------------------------------------------------------------------- /src/linkedin-interview.md: -------------------------------------------------------------------------------- 1 | Thanks again for your time on the phone today. I enjoyed talking with you and I’ll be back in touch soon with more information on the next steps. 2 | 3 | Please let me know if you have any questions in the meantime or if there is any information that we didn't get to cover on the phone, but might be useful for me to know. 4 | 5 | Here is additional information on our interviews and the preparation material I mentioned. 6 | 7 | The phone interview will take an hour and you’ll need to have a computer with internet access for the coding portion of the interview which will be done through www.collabedit.com. A major component of our assessment involves applying algorithm and Computer Science fundamentals, and explaining/implementing the solution in code. You will need to do this without any aids engineers typically have access to, e.g. IDE, online docs and you will be under the time gun. 8 | 9 | Algorithms Hints: 10 | - Think about trade-offs of different approaches 11 | - Knowing the complexity of your solution can indicate whether you can do better 12 | - Don't over-complicate your algorithm to fit in specific concepts - we are looking for answers to the question, not looking for certain techniques 13 | 14 | I know interviewing can be a stressful process, so here are a few additional tips to help you: 15 | Tip #1: Ask clarifying questions instead of making assumptions. Before starting on your implementation, be clear on requirements. 16 | Tip #2: Verbalize your thoughts while you’re solving a problem. Our interviewer wants to understand your thought process and how you go about solving a problem. So by thinking out loud, you’ll give them an idea and they’ll be able to give you hints to steer you in the right direction. 17 | Tip #3: You will be assessed on both speed as well as quality of the solution. So, don’t take too much time to think through the problem, but take enough time so you can provide a clean and elegant solution. For some questions, the expectation is that someone should be able to work through the problem in 20min, so there could be multiple questions in the hour slot; others may take the entire hour to solve. 18 | Tip #4: If asked about an interesting project, make sure to choose a project that you are actually interested in rather than just picking your most current project. 19 | Tip #5: Find and fix your bugs using edge cases, tests, or other means. 20 | Tip #6: Visit our engineering blog to find out a bit more about the different technologies and projects we are working on as well as get a feel for our engineering and company culture. - http://engineering.linkedin.com/ 21 | 22 | Here are a few links to articles with additional tips on how to practice for Software Engineering interviews: 23 | - https://www.linkedin.com/today/post/article/20140205163949-28614372-getting-that-job-at-linkedin?goback=.nmp_*1_*1_*1_*1_*1_*1_*1_*1_*1_*1_*1&trk=object-title 24 | - https://www.linkedin.com/pulse/article/20141119081429-75491088-land-a-job-at-a-top-silicon-valley-company 25 | - https://www.linkedin.com/pulse/article/20140425202654-10752460-what-to-do-and-not-do-if-i-interview-you?trk=mp-reader-card 26 | 27 | Here’s an article about the interesting problems our engineers are working on: http://gigaom.com/2013/03/03/how-and-why-linkedin-is-becoming-an-engineering-powerhouse/ 28 | 29 | I know this email was long, but I hope you find it helpful. 30 | -------------------------------------------------------------------------------- /src/longest_common_subsequence.rb: -------------------------------------------------------------------------------- 1 | #Longest common subsequence problem 2 | #https://en.wikipedia.org/wiki/Longest_common_subsequence_problem 3 | def LCSLength (x, y) 4 | h = Array.new(x.size) {Array.new(y.size)} 5 | x.size.times {|i| h[i][0] = x[0] == y[0] ? 1 : 0 } 6 | y.size.times {|i| h[0][i] = x[0] == y[0] ? 1 : 0 } 7 | 8 | (1..x.size-1).each do |i| 9 | (1..y.size-1).each do |j| 10 | if x[i] == y[j] 11 | h[i][j] = (h[i-1][j-1] ? h[i-1][j-1] : 0) + 1 12 | else 13 | h[i][j] = [ h[i-1][j] ? h[i-1][j] : 0, h[i][j-1] ? h[i][j-1] : 0].max 14 | end 15 | end 16 | end 17 | h[x.size-1][y.size-1] 18 | end 19 | 20 | 21 | p LCSLength('waaaa', 'bbbbasfaaewra') #returns 4 -------------------------------------------------------------------------------- /src/longest_increasing_subsequence.rb: -------------------------------------------------------------------------------- 1 | #n^2 2 | def lis arr 3 | a = Array.new(arr.size) { |i| i = [arr[i]] } 4 | (1..arr.size-1).each do |i| 5 | (0..i).each do |j| 6 | if a[i].size <= a[j].size and a[i].last > a[j].last 7 | a[i] = a[j] + [arr[i]] 8 | end 9 | end 10 | end 11 | a.last 12 | end 13 | 14 | #test 15 | p lis [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11,4,5,6, 7, 15,8,9] #[0, 2, 3, 4, 5, 6, 7, 8, 9] -------------------------------------------------------------------------------- /src/longest_increasing_subsequence_nlogn.rb: -------------------------------------------------------------------------------- 1 | #nlogn 2 | def lis arr 3 | return [] if arr.empty? 4 | n = arr.size 5 | pre = [-1] 6 | len = [0] 7 | 8 | 1.upto(n-1) do |i| 9 | # binary search first pos s.t. arr[len[pos]] >= arr[i] 10 | left, right, mid, pos = 0, len.size-1, -1, len.size 11 | while left <= right 12 | mid = left + (right - left) / 2 13 | if arr[len[mid]] >= arr[i] 14 | right = mid - 1 15 | pos = mid 16 | else 17 | left = mid + 1 18 | end 19 | end 20 | if pos == len.size 21 | pre << len[pos-1] 22 | len << i 23 | else 24 | pre << (pos == 0 ? -1 : len[pos-1]) 25 | len[pos] = i 26 | end 27 | end 28 | 29 | o = [] 30 | i = len.last 31 | while i != -1 32 | o << arr[i] 33 | i = pre[i] 34 | end 35 | o.reverse 36 | end 37 | 38 | p lis [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 4, 5, 6, 7, 15, 8, 9] 39 | # [0, 1, 3, 4, 5, 6, 7, 8, 9] 40 | -------------------------------------------------------------------------------- /src/max_stack.rb: -------------------------------------------------------------------------------- 1 | class MaxStack 2 | def initialize() 3 | @q = [] 4 | @q2 = [] 5 | end 6 | 7 | def push(x) 8 | max = @q.any? ? [x, @q.last[1]].max : x 9 | @q << [x, max] 10 | x 11 | end 12 | 13 | def pop() 14 | return nil unless @q.any? 15 | 16 | ret = @q.pop[0] 17 | ret 18 | end 19 | 20 | def top() 21 | return nil unless @q.any? 22 | @q.last[0] 23 | end 24 | 25 | def peek_max() 26 | return nil unless @q.any? 27 | @q.last[1] 28 | end 29 | 30 | 31 | def pop_max() 32 | return nil unless @q.any? 33 | max = @q.last[1] 34 | q2 = [] 35 | 36 | while top() != max 37 | q2 << pop() 38 | end 39 | pop() 40 | 41 | while q2.any? 42 | push(q2.pop) 43 | end 44 | max 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /src/max_subarray.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} nums 2 | # @return {Integer} 3 | def max_sub_array(nums) 4 | max = -2147483648; t = 0; 5 | nums.each do |n| 6 | t = [t+n, n].max 7 | max = [max, t].max 8 | end 9 | max 10 | end 11 | -------------------------------------------------------------------------------- /src/merge_sort.rb: -------------------------------------------------------------------------------- 1 | def merge_sort(lst) 2 | return lst if lst.length <= 1 3 | mid = lst.length / 2 4 | left = merge_sort(lst[0..mid - 1]) 5 | right = merge_sort(lst[mid..lst.length]) 6 | merge(left, right) 7 | end 8 | 9 | def merge(left, right) 10 | result = []; i = j = 0; 11 | while i < left.size and j < right.size 12 | if left[i] <= right[j] 13 | result << left[i]; i+=1; 14 | else 15 | result << right[j]; j+=1; 16 | end 17 | end 18 | while i= j 8 | if s[i] == s[j] 9 | return helper(s, i+1, j-1) 10 | else 11 | return 1 + [helper(s, i+1, j), helper(s, i, j-1)].min 12 | end 13 | end 14 | 15 | p shortest_palindrome('geek') # => 2 (kgeegk) 16 | -------------------------------------------------------------------------------- /src/mirror.rb: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode 3 | # attr_accessor :val, :left, :right 4 | # def initialize(val) 5 | # @val = val 6 | # @left, @right = nil, nil 7 | # end 8 | # end 9 | 10 | # @param {TreeNode} root 11 | # @return {Boolean} 12 | def is_symmetric(root) 13 | return true if root.nil? 14 | mirror(root.left, root.right) 15 | end 16 | 17 | def mirror(a,b) 18 | return a == b if (a == nil || b == nil) 19 | a.val == b.val && mirror(a.left, b.right) && mirror(a.right,b.left) 20 | end 21 | 22 | -------------------------------------------------------------------------------- /src/monty_hall.rb: -------------------------------------------------------------------------------- 1 | class MontyHall 2 | def self.simulate(ndoors, switch = nil) 3 | prng = Random.new 4 | winning_door = prng.rand(1..ndoors) 5 | choice = prng.rand(1..ndoors) 6 | doors = (1..ndoors).to_a 7 | 8 | while doors.size > 2 9 | door_to_open_index = prng.rand(1..doors.size) 10 | door_to_open = doors[door_to_open_index] 11 | 12 | if door_to_open == winning_door and door_to_open = choice 13 | doors.delete_at(door_to_open_index) 14 | end 15 | end 16 | 17 | if switch 18 | choice = (doors[1] == choice and doors[2] or doors[1]) 19 | end 20 | 21 | # returns true if users wins 22 | return choice == winning_door 23 | end 24 | end 25 | 26 | p MontyHall.simulate(3, true) -------------------------------------------------------------------------------- /src/nested_list_weight_sum_ii.rb: -------------------------------------------------------------------------------- 1 | # This is the interface that allows for creating nested lists. 2 | # You should not implement it, or speculate about its implementation 3 | # 4 | #class NestedInteger 5 | # def is_integer() 6 | # """ 7 | # Return true if this NestedInteger holds a single integer, rather than a nested list. 8 | # @return {Boolean} 9 | # """ 10 | # 11 | # def get_integer() 12 | # """ 13 | # Return the single integer that this NestedInteger holds, if it holds a single integer 14 | # Return nil if this NestedInteger holds a nested list 15 | # @return {Integer} 16 | # """ 17 | # 18 | # def set_integer(value) 19 | # """ 20 | # Set this NestedInteger to hold a single integer equal to value. 21 | # @return {Void} 22 | # """ 23 | # 24 | # def add(elem) 25 | # """ 26 | # Set this NestedInteger to hold a nested list and adds a nested integer elem to it. 27 | # @return {Void} 28 | # """ 29 | # 30 | # def get_list() 31 | # """ 32 | # Return the nested list that this NestedInteger holds, if it holds a nested list 33 | # Return nil if this NestedInteger holds a single integer 34 | # @return {NestedInteger[]} 35 | # """ 36 | 37 | # @param {NestedInteger[]} nested_list 38 | # @return {Integer} 39 | 40 | def depth_sum_inverse(nested_list) 41 | h = {} 42 | lvl = 0 43 | 44 | while nested_list.any? 45 | q = [] 46 | lvl+=1 47 | h[lvl] = 0 48 | 49 | nested_list.each do |n| 50 | if n.is_integer() 51 | h[lvl] += n.get_integer 52 | else 53 | q << n.get_list() 54 | end 55 | end 56 | 57 | nested_list = q.flatten 58 | end 59 | 60 | sum = 0 61 | 62 | h.each do |k, v| 63 | sum += ((lvl-k+1) * v) 64 | end 65 | 66 | sum 67 | end 68 | -------------------------------------------------------------------------------- /src/permutations.rb: -------------------------------------------------------------------------------- 1 | def perm arr, i=0 2 | p arr if i == arr.size 3 | (i...arr.size).each do |j| 4 | arr[i], arr[j] = arr[j], arr[i] 5 | perm arr, i+1 6 | arr[i], arr[j] = arr[j], arr[i] 7 | end 8 | end 9 | 10 | perm 'ABC' 11 | =begin test 12 | "ABC" 13 | "ACB" 14 | "BAC" 15 | "BCA" 16 | "CBA" 17 | "CAB" 18 | =end 19 | -------------------------------------------------------------------------------- /src/phone.rb: -------------------------------------------------------------------------------- 1 | def letters_for_number num 2 | case num 3 | when 0, 1 4 | [num] 5 | when 2 6 | ['A', 'B', 'C'] 7 | when 3 8 | ['D', 'E', 'F'] 9 | when 4 10 | ['G', 'H', 'I'] 11 | when 5 12 | ['J', 'K', 'L'] 13 | when 6 14 | ['M', 'N', 'O'] 15 | when 7 16 | ['P', 'Q', 'R', 'S'] 17 | when 8 18 | ['T', 'U', 'V'] 19 | when 9 20 | ['W' ,'X', 'Y', 'Z'] 21 | else 22 | [] 23 | end 24 | end 25 | 26 | def join_sets set, i = 0, str = '', result = [] 27 | return result << str if str.size == set.size 28 | for item in set[i] 29 | join_sets set, i + 1, str + item.to_s, result 30 | end 31 | result 32 | end 33 | 34 | def phone_options number 35 | set = number.to_s.split('').map {|digit| letters_for_number digit.to_i} 36 | join_sets set 37 | end 38 | 39 | #test 40 | p phone_options '10-34' #["100DG", "100DH", "100DI", "100EG", "100EH", "100EI", "100FG", "100FH", "100FI"] 41 | -------------------------------------------------------------------------------- /src/powerset.rb: -------------------------------------------------------------------------------- 1 | #powerset - subset of set 2 | def powerset(arr) 3 | result = [[]] 4 | arr.each do |num| 5 | (0...arr.size).each do |j| 6 | result << result[j] + [num] 7 | end 8 | end 9 | result 10 | end 11 | 12 | p powerset [1,2,3] #[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]] 13 | -------------------------------------------------------------------------------- /src/product-of-array.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} nums 2 | # @return {Integer[]} 3 | def product_except_self(nums) 4 | left = Array.new(nums.size) 5 | right = Array.new(nums.size) 6 | ans = Array.new(nums.size) 7 | 8 | (0...nums.size).each do |i| 9 | if i == 0 10 | left[i] = nums[i] 11 | else 12 | left[i] = nums[i] * left[i - 1] 13 | end 14 | end 15 | 16 | (nums.size - 1).downto(0).each do |i| 17 | if i == nums.size - 1 18 | right[i] = nums[i] 19 | else 20 | right[i] = nums[i] * right[i + 1] 21 | end 22 | end 23 | 24 | (0...nums.size).each do |i| 25 | l = i == 0 ? 1 : left[i - 1] 26 | r = i == nums.size - 1 ? 1 : right[i + 1] 27 | ans[i] = l * r 28 | end 29 | ans 30 | end 31 | 32 | p product_except_self([1,2]) -------------------------------------------------------------------------------- /src/quicksort.rb: -------------------------------------------------------------------------------- 1 | def partition arr, lo, hi 2 | i,j = lo+1,hi 3 | while true 4 | i+=1 while arr[i] <= arr[lo] and i < hi 5 | j-=1 while arr[j] > arr[lo] and j > lo 6 | break if i>=j 7 | arr[i], arr[j] = arr[j], arr[i] #swap 8 | end 9 | arr[lo], arr[j] = arr[j], arr[lo] 10 | j 11 | end 12 | 13 | def quicksort arr, lo = 0, hi = nil 14 | hi ||= arr.size - 1 15 | return arr if hi <= lo 16 | pivot = partition arr, lo, hi 17 | quicksort arr, lo, pivot-1 18 | quicksort arr, pivot+1, hi 19 | end 20 | 21 | #test 22 | p quicksort [3,4,2,6,56,14,33,1,9,6] #[1, 2, 3, 4, 6, 6, 9, 14, 33, 56] -------------------------------------------------------------------------------- /src/reverse-linked-list.rb: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode 3 | # attr_accessor :val, :next 4 | # def initialize(val) 5 | # @val = val 6 | # @next = nil 7 | # end 8 | # end 9 | 10 | # @param {ListNode} head 11 | # @return {ListNode} 12 | def reverse_list(head) 13 | cur = head; prev = nil 14 | while cur 15 | nxt = cur.next 16 | cur.next = prev 17 | 18 | prev = cur 19 | cur = nxt 20 | end 21 | return prev 22 | end 23 | -------------------------------------------------------------------------------- /src/reverse-string-inplace.rb: -------------------------------------------------------------------------------- 1 | # @param {String} s 2 | # @return {String} 3 | def reverse_string(s) 4 | return nil unless s 5 | return '' if s.empty? 6 | (0...s.size/2).each do |i| 7 | s[i], s[s.size - 1 - i] = s[s.size - 1 - i], s[i] 8 | end 9 | return s 10 | end 11 | 12 | p reverse_string('helwo') -------------------------------------------------------------------------------- /src/rmq.rb: -------------------------------------------------------------------------------- 1 | # range minimum query, sparse table algorithm. O(nlogn) preprocess time 2 | class RMQ 3 | def initialize(arr) 4 | n = arr.size 5 | @rmq = [arr] 6 | 1.upto(n) do |j| 7 | break if 2**j > n 8 | cur = Array.new(n) 9 | 0.upto(n-2**j){ |i| cur[i] = [@rmq[j-1][i], @rmq[j-1][i+2**(j-1)]].min } 10 | @rmq << cur 11 | end 12 | end 13 | 14 | def query(l, r) 15 | d = 0 16 | d += 1 while 2**(d+1) < r - l + 2 17 | [@rmq[d][l], @rmq[d][r+1-2**d]].min 18 | end 19 | end 20 | 21 | # test 22 | 10.times do 23 | n = rand(1000) 24 | arr = [*0..n].shuffle 25 | rmq = RMQ.new(arr) 26 | 27 | 100.times do 28 | l, r = [rand(n), rand(n)].sort 29 | min = arr[l..r].min 30 | cur_min = rmq.query(l, r) 31 | if min != cur_min 32 | p "error!" 33 | p arr.join(' ') 34 | p "query on range [#{l}, #{r}], minimum should be #{min}, found #{cur_min}!" 35 | exit 1 36 | end 37 | end 38 | end 39 | 40 | p "ok" 41 | -------------------------------------------------------------------------------- /src/shell_sort.rb: -------------------------------------------------------------------------------- 1 | def shell_sort a 2 | n = a.size 3 | gap = 1 4 | gap = gap * 3 + 1 while gap < n / 3 5 | while gap > 0 6 | gap.upto(n-1) do |i| 7 | j = i 8 | ai = a[i] 9 | while j >= gap && ai < a[j-gap] 10 | a[j] = a[j-gap] 11 | j -= gap 12 | end 13 | a[j] = ai 14 | end 15 | gap /= 3 16 | end 17 | a 18 | end 19 | 20 | p shell_sort [*1..20].shuffle 21 | -------------------------------------------------------------------------------- /src/simlify_path.rb: -------------------------------------------------------------------------------- 1 | # @param {String} path 2 | # @return {String} 3 | def simplify_path(path) 4 | dirs = path.split("/") 5 | stack = [] 6 | 7 | dirs.each do |dir| 8 | next if dir.empty? || dir == "." 9 | if dir == ".." 10 | stack.pop unless stack.empty? 11 | else 12 | stack.push(dir) 13 | end 14 | end 15 | 16 | "/" + stack.join("/") 17 | end 18 | 19 | p simplify_path("/a/./b/../../c/") # => "/c" 20 | -------------------------------------------------------------------------------- /src/sq_root.rb: -------------------------------------------------------------------------------- 1 | #https://en.wikipedia.org/wiki/Newton%27s_method 2 | def sqr_root x, accuracy = 0.0001, round = 3 3 | return 'error' if x < 0 4 | a,b = 1.to_f,x.to_f 5 | while (b-a).abs > accuracy 6 | b = (a+b) / 2 7 | a = x/b 8 | end 9 | b.round round 10 | end 11 | 12 | #test 13 | p sqr_root 0.4 #0.632 14 | p sqr_root 100 #10 15 | p sqr_root 12 #3.464 -------------------------------------------------------------------------------- /src/stocks.rb: -------------------------------------------------------------------------------- 1 | #Write an efficient function that takes stock_prices_yesterday and returns the best profit I could have made from 1 purchase and 1 sale of 1 Apple stock yesterday. 2 | 3 | # @param {Integer[]} prices 4 | # @return {Integer} 5 | def max_profit(prices) 6 | return 0 unless prices.size > 1 7 | l, r, max_prof = 0, 1, 0 8 | while r < prices.size 9 | max_prof = [prices[r] - prices[l], max_prof].max 10 | l = r if prices[r] <= prices[l] 11 | r+=1 12 | end 13 | max_prof 14 | end 15 | 16 | p [10, 7, 5, 8, 11, 9] # => 6 -------------------------------------------------------------------------------- /src/suffix_trees.txt: -------------------------------------------------------------------------------- 1 | suffix trees (liner time): 2 | - longest substring in string (the deepest node) 3 | - longest common substring of 2 strings (LCA of two nodes) 4 | - number of times substring occure in string (follow the path and see how many childs the node have) 5 | - longest common subsequence of two strings (concat the strings, get the deepest one who contains the two strings) 6 | 7 | KMP: 8 | - liner time finding substring in string (substring search) 9 | 10 | TODO: implement suffix tree. -------------------------------------------------------------------------------- /src/sugget_index_in_array.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | Given an unsorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. 3 | You may assume no duplicates in the array. 4 | Here are few examples. 5 | [1,3,5,6], 5 → 2 6 | [1,5,3,6], 2 → 1 7 | [1,3,5,6], 7 → 4 8 | [3,1,6,5], 0 → 0 9 | =end 10 | 11 | def search_insert(nums, target) 12 | return 0 if !nums or nums.empty? 13 | lo = 0; hi = nums.size-1 14 | while hi > lo 15 | k = quicksort(nums,lo,hi,target) 16 | if target == nums[k] 17 | return k 18 | elsif target > nums[k] 19 | lo=k+1; 20 | else # target < nums[k] 21 | hi=k-1 22 | end 23 | end 24 | k 25 | end 26 | 27 | def quicksort(arr, lo, hi, k) 28 | i = lo+1; j = hi; 29 | while true 30 | i+=1 while arr[i] <= arr[lo] and i < hi 31 | j-=1 while arr[j] > arr[lo] and j > lo 32 | break if i >= j 33 | arr[i], arr[j] = arr[j], arr[i] 34 | end 35 | arr[lo], arr[j] = arr[j], arr[lo] 36 | j 37 | end 38 | -------------------------------------------------------------------------------- /src/target_sum.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} nums 2 | # @param {Integer} s 3 | # @return {Integer} 4 | def find_target_sum_ways(nums, s, i = 0, dp = {}) 5 | return dp["#{s}-#{i}"] if dp["#{s}-#{i}"] 6 | if i == nums.size 7 | return dp["#{s}-#{i}"] = s == 0 ? 1 : 0 8 | end 9 | 10 | return dp["#{s}-#{i}"] ||= 11 | find_target_sum_ways(nums, s + nums[i], i+1, dp) + 12 | find_target_sum_ways(nums, s - nums[i], i+1, dp) 13 | end 14 | 15 | p find_target_sum_ways([1, 1, 1, 1, 1], 3) # => 5 -------------------------------------------------------------------------------- /src/tarjan.rb: -------------------------------------------------------------------------------- 1 | class Node 2 | attr_accessor :name, :idx, :lowlink, :on_stack 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def ==(other) 9 | if other.class == Node 10 | other.name == self.name 11 | elsif other.class == String 12 | other == self.name 13 | else 14 | false 15 | end 16 | end 17 | 18 | def to_s 19 | @name 20 | end 21 | end 22 | 23 | class Edge 24 | attr_accessor :from, :to 25 | 26 | def initialize(node_from, node_to) 27 | @from, @to = node_from, node_to 28 | end 29 | 30 | def ==(other) 31 | self.from == other.from && self.to == other.to 32 | end 33 | 34 | def to_s 35 | "(#{from.to_s} -> #{to.to_s})" 36 | end 37 | end 38 | 39 | class Graph 40 | attr_accessor :nodes, :edges, :adjmt 41 | 42 | def initialize(load_dict = {}) 43 | @nodes = [] 44 | @edges = [] 45 | @adjmt = {} 46 | 47 | if load_dict.size > 0 && load_dict.class == Hash 48 | load_dict.each do |v, edges| 49 | node_from = self.add_node(v) 50 | adjmt[node_from] = [] 51 | edges.each do |w| 52 | node_to = self.add_node(w) 53 | adjmt[node_from] << node_to 54 | self.add_edge(v, w) 55 | end 56 | end 57 | end 58 | end 59 | 60 | def add_node(node_name) 61 | if nodes.index(node_name) 62 | return nodes[nodes.index(node_name)] 63 | else 64 | node = Node.new(node_name) 65 | nodes << node 66 | return node 67 | end 68 | end 69 | 70 | def add_edge(node_name_from, node_name_to) 71 | node_from = nodes[nodes.index(node_name_from)] 72 | node_to = nodes[nodes.index(node_name_to)] 73 | edges << Edge.new(node_from, node_to) 74 | end 75 | end 76 | 77 | class Tarjan 78 | attr_accessor :graph, :idx, :stack, :sccs 79 | 80 | def initialize(graph) 81 | if graph.class == Graph 82 | @graph = graph 83 | elsif graph.class == Hash 84 | @graph = Graph.new(graph) 85 | end 86 | 87 | if graph != nil 88 | @idx = 0 89 | @stack = [] 90 | 91 | # Runs Tarjan 92 | # Set all node index to None 93 | self.graph.nodes.each do |v| 94 | v.idx = nil 95 | end 96 | 97 | @sccs = [] 98 | self.graph.nodes.each do |v| 99 | if v.idx == nil 100 | self.strongconnect(v, sccs) 101 | end 102 | end 103 | end 104 | end 105 | 106 | def strongconnect(v, sccs) 107 | # Set the depth index for v to the smallest unused index 108 | v.idx = self.idx 109 | v.lowlink = self.idx 110 | self.idx += 1 111 | stack << v 112 | v.on_stack = true 113 | 114 | if graph.adjmt[v] != nil 115 | # Consider successors of v 116 | graph.adjmt[v].each do |w| 117 | if w.idx == nil 118 | # Successor w has not yet been visited; recurse on it 119 | self.strongconnect(w, sccs) 120 | v.lowlink = [v.lowlink, w.lowlink].min 121 | elsif w.on_stack 122 | # Successor w is in stack S and hence in the current SCC 123 | # If w is not on stack, then (v, w) is a cross-edge in the DFS tree and must be ignored 124 | # Note: The next line may look odd - but is correct. 125 | # It says w.index not w.lowlink; that is deliberate and from the original paper 126 | v.lowlink = [v.lowlink, w.idx].min 127 | end 128 | end 129 | end 130 | 131 | # If v is a root node, pop the stack and generate an SCC 132 | if v.lowlink == v.idx 133 | # start a new strongly connected component 134 | scc = [] 135 | while true 136 | w = stack.pop() 137 | w.on_stack = false 138 | scc << w 139 | if w == v 140 | break 141 | end 142 | end 143 | sccs << scc 144 | end 145 | end 146 | end 147 | 148 | # Graph from https://en.wikipedia.org/wiki/File:Scc.png 149 | example = { 150 | 'A' => ['B'], 151 | 'B' => ['C', 'E', 'F'], 152 | 'C' => ['D', 'G'], 153 | 'D' => ['C', 'H'], 154 | 'E' => ['A', 'F'], 155 | 'F' => ['G'], 156 | 'G' => ['F'], 157 | 'H' => ['D', 'G'] 158 | } 159 | 160 | g = Tarjan.new(example) 161 | print g.sccs.map{|scc| scc.map{|v| v.to_s}}.to_s + "\n" 162 | 163 | # Graph from https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm#/media/File:Tarjan%27s_Algorithm_Animation.gif 164 | example = { 165 | 'A' => ['E'], 166 | 'B' => ['A'], 167 | 'C' => ['B', 'D'], 168 | 'D' => ['C'], 169 | 'E' => ['B'], 170 | 'F' => ['B', 'E', 'G'], 171 | 'G' => ['F', 'C'], 172 | 'H' => ['G', 'H', 'D'] 173 | } 174 | g = Tarjan.new(example) 175 | print g.sccs.map{|scc| scc.map{|v| v.to_s}}.to_s + "\n" 176 | -------------------------------------------------------------------------------- /src/top-k-elements.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} nums 2 | # @param {Integer} k 3 | # @return {Integer[]} 4 | def top_k_frequent(nums, k) 5 | buckets = Array.new(nums.size) 6 | h = {} 7 | #store counts 8 | nums.each do |n| 9 | h[n] = h[n] ? h[n] + 1 : 1 10 | end 11 | #move cunts to buckets 12 | h.each do |number, count| 13 | buckets[count] = [] unless buckets[count] 14 | buckets[count] << number 15 | end 16 | #get top k elements 17 | ans = [] 18 | (buckets.size - 1).downto(0).each do |i| 19 | if buckets[i] 20 | buckets[i].each do |n| 21 | ans << n if ans.size < k 22 | end 23 | end 24 | end 25 | 26 | return ans 27 | end 28 | 29 | p top_k_frequent([1,2], 2) -------------------------------------------------------------------------------- /src/towers_of_hanoi_with_stack.rb: -------------------------------------------------------------------------------- 1 | def towers(list) 2 | while !list.empty? 3 | n, src, dst, aux = list.pop 4 | if n == 1 5 | puts "Move disk from #{src} to #{dst}" 6 | else 7 | list.push [n-1, aux, dst, src] 8 | list.push [1, src, dst, aux] 9 | list.push [n-1, src, aux, dst] 10 | end 11 | end 12 | end 13 | 14 | list = [] 15 | list.push([3, "a", "c", "b"]) 16 | towers(list) 17 | -------------------------------------------------------------------------------- /src/unique_bsts.rb: -------------------------------------------------------------------------------- 1 | def unique_bsts(n) 2 | a = Array.new(n+1, 0) 3 | a[0] = a[1] = 1 4 | 5 | (2..n).each do |i| 6 | (1..i).each do |j| 7 | a[i] += (a[j-1] * a[i-j]) 8 | end 9 | end 10 | 11 | return a[n] 12 | end 13 | 14 | p unique_bsts(4) # => 14 -------------------------------------------------------------------------------- /src/wiggle_subsequence.rb: -------------------------------------------------------------------------------- 1 | # @param {Integer[]} nums 2 | # @return {Integer} 3 | def wiggle_max_length(nums) 4 | return nums.size if nums.size < 2 5 | up = nil 6 | i = 1; res = 0 7 | while i < nums.size 8 | while i < nums.size and ( 9 | (up == nil and nums[i] == nums[i-1]) or 10 | (up == true && nums[i] >= nums[i-1]) or 11 | (up == false && nums[i] <= nums[i-1])) 12 | i += 1 13 | end 14 | up = (up == nil and i < nums.size) ? nums[i] > nums[i-1] : !up 15 | res += 1 16 | end 17 | 18 | return res 19 | end 20 | 21 | p wiggle_max_length([1,17,5,10,13,15,10,5,16,8]) # => 7 22 | --------------------------------------------------------------------------------