├── Gemfile ├── Gemfile.lock ├── README.md ├── lib ├── b_tree.rb ├── bst.js ├── bst.rb ├── ch10_btree.rb ├── ch12_search.rb ├── ch13_hash.rb ├── ch14_sorting.rb ├── ch15_bst.rb ├── ch16_recursion.rb ├── ch17_dp.rb ├── ch18_greedy.rb ├── ch18_invariants.rb ├── ch19_graph.rb ├── ch6_array.rb ├── ch7_string.rb ├── ch8_linked_list.rb ├── ch9_stack.rb ├── linked_list.rb └── myqueue.rb └── spec ├── ch6_array_spec.rb ├── ch7_string_spec.rb ├── ch8_linked_list_spec.rb └── ch9_stack_spec.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem "rspec", "~> 3.5.0" 3 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | diff-lcs (1.2.5) 5 | rspec (3.5.0) 6 | rspec-core (~> 3.5.0) 7 | rspec-expectations (~> 3.5.0) 8 | rspec-mocks (~> 3.5.0) 9 | rspec-core (3.5.4) 10 | rspec-support (~> 3.5.0) 11 | rspec-expectations (3.5.0) 12 | diff-lcs (>= 1.2.0, < 2.0) 13 | rspec-support (~> 3.5.0) 14 | rspec-mocks (3.5.0) 15 | diff-lcs (>= 1.2.0, < 2.0) 16 | rspec-support (~> 3.5.0) 17 | rspec-support (3.5.0) 18 | 19 | PLATFORMS 20 | ruby 21 | 22 | DEPENDENCIES 23 | rspec (~> 3.5.0) 24 | 25 | BUNDLED WITH 26 | 1.12.5 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elements of Programming Interviews in "Ruby" 2 | 3 | [Book: Elements of Programming Interviews in Java](https://www.amazon.com/gp/product/1517435803/) 4 | 5 | This repo is mainly translating the book "Elements of Programming Interviews in Java" from Java to Ruby. 6 | I also write couple extra classes i.e. bst, b_tree, linked_list for testing purposes. 7 | 8 | [Chap 6: Array](lib/ch6_array.rb) 9 | 10 | [Chap 7: String](lib/ch7_string.rb) 11 | 12 | [Chap 8: Linked List](lib/ch8_linked_list.rb) 13 | 14 | [Chap 9: Stack](lib/ch9_stack.rb) 15 | 16 | [Chap 10: Binary Tree](lib/ch10_btree.rb) 17 | 18 | [Chap 11: Heap](lib/ch11_heap.rb) (Working) 19 | 20 | [Chap 12: Search](lib/ch12_search.rb) 21 | 22 | [Chap 13: Hash](lib/ch13_hash.rb) 23 | 24 | [Chap 14: Sorting](lib/ch14_sorting.rb) 25 | 26 | [Chap 15: Binary Search Tree](lib/ch15_bst.rb) 27 | 28 | [Chap 16: Recursion](lib/ch16_recursion.rb) 29 | 30 | [Chap 17: Dynamic Programming](lib/ch17_dp.rb) 31 | 32 | [Chap 18: Greedy Algorithms](lib/ch18_greedy.rb) 33 | 34 | [Chap 18: Invariants](lib/ch18_invariants.rb) 35 | 36 | [Chap 19: Graph](lib/ch19_graph.rb) (Working) 37 | -------------------------------------------------------------------------------- /lib/b_tree.rb: -------------------------------------------------------------------------------- 1 | class BTreeNode 2 | 3 | def initialize(value) 4 | @data = value 5 | @right = nil 6 | @left = nil 7 | end 8 | 9 | def add_right(value) 10 | right = BTreeNode.new(value) 11 | this.right = right 12 | end 13 | 14 | def add_left(value) 15 | left = BTreeNode.new(value) 16 | this.left = left 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /lib/bst.js: -------------------------------------------------------------------------------- 1 | function Node(val) { 2 | this.val = val; 3 | this.left = null; 4 | this.right = null; 5 | } 6 | 7 | function BinarySearchTree(root = null) { 8 | this.root = root; 9 | } 10 | 11 | BinarySearchTree.prototype.insert = function (val) { 12 | let curr = this.root; 13 | let newNode = new Node(val); 14 | while (curr !== null) { 15 | if (curr.val > val) { 16 | if (curr.left !== null) { 17 | curr = curr.left; 18 | } else { 19 | curr.left = newNode; 20 | break; 21 | } 22 | } else { 23 | if (curr.right !== null) { 24 | curr = curr.right; 25 | } else { 26 | curr.right = newNode; 27 | break; 28 | } 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lib/bst.rb: -------------------------------------------------------------------------------- 1 | class BSTNode 2 | attr_accessor :left, :right 3 | attr_reader :value 4 | 5 | def initialize(value = nil, left = nil, right = nil) 6 | @value = value 7 | @left = left 8 | @right = right 9 | end 10 | end 11 | 12 | class BinarySearchTree 13 | attr_accessor :root 14 | def initialize 15 | @root = nil 16 | end 17 | 18 | # def insert(value) 19 | # if !@root 20 | # @root = BSTNode.new(value) 21 | # return 22 | # end 23 | # 24 | # BinarySearchTree.insert!(@root, value) 25 | # end 26 | 27 | def find(value) 28 | BinarySearchTree.find!(@root, value) 29 | end 30 | 31 | def inorder 32 | BinarySearchTree.inorder!(@root) 33 | end 34 | 35 | def postorder 36 | BinarySearchTree.postorder!(@root) 37 | end 38 | 39 | def preorder 40 | BinarySearchTree.preorder!(@root) 41 | end 42 | 43 | def height 44 | BinarySearchTree.height!(@root) 45 | end 46 | 47 | def min 48 | BinarySearchTree.min(@root) 49 | end 50 | 51 | def max 52 | BinarySearchTree.max(@root) 53 | end 54 | 55 | def delete(value) 56 | @root = BinarySearchTree.delete!(@root, value) 57 | end 58 | 59 | def insert(value) 60 | unless @root 61 | @root = BSTNode.new(value) 62 | return @root 63 | end 64 | node = @root 65 | until node 66 | parent = node 67 | if value < node.value 68 | node = node.left 69 | elsif value > node.value 70 | node = node.right 71 | else 72 | p "Duplicated Key is inserted" 73 | raise "Duplicated Key is inserted" 74 | return node 75 | end 76 | end 77 | new_node = BSTNode.new(value) 78 | if value < parent.value 79 | parent.left = new_node 80 | else 81 | parent.right = new_node 82 | end 83 | new_node 84 | end 85 | 86 | def self.insert!(node, value) 87 | return BSTNode.new(value) unless node 88 | if value <= node.value 89 | node.left = self.insert!(node.left, value) 90 | else 91 | node.right = self.insert!(node.right, value) 92 | end 93 | node 94 | end 95 | 96 | def self.find!(node, value) 97 | return nil unless node 98 | return node if node.value == value 99 | if value < node.value 100 | return self.find!(node.left, value) 101 | end 102 | return self.find!(node.right, value) 103 | end 104 | 105 | def self.preorder!(node) 106 | return [] unless node 107 | arr = [node.value] 108 | arr += self.preorder!(node.left) if node.left 109 | arr += self.preorder!(node.right) if node.right 110 | arr 111 | end 112 | 113 | def self.inorder!(node) 114 | return [] unless node 115 | arr = [] 116 | arr += self.preorder!(node.left) if node.left 117 | arr << node.value 118 | arr += self.preorder!(node.right) if node.right 119 | arr 120 | end 121 | 122 | def self.postorder!(node) 123 | return [] unless node 124 | arr = [] 125 | arr += self.postorder!(node.left) if node.left 126 | arr += self.postorder!(node.right) if node.right 127 | arr << node.value 128 | arr 129 | end 130 | 131 | def self.height!(node) 132 | return -1 unless node 133 | 1 + [self.height!(node.left), self.height!(node.right)].max 134 | end 135 | 136 | def self.max(node) 137 | return nil unless node 138 | return node unless node.right 139 | self.max(node.right) 140 | end 141 | 142 | def self.min(node) 143 | return nil unless node 144 | return node unless node.left 145 | self.min(node.left) 146 | end 147 | 148 | def self.delete_min!(node) 149 | return nil unless node 150 | return node.right unless node.left 151 | node.left = BinarySearchTree.delete_min!(node.left) 152 | node 153 | end 154 | 155 | def self.delete!(node, value) 156 | return nil unless node 157 | if value < node.value 158 | node.left = self.delete!(node.left, value) 159 | elsif value > node.value 160 | node.right = self.delete!(node.right, value) 161 | else 162 | return node.left unless node.right 163 | return node.right unless node.left 164 | temp = node 165 | node = temp.right.min 166 | node.right = BinarySearchTree.delete_min!(temp.right) 167 | node.left = temp.left 168 | end 169 | node 170 | end 171 | end 172 | A = BSTNode.new(19) 173 | B = BSTNode.new(7) 174 | C = BSTNode.new(3) 175 | D = BSTNode.new(2) 176 | E = BSTNode.new(5) 177 | F = BSTNode.new(11) 178 | G = BSTNode.new(17) 179 | H = BSTNode.new(13) 180 | I = BSTNode.new(43) 181 | J = BSTNode.new(23) 182 | K = BSTNode.new(37) 183 | L = BSTNode.new(29) 184 | M = BSTNode.new(31) 185 | N = BSTNode.new(43) 186 | O = BSTNode.new(47) 187 | P = BSTNode.new(53) 188 | A.left = B 189 | A.right = I 190 | B.left = C 191 | B.right = F 192 | C.left = D 193 | C.right = E 194 | F.right = G 195 | G.left = H 196 | I.left = J 197 | I.right = O 198 | J.right = K 199 | K.left = L 200 | K.right = N 201 | L.right = M 202 | O.right = P 203 | bst = BinarySearchTree.new 204 | bst.root = A 205 | -------------------------------------------------------------------------------- /lib/ch10_btree.rb: -------------------------------------------------------------------------------- 1 | class BSTNode 2 | attr_accessor :left, :right 3 | attr_reader :data 4 | 5 | def initialize(data) 6 | @data = data 7 | @left = nil 8 | @right = nil 9 | end 10 | end 11 | 12 | 13 | # 10.2 Check if a tree is symmetric 14 | def symmetric_tree?(root) 15 | return true unless root 16 | if root.right.nil? || root.left.nil? 17 | return false 18 | else 19 | return check_symmetric(root.left, root.right) 20 | end 21 | return false 22 | end 23 | 24 | def check_symmetric(left, right) 25 | return left.data == right.data && 26 | check_symmetric(left.left, right.right) && 27 | check_symmetric(left.right, right.left) 28 | end 29 | 30 | # 10.4 if Node has parent, find LCA (Lowest common ancestor) 31 | # Time O(h), Space O(1) 32 | def parent_lca(node1, node2) 33 | # Find the min_depth 34 | n1_depth = find_depth(node1) 35 | n2_depth = find_depth(node2) 36 | if n2_depth > n1_depth 37 | node1, node2 = node2, node1 38 | end 39 | 40 | # Make sure 2 nodes has the same depth 41 | diff_depth = (n1_depth - n2_depth).abs 42 | while diff_depth > 0 43 | node1 = node1.parent 44 | diff_depth -= 1 45 | end 46 | 47 | until node1.parent.nil? 48 | return node1 if node1 == node2 49 | node1 = node1.parent 50 | node2 = node2.parent 51 | end 52 | 53 | nil 54 | end 55 | 56 | def find_depth(node) 57 | depth = 0 58 | until node.parent.nil? 59 | depth += 1 60 | end 61 | return depth 62 | end 63 | 64 | # 10.5 sum the root to leaf binary tree 65 | 66 | def sum_binary(root) 67 | return partial_sum(root, 0) 68 | end 69 | 70 | def partial_sum(node, curr_sum) 71 | return 0 if node.nil? 72 | curr_sum = curr_sum * 2 + node.data 73 | if node.left.nil? && node.right.nil? 74 | return curr_sum 75 | else 76 | return partial_sum(node.left, curr_sum) + partial_sum(node.right, curr_sum) 77 | end 78 | end 79 | 80 | 81 | # 10.6, similar to 10.5, find the target sum 82 | 83 | def target_sum(root, target_sum) 84 | return partial_target_sum(root, target_sum, 0) 85 | end 86 | 87 | def partial_target_sum(node, target_sum, curr_sum) 88 | return false if node.nil? 89 | curr_sum += node.data 90 | return true if curr_sum == target_sum 91 | return partial_target_sum(node.left, target_sum, curr_sum) || partial_target_sum(right, target_sum, curr_sum) 92 | end 93 | 94 | # 10.7 BST inorder 95 | def bst_inorder(root) 96 | result = [] 97 | stack = [] 98 | curr = root 99 | until curr.nil? && result.empty? 100 | if !curr.nil? 101 | # Going left 102 | stack.push(curr) 103 | curr = curr.left 104 | else 105 | # Going up 106 | curr = stack.pop 107 | result << curr 108 | 109 | # Going right 110 | curr = curr.right 111 | 112 | end 113 | end 114 | result 115 | end 116 | 117 | # 10.8 BST preorder 118 | def bst_preorder(root) 119 | result = [] 120 | stack = [root] 121 | until stack.empty? 122 | curr = stack.pop 123 | unless curr.nil? 124 | result << curr 125 | stack.push(curr.right) 126 | stack.push(curr.left) 127 | end 128 | end 129 | result 130 | end 131 | 132 | # 10.9 Find Kth node inorder BTree 133 | def find_kth_node(root, k) 134 | 135 | end 136 | 137 | # 10.10 find successor inorder BTree 138 | def find_successor(node) 139 | iter = node 140 | 141 | # find the left most node in right sub tree 142 | unless iter.right.nil? 143 | iter = iter.right 144 | until iter.left.nil? 145 | iter = lter.left 146 | end 147 | return iter 148 | end 149 | 150 | # A 151 | # / 152 | # B 153 | # / \ 154 | # C D => C,B,D,A => A is D's successor 155 | # find the closest ancestor whose left subtree contains node. 156 | while iter.parent != null && iter.parent.right == iter 157 | iter = iter.parent 158 | end 159 | return iter.parent 160 | end 161 | -------------------------------------------------------------------------------- /lib/ch12_search.rb: -------------------------------------------------------------------------------- 1 | # 12.1 search a sorted array for first occurrence of k 2 | # arr = [0,4,5,5,5,7,7,9,10,11,12], k = 5, out = 2 3 | # Time: O(logn) 4 | def search_first_of_k(arr, k) 5 | result = nil 6 | left = 0 7 | right = arr.length - 1 8 | while left <= right 9 | mid = left + (right - left) / 2 10 | if arr[mid] == k 11 | result = mid 12 | right = mid - 1 13 | elsif arr[mid] > k 14 | right = mid - 1 15 | else 16 | left = mid + 1 17 | end 18 | end 19 | result 20 | end 21 | 22 | # 12.3 search the smallest element in a cyclically array 23 | # arr = [5,6,7,8,9,10,1,2], out = index 6 24 | 25 | def search_the_smallest(arr) 26 | mid = nil 27 | left = 0 28 | right = arr.length - 1 29 | while left < right 30 | mid = left + (right - left) / 2 31 | if arr[mid] > arr[right] 32 | left = mid + 1 33 | else 34 | right = mid 35 | end 36 | end 37 | left 38 | end 39 | 40 | # 12.4 find the largest x ,where x^2 < k, and (x + 1)^2 > k 41 | def find_closest_sq_root(k) 42 | left = 0 43 | right = k 44 | while left <= right 45 | mid = left + (right - left) / 2 46 | right = mid - 1 if mid * mid > k 47 | left = mid + 1 if mid * mid <= k 48 | end 49 | return left - 1 50 | end 51 | 52 | # 12.6 Sorted 2D array search 53 | # Time: O(m + n) 54 | def matrix_search(arr, k) 55 | row = 0 56 | col = arr[0].length - 1 57 | while row < arr.length && col >= 0 58 | if arr[row][col] == k 59 | return true 60 | elsif arr[row][col] < k 61 | row += 1 62 | else 63 | col -= 1 64 | end 65 | end 66 | false 67 | end 68 | -------------------------------------------------------------------------------- /lib/ch13_hash.rb: -------------------------------------------------------------------------------- 1 | # 13.1: take in a string and return whether all of it's permutations are palindromes 2 | # Time: O(n) 3 | def per_palindromes?(str) 4 | hash = Hash.new{0} 5 | 6 | # Find the frequency for each char 7 | str.chars.each do |c| 8 | hash[c] += 1 9 | end 10 | 11 | odd_count = 0 12 | hash.each do |key, value| 13 | odd_count += 1 unless value % 2 == 0 14 | return false if odd_count > 1 15 | end 16 | true 17 | end 18 | 19 | # 13.4 LCA with hash 20 | # Time: O(D0 + D1), Space: O(D0 + D1) 21 | def hash_lca(node0, node1) 22 | hash = Hash.new 23 | until node0.nil && node1.nil 24 | unless node0.nil 25 | if hash[node0] 26 | return node0 27 | else 28 | hash[node0] = 1 29 | end 30 | node0 = node0.parent 31 | end 32 | 33 | unless node1.nil 34 | if hash[node1] 35 | return node1 36 | else 37 | hash[node1] = 1 38 | end 39 | node1 = node1.parent 40 | end 41 | end 42 | nil 43 | end 44 | 45 | # 13.6 Find the nearest repeated entries in an array 46 | # Time: O(n) 47 | def find_nearest_repetition(arr) 48 | max_dis = nil 49 | hash = {} 50 | arr.each_with_index do |el, idx| 51 | if hash[el].nil? 52 | hash[el] = idx 53 | else 54 | distance = idx - hash[el] 55 | max_dis = distance if max_dis.nil? || distance < max_dis 56 | end 57 | end 58 | max_dis 59 | end 60 | 61 | # 13.10 Find the length of a longest contain interval 62 | =begin 63 | arr = [3,7,1,2,0,9,11] 64 | range = 0,1,2,3 => 4 65 | =end 66 | require 'set' 67 | def longest_contained_range(arr) 68 | unprocessed_set = arr.to_set 69 | range = 0 70 | until unprocessed_set.empty? 71 | itr = unprocessed_set.each 72 | el = itr.next 73 | unprocessed_set.delete(el) 74 | 75 | # Find the continuous numbers from the arr 76 | lower_bound = el - 1 77 | while unprocessed_set.include?(lower_bound) 78 | unprocessed_set.delete(lower_bound) 79 | lower_bound -= 1 80 | end 81 | 82 | upper_bound = el + 1 83 | while unprocessed_set.include?(upper_bound) 84 | unprocessed_set.delete(upper_bound) 85 | upper_bound += 1 86 | end 87 | range = [range, upper_bound - lower_bound - 1].max 88 | end 89 | range 90 | end 91 | -------------------------------------------------------------------------------- /lib/ch14_sorting.rb: -------------------------------------------------------------------------------- 1 | # 14.1 2 | # Find the intersection of 2 sorted arrays 3 | # Time: O(m + n), space: O(1) 4 | def find_intersection(arr1, arr2) 5 | i = 0 6 | j = 0 7 | out_arr = [] 8 | while i < arr1.length && j < arr2.length 9 | if arr1[i] == arr2[j] 10 | out_arr << arr1[i] 11 | i += 1 12 | j += 1 13 | elsif arr1[i] < arr2[j] 14 | i += 1 15 | else 16 | j += 1 17 | end 18 | end 19 | out_arr 20 | end 21 | 22 | # 14.2 Merge 2 sorted array to the first array, 23 | # assuming the first array has enough empty space at the end of the array. 24 | 25 | # arr1 = [5,13,17,nil,nil,nil,nil,nil], arr2 = [3,7,11,19], m = 3, n = 4 26 | # output arr1 = [3,5,7,11,13,17,19,nil] 27 | def merge_sorted_arrays(arr1, arr2, m, n) 28 | a = m - 1 29 | b = n - 1 30 | curr_idx = m + n - 1 31 | while a >= 0 && b >= 0 32 | if arr1[a] > arr2[b] 33 | arr1[curr_idx] = arr1[a] 34 | curr_idx -= 1 35 | a -= 1 36 | else 37 | arr1[curr_idx] = arr2[b] 38 | curr_idx -= 1 39 | b -= 1 40 | end 41 | end 42 | while b >= 0 43 | arr1[curr_idx] = arr2[b] 44 | curr_idx -= 1 45 | b -= 1 46 | end 47 | return arr1 48 | end 49 | 50 | # 14.10 Find the salary cap 51 | # Time: at Sort, so O(nlogn) 52 | def salary_cap(target, sal_arr) 53 | sal_arr.sort! 54 | unadj_total = 0 55 | sal_arr.each_with_index do |sal, idx| 56 | adj_total = sal * (sal_arr.length - idx) 57 | return (target - unadj_total) / (sal_arr.length - idx) if adj_total + unadj_total >= target 58 | unadj_total += sal 59 | end 60 | nil 61 | end 62 | -------------------------------------------------------------------------------- /lib/ch15_bst.rb: -------------------------------------------------------------------------------- 1 | require 'bst' 2 | 3 | # 15.1 check if a bt a bst 4 | def is_bst?(tree) 5 | return in_bst_range(tree, -999999999, 999999999) 6 | end 7 | 8 | def in_bst_range(tree, min, max) 9 | return true unless tree 10 | return false if tree.data < min || tree.data > max 11 | return is_bst_range(tree.left, min, tree.data) && in_bst_range(tree.right, tree.data, max) 12 | end 13 | 14 | # 15.2 find the first node that is greater than input in BST 15 | # Time: O(h) 16 | def find_first_key(tree, value) 17 | sub_tree = tree 18 | curr = nil 19 | until sub_tree 20 | if sub_tree.data > k 21 | curr = sub_tree 22 | # Go left 23 | sub_tree = sub_tree.left 24 | else 25 | # Go right 26 | sub_tree = sub_tree.right 27 | end 28 | end 29 | curr 30 | end 31 | 32 | # 15.3 Find the K largest elements in BST, reverse order 33 | # Time: O(h + k) 34 | def find_k_largest_bst(tree, k, arr = []) 35 | if !tree.nil? && arr.length < k 36 | find_k_largest_bst(tree.right, k, arr) 37 | if arr.length < k 38 | arr << tree.data 39 | find_k_largest_bst(tree.left, k, arr) 40 | end 41 | end 42 | # p "returning... #{tree.data}" if tree 43 | arr 44 | end 45 | 46 | #15.4 Find the LCA for BST 47 | # Time: O(h) 48 | def find_lca_bst(root, left_node, right_node) 49 | node = root 50 | 51 | # Start from root, looking for a node > left_node.data && < right_node.data 52 | while node.data < left_node.data || node.data > right_node.data 53 | while node.data < left_node.data 54 | node = node.right 55 | end 56 | while node.data > right_node.data 57 | node = node.left 58 | end 59 | end 60 | 61 | # Now node.data >= left_node.data && node.data <= right_node.data, which is the LCA 62 | return node 63 | end 64 | 65 | # 15.5 Reconstruct Preorder BST 66 | # Time: O(n) 67 | def preorder_bst(seq) 68 | $curr_idx = 0 69 | return rebuild_bst_preorder(seq, -9999999, 9999999) 70 | end 71 | 72 | def rebuild_bst_preorder(seq, min, max) 73 | return nil if $curr_idx == seq.length 74 | curr_root = seq[$curr_idx] 75 | return nil if curr_root < min || curr_root > max 76 | 77 | $curr_idx += 1 78 | left = rebuild_bst_preorder(seq, min, curr_root) 79 | right = rebuild_bst_preorder(seq, curr_root, max) 80 | return BSTNode.new(curr_root, left, right) 81 | end 82 | 83 | # 15.7 Generate K number of ABSqrt2, where a & b >=0 84 | # Time: O(n) 85 | 86 | class ABSqrt2 87 | attr_accessor :a, :b 88 | attr_reader :val 89 | def initialize(a, b) 90 | @a = a 91 | @b = b 92 | @val = a + b * Math.sqrt(2) 93 | end 94 | end 95 | 96 | def generate_absqrt2(k) 97 | return [] if k < 1 98 | result = [] 99 | result << ABSqrt2.new(0,0) 100 | i, j = 0, 0 101 | 1.upto(k).each do |n| 102 | result_i_plus_one = ABSqrt2.new(result[i].a + 1, result[i].b) 103 | result_j_plus_one = ABSqrt2.new(result[j].a, result[j].b + 1) 104 | if result_i_plus_one.val < result_j_plus_one.val 105 | i += 1 106 | result << result_i_plus_one 107 | elsif result_i_plus_one.val > result_j_plus_one.val 108 | j += 1 109 | result << result_j_plus_one 110 | else 111 | i += 1 112 | j += 1 113 | result << result_i_plus_one 114 | end 115 | end 116 | result 117 | end 118 | 119 | # 15.9 Build a minimum height BST from a sorted array 120 | # Time: O(n) 121 | def min_height_bst_from_sorted_arr(arr, start = 0, ending = arr.length) 122 | return nil if start >= ending 123 | mid = start + (ending - start) / 2 124 | return BSTNode.new(arr[mid], 125 | min_height_bst_from_sorted_arr(arr, start, mid), 126 | min_height_bst_from_sorted_arr(arr, mid + 1, ending)) 127 | end 128 | 129 | # 15.12 Lookup range for BST 130 | # Time: O(h + m), where h is height and m is the number of the elements within the interval 131 | def range_lookup_bst(node, interval, result = []) 132 | return nil unless node 133 | if node.value >= interval[0] && node.value <= interval[1] 134 | range_lookup_bst(node.left, interval, result) 135 | result << node.value 136 | range_lookup_bst(node.right, interval, result) 137 | elsif node.value < interval[0] 138 | range_lookup_bst(node.right, interval, result) 139 | else 140 | range_lookup_bst(node.left, interval, result) 141 | end 142 | result 143 | end 144 | 145 | # 15.13 Client Credit Info System 146 | class ClientsCreditsInfo 147 | @@offset = 0 148 | def initialize 149 | @client_to_credit = {} 150 | @credit_to_client = BinarySearchTree.new 151 | end 152 | 153 | def insert(id, credit) 154 | remove(id) 155 | @client_to_credit[id] = (credit - @@offset) 156 | node = BSTNode.new(credit - @@offset) 157 | @credit_to_client.insert(node) 158 | end 159 | 160 | def remove(id) 161 | credit = @client_to_credit[id] 162 | @client_to_credit.delete(id) 163 | @credit_to_client.delete(credit) 164 | end 165 | 166 | def lookup(id) 167 | credit = @client_to_credit[id] 168 | return credit.nil? ? nil : credit + @@offset 169 | end 170 | 171 | def add_all(credit) 172 | @@offset += credit 173 | end 174 | 175 | def max 176 | @credit_to_client.max + @@offset 177 | end 178 | -------------------------------------------------------------------------------- /lib/ch16_recursion.rb: -------------------------------------------------------------------------------- 1 | # 16.1 Tower of Hanoi 2 | def compute_tower_of_hanoi(nums) 3 | # Setup 4 | num_pegs = 4 5 | pegs = [] 6 | num_pegs.times do |time| 7 | pegs << [] 8 | end 9 | (nums - 1).downto(0).each do |num| 10 | pegs[0] << num 11 | end 12 | p pegs 13 | compute_toh(nums, pegs, 0,1,2) 14 | end 15 | 16 | def compute_toh(nums, pegs, from, to, via) 17 | if nums > 0 18 | compute_toh(nums - 1, pegs, from, via, to) 19 | pegs[to] << pegs[from].pop 20 | p 'moving from peg ' + from.to_s + ' to peg ' + to.to_s + ' --- Disk ' + pegs[to].last.to_s 21 | compute_toh(nums - 1, pegs, via, to, from) 22 | end 23 | end 24 | 25 | 26 | # 16.2 Generate all non-attacking placements of n-Queens 27 | def n_queens(n) 28 | result = [] 29 | solve_queens(n, 0, [], result) 30 | p result 31 | result 32 | end 33 | 34 | def solve_queens(n, row, colplacement, result) 35 | if row == n 36 | result << colplacement.dup 37 | else 38 | 0.upto(n - 1) do |col| 39 | colplacement << col 40 | solve_queens(n, row + 1, colplacement, result) if is_valid?(colplacement) 41 | colplacement.pop 42 | end 43 | end 44 | return result 45 | end 46 | 47 | def is_valid?(colplacement) 48 | # start 49 | # colplacement[1,2] 50 | # [0,1,0,0] 51 | # [0,0,1,0] 52 | # row_idx = 1 53 | # idx = 0 54 | # end 55 | 56 | row_idx = colplacement.length - 1 57 | 0.upto(row_idx - 1) do |idx| 58 | diff = (colplacement[idx] - colplacement[row_idx]).abs 59 | return false if diff == 0 || diff == row_idx - idx 60 | end 61 | true 62 | end 63 | 64 | # 16.3 Generate permutations 65 | def permutations(input) 66 | result = [] 67 | build_permutations(input, 0, result) 68 | end 69 | 70 | def build_permutations(input, i, result) 71 | if i == (input.length - 1) 72 | result << input.dup 73 | return result 74 | end 75 | j = i 76 | while j < input.length 77 | input[i], input[j] = input[j], input[i] 78 | build_permutations(input, i + 1, result) 79 | input[i], input[j] = input[j], input[i] 80 | j += 1 81 | end 82 | return result 83 | end 84 | 85 | # 16.6 Generate Strings of matched parens 86 | def generate_balanced_parens(num_pairs) 87 | result = [] 88 | build_balanced_parens(num_pairs, num_pairs, "", result) 89 | end 90 | 91 | def build_balanced_parens(num_left_parens_needed, num_right_parens_needed, valid_prefix, result) 92 | if num_left_parens_needed == 0 && num_right_parens_needed == 0 93 | result << valid_prefix 94 | return 95 | end 96 | # Able to insert ( 97 | if num_left_parens_needed > 0 98 | build_balanced_parens(num_left_parens_needed - 1, num_right_parens_needed, valid_prefix + "(", result) 99 | end 100 | 101 | # Able to insert ) 102 | if num_left_parens_needed < num_right_parens_needed 103 | build_balanced_parens(num_left_parens_needed, num_right_parens_needed - 1, valid_prefix + ")", result) 104 | end 105 | result 106 | end 107 | 108 | # 16.7 Generate Palindromic decompositions 109 | # Time: O(n 2^n) 110 | def generate_palindromic_decompositions(input) 111 | result = [] 112 | build_palindromic_compositions(input, 0, [], result) 113 | end 114 | 115 | def build_palindromic_compositions(input, start, partial_partition, result) 116 | # Base case 117 | if start == input.length 118 | result << partial_partition.dup 119 | return 120 | end 121 | 122 | # partition 123 | ending = start + 1 124 | while ending <= input.length 125 | prefix = input[start...ending] 126 | if is_palindrome?(prefix) 127 | partial_partition << prefix 128 | build_palindromic_compositions(input, ending, partial_partition, result) 129 | partial_partition.pop 130 | end 131 | ending += 1 132 | end 133 | result 134 | end 135 | 136 | def is_palindrome?(str) 137 | 0.upto(str.length / 2).each do |idx| 138 | return false unless str[idx, 1] == str[str.length - idx - 1, 1] 139 | end 140 | return true 141 | end 142 | 143 | # 16.8 Generate BTree with n nodes 144 | def generate_btree(n) 145 | result = [] 146 | result << nil if n == 0 147 | left_node = 0 148 | while left_node < n 149 | right_node = n - left_node - 1 150 | left_sub_trees = generate_btree(left_node) 151 | right_sub_trees = generate_btree(right_node) 152 | left_sub_trees.each do |left| 153 | right_sub_trees.each do |right| 154 | result << BSTNode.new(n, left, right) 155 | end 156 | end 157 | left_node += 1 158 | end 159 | result 160 | end 161 | 162 | # 16.10 Generate n-bit gray code 163 | # Time: O(2^n) 164 | def graycode(n) 165 | # Base case 166 | if n == 0 167 | return [0] 168 | elsif n == 1 169 | return [0, 1] 170 | end 171 | prev_codes = graycode(n - 1) 172 | leading_bit_one = 1 << (n - 1) 173 | reflection = [] 174 | 175 | # Here we want to reverse to prepend the leading one to prev_codes, 176 | # in order to get the gray codes differ in one place on wrapping around. 177 | (prev_codes.length - 1).downto(0).each do |i| 178 | reflection << (leading_bit_one | prev_codes[i]) 179 | end 180 | prev_codes + reflection 181 | end 182 | -------------------------------------------------------------------------------- /lib/ch17_dp.rb: -------------------------------------------------------------------------------- 1 | # 17.0 Fib seq 2 | # Time: O(n) 3 | def fib_seq(n) 4 | cache = {} 5 | fib(n, cache) 6 | end 7 | 8 | def fib(n, cache) 9 | return n if n < 2 10 | unless cache[n] 11 | cache[n] = fib(n - 1, cache) + fib(n - 2, cache) 12 | end 13 | cache[n] 14 | end 15 | 16 | # 17.0.1 Max sum subarray 17 | def max_subarr(arr) 18 | max = 0 19 | min = 0 20 | sum = 0 21 | arr.each do |el| 22 | sum += el 23 | min = sum if sum < min 24 | max = sum - min if max < sum - min 25 | end 26 | max 27 | end 28 | 29 | # 17.1 Count how many ways of coin change 30 | =begin 31 | Amount 0 1 2 3 4 5 32 | Coin 33 | 1 [1, 1, 1, 1, 1, 1], 34 | 2 [1, 1, 2, 2, 3, 3], 35 | 3 [1, 1, 2, 3, 4, 5] 36 | =end 37 | def coin_change(amount, coins = [1,2,3]) 38 | solution = Array.new(coins.length){Array.new(amount + 1)} 39 | 40 | # Base case are always 1 way, where amount = 0 41 | 0.upto(coins.length - 1).each do |i| 42 | solution[i][0] = 1 43 | end 44 | 45 | # Base case for coin 1, always 0 way 46 | 1.upto(amount).each do |j| 47 | solution[0][j] = 1 48 | end 49 | 50 | 1.upto(coins.length - 1).each do |i| 51 | 1.upto(amount).each do |j| 52 | # check if the amount needed is less than the coin value 53 | if j < coins[i] 54 | # just copy the value from the top 55 | solution[i][j] = solution[i - 1][j]; 56 | else 57 | # reduce the amount by coin value and 58 | # use the subproblem solution (amount-v[i]) and 59 | # add the solution from the top to it 60 | solution[i][j] = solution[i - 1][j] + solution[i][j - coins[i]] 61 | end 62 | end 63 | end 64 | solution 65 | end 66 | 67 | def make_change(target, coins = [25, 10, 5, 1]) 68 | return [] if target == 0 69 | 70 | # Optimization: make sure coins are always sorted descending in 71 | # size. We'll see why later. 72 | coins = coins.sort.reverse 73 | 74 | best_change = nil 75 | coins.each_with_index do |coin, index| 76 | if target < coin 77 | # can't use this coin, it's too big 78 | next 79 | else 80 | remainder = target - coin 81 | end 82 | 83 | best_remainder = make_change(remainder, coins.drop(index)) 84 | 85 | # We may not be able to make the remaining amount of change (e.g., 86 | # if coins doesn't have a 1cent piece), in which case we shouldn't 87 | # use this coin. 88 | if best_remainder.nil? 89 | next 90 | else 91 | this_change = [coin] + best_remainder 92 | end 93 | 94 | # Is this better than anything we've seen so far? 95 | if (best_change.nil? || (this_change.count < best_change.count)) 96 | best_change = this_change 97 | end 98 | end 99 | best_change 100 | end 101 | 102 | # 17.2 CODE NOT WORKING!!!! 103 | # 17.2 Compute Levenshtein distance 104 | # def levenshtein_distance(str_a, str_b) 105 | # dis_arr = Array.new(str_a.length){Array.new(str_b.length){-1}} 106 | # compute_dis(str_a, str_a.length - 1, str_b, str_b.length - 1, dis_arr) 107 | # end 108 | # 109 | # def compute_dis(str_a, idx_a, str_b, idx_b, dis_arr) 110 | # # str_a is empty, so add all str_b char 111 | # if idx_a < 0 112 | # return idx_b + 1 113 | # elsif idx_b < 0 114 | # return idx_a + 1 115 | # end 116 | # 117 | # if dis_arr[idx_a][idx_b] == -1 118 | # if str_a[idx_a, 1] == str_b[idx_b, 1] 119 | # compute_dis(str_a, idx_a - 1, str_b, idx_b - 1, dis_arr) 120 | # else 121 | # sub_last_a_with_last_b = compute_dis(str_a, idx_a - 1, str_b, idx_b - 1, dis_arr) 122 | # add_last = compute_dis(str_a, idx_a, str_b, idx_b - 1, dis_arr) 123 | # delete_last = compute_dis(str_a, idx_a - 1, str_b, idx_b, dis_arr) 124 | # dis_arr[idx_a][idx_b] = 1 + [sub_last_a_with_last_b, add_last, delete_last].min 125 | # end 126 | # end 127 | # p dis_arr 128 | # dis_arr[idx_a][idx_b] 129 | # end 130 | 131 | # 17.3 Count number of ways to go from 0,0 to m,n in a 2-D array 132 | # Time: O(mn) 133 | # Space: O(mn) 134 | def walk_array(m,n) 135 | arr = Array.new(m){Array.new(n)} 136 | result = compute_walk(m - 1, n - 1, arr) 137 | return result 138 | end 139 | 140 | def compute_walk(x, y, arr) 141 | return 1 if x == 0 || y == 0 142 | way_top = x == 0 ? 0 : compute_walk(x - 1, y, arr) 143 | way_left = x == 0 ? 0 : compute_walk(x, y - 1, arr) 144 | arr[x][y] = way_top + way_left 145 | arr[x][y] 146 | end 147 | 148 | # 17.4 Binomial Coefficient 149 | # Time: O(nk) 150 | # Space: O(nk) 151 | def binomial_coefficient(n, k) 152 | arr = Array.new(n + 1){Array.new(k + 1)} 153 | compute_bc(n, k, arr) 154 | end 155 | 156 | # (n,k) = 0 if n == 0 157 | # (n,k) = 1 if k == 0 158 | # (n,k) = 1 if n == k 159 | 160 | def compute_bc(n, k, arr) 161 | return 0 if n == 0 162 | return 1 if n == k || k == 0 163 | unless arr[n][k] 164 | arr[n][k] = compute_bc(n - 1, k, arr) + compute_bc(n - 1, k - 1, arr) 165 | end 166 | arr[n][k] 167 | end 168 | 169 | # 17.6 Knapsack problem 170 | class Item 171 | attr_accessor :value, :weight 172 | 173 | def initialize(value, weight) 174 | @value = value 175 | @weight = weight 176 | end 177 | end 178 | i1 = Item.new(60, 5) 179 | i2 = Item.new(50, 3) 180 | i3 = Item.new(70, 4) 181 | i4 = Item.new(30, 2) 182 | items = [i1, i2, i3, i4] 183 | 184 | =begin 185 | weight 0 1 2 3 4 5 186 | item 187 | i1 [0, 0, 0, 0, 0, 60], 188 | i2 [0, 0, 0, 50, 50, 60], 189 | i3 [0, 0, 0, 50, 70, 70], 190 | i4 [0, 0, 30, 50, 70, 80] 191 | =end 192 | # Time: O(nw) 193 | # Space: O(nw) 194 | def knapsack(items, capacity) 195 | result = Array.new(items.length) {Array.new(capacity + 1)} 196 | 0.upto(items.length - 1).each do |i| 197 | result[i][0] = 0 198 | 1.upto(capacity).each do |j| 199 | without_curr = result[i - 1][j] 200 | 201 | # If current weight is less than current item's weight 202 | if j < items[i].weight 203 | result[i][j] = (i - 1) >= 0 ? without_curr : 0 204 | else 205 | result[i][j] = (i - 1) >= 0 ? 206 | # Max of Current item value + value from same row but Value[j - W[i]], or the value from the top 207 | [without_curr, items[i].value + result[i][j - items[i].weight]].max : 208 | # Current item value + value from same row but Value[j - W[i]] 209 | items[i].value + result[i][j - items[i].weight] 210 | end 211 | end 212 | end 213 | result 214 | end 215 | 216 | # 17.8 Find the minumum weight path in a triangle 217 | =begin 218 | triangle = [['2'], 219 | [4,'4'], 220 | [8,'5',6], 221 | [4,'2',6,2], 222 | [1,5,'2',3,4]] 223 | Min Weight would be 2 + 4 + 5 + 2 + 2 = 15 224 | =end 225 | def min_path_triangle(triangle) 226 | if triangle.empty? 227 | return [] 228 | end 229 | prev_row = triangle.first 230 | 1.upto(triangle.length - 1).each do |i| 231 | curr_row = triangle[i] 232 | 233 | # For the first element 234 | curr_row[0] = prev_row[0] + curr_row[0] 235 | 236 | # Elements in between, exclude the last element 237 | 1.upto(curr_row.length - 2).each do |j| 238 | curr_row[j] = curr_row[j] + [prev_row[j - 1], prev_row[j]].min 239 | end 240 | 241 | # Last element 242 | curr_row[curr_row.length - 1] = curr_row[curr_row.length - 1] + prev_row[prev_row.length - 1] 243 | prev_row = curr_row 244 | end 245 | prev_row.min 246 | end 247 | 248 | # 17.9 pick coins for max gain 249 | # Time: O(n ^ 2) 250 | =begin 251 | coins = [25,5,10,5,10,5,10,25,1,25,1,25,1,25,5,10] 252 | (starts at i) O ...coins... O (ends at j) 253 | F(i, j) = Max{ 254 | Pick(i) + Min{F(i + 2, j), F(i + 1, j - 1)} 255 | Pick(j) + Min{F(i, j - 2), F(i + 1, j - 1)} 256 | }, otherwise 0 257 | =end 258 | def pickup_coins(coins, i = 0, j = coins.length - 1, result = Array.new(coins.length){Array.new(coins.length)}) 259 | return 0 if i > j 260 | max_i = coins[i] + [pickup_coins(coins, i + 2, j, result), pickup_coins(coins, i + 1, j - 1, result)].min 261 | max_j = coins[j] + [pickup_coins(coins, i, j - 2, result), pickup_coins(coins, i + 1, j - 1, result)].min 262 | result[i][j] = [max_i, max_j].max 263 | return result[i][j] 264 | end 265 | 266 | # 17.10 Ways of climbing n stairs, each time can climb 1~max stairs 267 | def ways_climbing_stairs(n, max, result = Array.new(n + 1){0}) 268 | return 1 if n <= 1 269 | if result[n] == 0 270 | i = 1 271 | # result[n] = ways_climbing_stairs(n - 1, max, result) + ways_climbing_stairs(n - 2, max, result) 272 | # + ..... + ways_climbing_stairs(n - n, max, result) 273 | while i <= max && n - i >= 0 274 | result[n] += ways_climbing_stairs(n - i, max, result) 275 | i += 1 276 | end 277 | end 278 | p result 279 | return result[n] 280 | end 281 | 282 | # 17.12 Find the longest non-decreasing sub sequence length 283 | # arr = [0,8,4,12,2,10,6,14,1,9] 284 | # Time: O(n ^ 2) 285 | # Space: O(n) 286 | def longest_nondecreasing_sub_seq(arr) 287 | max_length = Array.new(arr.length) {1} 288 | 1.upto(arr.length - 1).each do |i| 289 | 0.upto(i - 1).each do |j| 290 | if arr[i] >= arr[j] 291 | max_length[i] = [max_length[i], 1 + max_length[j]].max 292 | end 293 | end 294 | end 295 | max_length.max 296 | end 297 | -------------------------------------------------------------------------------- /lib/ch18_greedy.rb: -------------------------------------------------------------------------------- 1 | # 18.1 Assign pair tasks to compute an optimum assignment of tasks 2 | # arr = [5,2,1,6,4,4] 3 | # Time: O(nlogn) 4 | def assign_tasks(arr) 5 | result = [] 6 | # Sort the work hours! 7 | arr.sort! 8 | 0.upto(arr.length / 2 - 1).each do |idx| 9 | result << [arr[idx], arr[arr.length - 1 - idx]] 10 | end 11 | result 12 | end 13 | 14 | # 18.3 Find the minimum sized set of numbers that covers all the intervals 15 | # intervals = [[3,4],[1,2],[4,5],[2,3],[6,7],[2,3]] 16 | # Time: O(nlogn) 17 | def min_set_of_intervals(intervals) 18 | intervals.sort! 19 | # Get the right point from the first set 20 | result = [intervals[0][1]] 21 | 1.upto(intervals.length - 1).each do |idx| 22 | # The current right endpoint will not cover any more intervals 23 | if intervals[idx][0] > result.last 24 | result << intervals[idx][1] 25 | end 26 | end 27 | result 28 | end 29 | -------------------------------------------------------------------------------- /lib/ch18_invariants.rb: -------------------------------------------------------------------------------- 1 | # 18.4.0 Two Sum for a sorted array 2 | # Time: O(n) 3 | # Space: O(1) 4 | # arr = [-2,1,2,4,7,11] 5 | def has_two_sum?(arr, target) 6 | i = 0 7 | j = arr.length - 1 8 | while i < j 9 | if arr[i] + arr[j] == target 10 | return true 11 | elsif arr[i] + arr[j] < target 12 | i += 1 13 | else 14 | j -= 1 15 | end 16 | end 17 | false 18 | end 19 | 20 | # 18.4 Three sum for un-sorted array 21 | # Time: O(n ^ 2) 22 | # Space: O(n) - with hash table 23 | 24 | # arr = [-2,1,2,4,7,11] 25 | # target: 10 26 | def three_sum?(arr,target) 27 | hash = {} 28 | arr.each do |el| 29 | hash[target - el] = true 30 | end 31 | 0.upto(arr.length - 2) do |i| 32 | (i + 1).upto(arr.length - 1) do |j| 33 | return true if hash[arr[i] + arr[j]] 34 | end 35 | end 36 | false 37 | end 38 | 39 | # Time: O(n ^ 2) - with Sorted array, can use prev two_sum? method 40 | # Space: O(1) 41 | def better_three_sum?(arr, target) 42 | arr.sort! 43 | arr.each do |el| 44 | return true if has_two_sum?(arr, target - el) 45 | end 46 | false 47 | end 48 | 49 | # 18.5 Find the majority char in a string 50 | # Time: O(n) 51 | # Space: O(k) - distinct chars in the string 52 | 53 | def majority_char(str) 54 | hash = Hash.new(){0} 55 | str.each_char do |c| 56 | hash[c] += 1 57 | end 58 | max = hash.values.max 59 | str.each_char.select do |c| 60 | hash[c] == max 61 | end.first 62 | end 63 | 64 | # Time: O(n) 65 | # Space: O(1) - Use counter 66 | def better_majority_char(str) 67 | candidate = "" 68 | count = 0 69 | str.each_char do |c| 70 | if count == 0 71 | candidate = c 72 | count = 1 73 | elsif candidate == c 74 | count += 1 75 | else 76 | count -= 1 77 | end 78 | end 79 | candidate 80 | end 81 | 82 | # 18.7 Max water (area) for a pair of vertical lines 83 | # heights = [1,2,1,3,4,5,5,6,7,2,1,3,1,3,2,1,2,4,1] 84 | # Time: O(n) 85 | # Space: O(1) 86 | def max_water(heights) 87 | i = 0 88 | j = heights.length - 1 89 | max = 0 90 | while i < j 91 | max = [[heights[i], heights[j]].min * (j - i), max].max 92 | if heights[i] < heights[j] 93 | i += 1 94 | elsif heights[i] > heights[j] 95 | j -= 1 96 | else 97 | i += 1 98 | j -= 1 99 | end 100 | end 101 | max 102 | end 103 | -------------------------------------------------------------------------------- /lib/ch19_graph.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorlin-houzz/elements-of-programming-interviews-in-ruby/76542bbd2cb8c86e5684f43bde98b513c76c4a53/lib/ch19_graph.rb -------------------------------------------------------------------------------- /lib/ch6_array.rb: -------------------------------------------------------------------------------- 1 | # 6.0 2 | # Swap even number to the front of the array. 3 | # 2 pointers 4 | # O(n) time and O(1) space. 5 | def swap_even_odd(arr) 6 | even_idx = 0 7 | odd_idx = arr.length - 1 8 | while even_idx < odd_idx 9 | if arr[even_idx] % 2 == 0 10 | even_idx += 1 11 | else 12 | arr[even_idx], arr[odd_idx] = arr[odd_idx], arr[even_idx] 13 | # arr[even_idx] ^= arr[odd_idx] 14 | # arr[odd_idx] ^= arr[even_idx] 15 | # arr[even_idx] ^= arr[odd_idx] 16 | odd_idx -= 1 17 | end 18 | end 19 | arr 20 | end 21 | 22 | # 6.1 23 | # Return an array with 3 sections: all the numbers 24 | # less than the number at the index, all numbers equal to the number at the index, and then all 25 | # greater than the number at the index. 26 | # O(n) time and O(1) space. 27 | def dutch_flag_partition(arr, idx) 28 | small_pivot = 0 29 | large_pivot = arr.length - 1 30 | equal = 0 31 | 32 | while equal <= large_pivot 33 | if arr[equal] < arr[idx] 34 | # p "swapping #{arr[small_pivot]} and #{arr[equal]}" 35 | arr[equal], arr[small_pivot] = arr[small_pivot], arr[equal] 36 | equal += 1 37 | small_pivot += 1 38 | elsif arr[equal] == arr[idx] 39 | equal += 1 40 | else 41 | # p "swapping #{arr[equal]} and #{arr[large_pivot]}" 42 | arr[equal], arr[large_pivot] = arr[large_pivot], arr[equal] 43 | large_pivot -= 1 44 | end 45 | # p arr 46 | end 47 | 48 | arr 49 | end 50 | 51 | # 6.6 52 | # Return the idx pair for the max profit for buying & selling a stock. 53 | # O(n) time and O(1) space. 54 | 55 | def stock_max_profit(arr) 56 | max_profit = 0 57 | min = arr[0] 58 | min_idx = 0 59 | max_idx = 0 60 | 1.upto(arr.length - 1) do |idx| 61 | if arr[idx] < min 62 | min_idx = idx 63 | min = arr[idx] 64 | end 65 | 66 | if (arr[idx] - min) > max_profit 67 | max_idx = idx 68 | max_profit = arr[idx] - min 69 | end 70 | end 71 | [min_idx, max_idx] 72 | end 73 | 74 | # Return the max profit 75 | def stock_max(arr) 76 | max = -1 77 | min = arr[0] 78 | 1.upto(arr.length - 1) do |i| 79 | min = [min, arr[i]].min 80 | max = [arr[i] - min, max].max 81 | end 82 | max 83 | end 84 | 85 | # 6.11 86 | # Random subset of an array with size n 87 | # O(n) time and O(1) space. 88 | def random_subset(arr, n) 89 | sub_arr = arr.dup 90 | return nil if n > sub_arr.length 91 | 0.upto(n) do |idx| 92 | rand_idx = rand(sub_arr.length) 93 | sub_arr[idx], sub_arr[rand_idx] = sub_arr[rand_idx], sub_arr[idx] 94 | end 95 | sub_arr[0...n] 96 | end 97 | 98 | #6.17 99 | # Return an array in spiral order for a 2D array 100 | # O(n^2) time and O(1) space. 101 | def matrix_spiral(arr) 102 | shift = [[0,1], [1,0], [0,-1], [-1,0]] 103 | dup_arr = deep_dup(arr) 104 | p dup_arr 105 | out_arr = [] 106 | idx = 0 107 | x = 0 108 | y = 0 109 | dir = 0 110 | 111 | while idx < dup_arr.length * dup_arr[0].length 112 | out_arr << dup_arr[x][y] 113 | next_x = x + shift[dir][0] 114 | next_y = y + shift[dir][1] 115 | dup_arr[x][y] = nil 116 | 117 | if next_x < 0 || next_x >= dup_arr.length || 118 | next_y < 0 || next_y >= dup_arr[0].length || dup_arr[next_x][next_y] == nil 119 | dir = (dir + 1) % 4 120 | end 121 | 122 | x += shift[dir][0] 123 | y += shift[dir][1] 124 | idx += 1 125 | end 126 | # p out_arr 127 | out_arr 128 | end 129 | 130 | def deep_dup(arr) 131 | return arr if arr.length <= 1 132 | out_arr = [] 133 | arr.each do |el| 134 | out_arr << (el.is_a?(Array) ? deep_dup(el) : el) 135 | end 136 | out_arr 137 | end 138 | 139 | # 6.18 Rotate an array clockwise or counter-clockwise 140 | # O(n^2) time and O(1) space. 141 | def rotate_matrix(arr, clockwise) 142 | dup_arr = deep_dup(arr) 143 | temp_arr = [] 144 | size = dup_arr.length - 1 145 | 0.upto(dup_arr.length / 2) do |i| 146 | i.upto(dup_arr.length - 2 - i) do |j| 147 | if clockwise 148 | # p "#{i}, #{j}" 149 | # p "#{dup_arr[i][j]}, #{dup_arr[j][size - i]}, #{dup_arr[size - i][size - j]}, #{dup_arr[size - j][i]}" 150 | dup_arr[i][j], dup_arr[j][size - i], dup_arr[size - i][size - j], dup_arr[size - j][i] = 151 | dup_arr[size - j][i], dup_arr[i][j], dup_arr[j][size - i], dup_arr[size - i][size - j] 152 | # p "#{dup_arr[i][j]}, #{dup_arr[j][size - i]}, #{dup_arr[size - i][size - j]}, #{dup_arr[size - j][i]}" 153 | 154 | else 155 | dup_arr[i][j], dup_arr[j][size - i], dup_arr[size - i][size - j], dup_arr[size - j][i] = 156 | dup_arr[j][size - i], dup_arr[size - i][size - j], dup_arr[size - j][i], dup_arr[i][j] 157 | end 158 | end 159 | end 160 | # p dup_arr 161 | dup_arr 162 | end 163 | 164 | def rotate_matrix_2(arr, clockwise) 165 | 166 | # Clockwise flip up & down, flip diag 167 | # 1 2 3 7 8 9 7 4 1 168 | # 4 5 6 => 4 5 6 => 8 5 2 169 | # 7 8 9 1 2 3 9 6 3 170 | if clockwise 171 | 0.upto(arr.length / 2) do |i| 172 | arr[i], arr[arr.length-1-i] = arr[arr.length-1-i], arr[i] 173 | end 174 | 0.upto(arr.length - 1) do |i| 175 | (i + 1).upto(arr.length - 1) do |j| 176 | arr[i][j], arr[j][i] = arr[j][i], arr[i][j] 177 | end 178 | end 179 | 180 | # Counter Clockwise flip left & right, flip diag 181 | # 1 2 3 3 2 1 3 6 9 182 | # 4 5 6 => 6 5 4 => 2 5 8 183 | # 7 8 9 9 8 7 1 4 7 184 | else 185 | 0.upto(arr.length - 1) do |i| 186 | (0).upto(arr.length/2) do |j| 187 | arr[i][j], arr[i][arr.length-1-j] = arr[i][arr.length-1-j], arr[i][j] 188 | end 189 | end 190 | 0.upto(arr.length - 1) do |i| 191 | (i + 1).upto(arr.length - 1) do |j| 192 | arr[i][j], arr[j][i] = arr[j][i], arr[i][j] 193 | end 194 | end 195 | end 196 | 197 | arr 198 | end 199 | 200 | # 6.19 Pascal's triangle 201 | # O(n^2) time and O(1) space. 202 | def pascal_triangle(n) 203 | return [[]] if n < 0 204 | return [[1]] if n == 1 205 | return [[1],[1,1]] if n == 2 206 | last_result = pascal_triangle(n - 1) 207 | last_row = last_result.last 208 | out_arr = [1] 209 | 0.upto(last_row.length - 2) do |idx| 210 | out_arr << last_row[idx] + last_row[idx + 1] 211 | end 212 | out_arr << 1 213 | last_result << out_arr 214 | end 215 | -------------------------------------------------------------------------------- /lib/ch7_string.rb: -------------------------------------------------------------------------------- 1 | # 7.1 2 | # Convert a number into string 3 | def int_to_str(num) 4 | is_negative = num < 0 5 | in_num = num.abs 6 | out_str = "" 7 | while in_num > 0 8 | out_str = ('0'.ord + in_num % 10).chr + out_str 9 | in_num /= 10 10 | end 11 | out_str = "-" + out_str if is_negative 12 | out_str 13 | end 14 | 15 | # 7.1 16 | # Convert a string to a number 17 | def str_to_int(str) 18 | is_negative = true if str[0] == "-" 19 | start = is_negative ? 1 : 0 20 | num = 0 21 | start.upto(str.length - 1) do |idx| 22 | num = num * 10 + (str[idx, 1].ord - "0".ord) 23 | end 24 | return is_negative ? -num : num 25 | end 26 | 27 | # 7.5 28 | # Check if a string is palindrome 29 | def is_palindrome(str) 30 | out_str = str.downcase.gsub(/\W/, "") 31 | 0.upto(out_str.length / 2) do |idx| 32 | return false if out_str[idx, 1] != out_str[out_str.length - idx - 1, 1] 33 | end 34 | true 35 | end 36 | 37 | # 7.6 Reverse words 38 | def reverse_words(str) 39 | out_str = str.reverse 40 | out_str.split(" ").map{|word| word.reverse}.join(" ") 41 | end 42 | 43 | # 7.7 Look and say 44 | def look_and_say(n) 45 | start = '1' 46 | n.times do |time| 47 | start = next_str(start) 48 | end 49 | start 50 | end 51 | 52 | def next_str(str) 53 | out_str = "" 54 | idx = 0 55 | while idx < str.length 56 | count = 1 57 | while idx < str.length - 1 && str[idx, 1] == str[idx + 1, 1] 58 | count += 1 59 | idx += 1 60 | end 61 | out_str += count.to_s + str[idx, 1] 62 | idx += 1 63 | end 64 | out_str 65 | end 66 | 67 | 68 | # 7.9 Convert from Roman to Decimal 69 | def roman_to_dec(str) 70 | hash_roman = { 71 | 'I' => 1, 72 | 'V' => 5, 73 | 'X' => 10, 74 | 'L' => 50, 75 | 'C' => 100, 76 | 'D' => 500, 77 | 'M' => 1000, 78 | } 79 | # From right to left; first set the num = the right most num 80 | num = hash_roman[str[str.length - 1, 1]] 81 | 82 | # Then compare if left < right, substract from num; else add to num 83 | (str.length - 2).downto(0).each do |idx| 84 | if hash_roman[str[idx, 1]] < hash_roman[str[idx + 1, 1]] 85 | num -= hash_roman[str[idx, 1]] 86 | else 87 | num += hash_roman[str[idx, 1]] 88 | end 89 | end 90 | num 91 | end 92 | 93 | # 7.10 Compute All Valid IP Address 94 | def valid_ip?(str) 95 | return false unless str.match(/^\d+(\.\d+){3}$/) 96 | str.split('.').all?{|el| el.to_i >= 0 && el.to_i <= 255} 97 | end 98 | 99 | # 7.11 Print out snake output 100 | def snake_string(str) 101 | out_arr = [] 102 | arr1 = [] 103 | arr2 = [] 104 | arr3 = [] 105 | 106 | first_idx = 1 107 | while first_idx < str.length 108 | arr1 << str[first_idx, 1] 109 | arr2 << "" 110 | arr3 << "" 111 | first_idx += 4 112 | end 113 | 114 | second_idx = 0 115 | while second_idx < str.length 116 | arr1 << "" 117 | arr2 << str[second_idx, 1] 118 | arr3 << "" 119 | second_idx += 2 120 | end 121 | 122 | third_idx = 3 123 | while third_idx < str.length 124 | arr1 << "" 125 | arr2 << "" 126 | arr3 << str[third_idx, 1] 127 | third_idx += 4 128 | end 129 | 130 | out_arr << arr1 131 | out_arr << arr2 132 | out_arr << arr3 133 | out_arr 134 | end 135 | 136 | # 7.12 RLE Encoding & Decoding 137 | 138 | def rle_encoding(str) 139 | idx = 0 140 | out_str = "" 141 | while idx < str.length 142 | count = 1 143 | while idx + 1 < str.length && str[idx,1] == str[idx + 1,1] 144 | count += 1 145 | idx += 1 146 | end 147 | out_str += count.to_s + str[idx] 148 | idx += 1 149 | end 150 | out_str 151 | end 152 | 153 | def rle_decoding(str) 154 | idx = 0 155 | out_str = "" 156 | while idx < str.length 157 | count = str[idx, 1].to_i 158 | while count > 0 159 | out_str += str[idx + 1, 1] 160 | count -= 1 161 | end 162 | idx += 2 163 | end 164 | out_str 165 | end 166 | 167 | # 7.13 Rabin-Karp hash algorithm to check sub strings 168 | # O(m+n) 169 | def substring_idx(str, sub_str) 170 | out_arr = [] 171 | hash = sub_str.hash 172 | 0.upto(str.length - sub_str.length).each do |idx| 173 | out_arr << idx if str[idx, sub_str.length].hash == hash 174 | end 175 | out_arr 176 | end 177 | -------------------------------------------------------------------------------- /lib/ch8_linked_list.rb: -------------------------------------------------------------------------------- 1 | require "linked_list" 2 | 3 | # 8.1 Merge two sorted list into one. 4 | def merge_sorted_list(list1, list2) 5 | node1 = list1.first 6 | node2 = list2.first 7 | new_list = LinkedList.new 8 | while node1 != list1.tail && node2 != list2.tail 9 | if node1.val <= node2.val 10 | new_list.insert(node1.key, node1.val) 11 | node1 = node1.next 12 | else 13 | new_list.insert(node2.key, node2.val) 14 | node2 = node2.next 15 | end 16 | end 17 | 18 | while node1 != list1.tail 19 | p node1.key 20 | new_list.insert(node1.key, node1.val) 21 | node1 = node1.next 22 | end 23 | while node2 != list2.tail 24 | p node2.key 25 | new_list.insert(node2.key, node2.val) 26 | node2 = node2.next 27 | end 28 | new_list 29 | end 30 | 31 | # 8.2 Reverse a sublist 32 | def reverse_sublist(list, start_idx, end_idx) 33 | new_list = LinkedList.new 34 | node = list.first 35 | count = 0 36 | last_node = nil 37 | 38 | # Dup the list 39 | while node != list.tail 40 | next_node = node.next 41 | new_list.add_link(node) 42 | node = next_node 43 | end 44 | node = new_list.first 45 | # Copy the first start_idx nodes to the new linkedlist 46 | while count < start_idx 47 | node = node.next 48 | count += 1 49 | end 50 | 51 | # Start to insert the next node to the end of the new linkedlist 52 | while count < end_idx 53 | temp_node = 54 | count += 1 55 | end 56 | 57 | while node != list.tail 58 | next_node = node.next 59 | new_list.add_link(node) 60 | last_node = node 61 | node = next_node 62 | end 63 | new_list 64 | end 65 | 66 | # 8.3 Find if a list has circle sub list 67 | 68 | def circle(list) 69 | slow = list.first 70 | fast = list.first 71 | while fast != nil && fast.next != nil && fast.next.next != nil 72 | # if not finding the circle yet, advance slow by one, and fast by two. 73 | slow = slow.next 74 | fast = fast.next.next 75 | # find the circle, then find the start node of the circle 76 | if slow == fast 77 | slow = list.first 78 | while slow != fast 79 | slow = slow.next 80 | fast = fast.next 81 | end 82 | # after the loop, slow will be the first node of the circle list. 83 | return slow 84 | end 85 | end 86 | 87 | # No circle, return nil 88 | nil 89 | end 90 | 91 | # 8.4 Find overlapped List 92 | def overlapping_lists(list1, list2) 93 | node1 = list1.first 94 | node2 = list2.first 95 | l1 = list_length(list1) 96 | l2 = list_length(list2) 97 | 98 | # Make sure the lists start with the same length 99 | if l1 > l2 100 | node1 = advanced_by_k(l1 - l2, node1) 101 | else 102 | node2 = advanced_by_k(l2 - l1, node2) 103 | end 104 | 105 | # Check if both lists has the same node (No Cycle) 106 | while node1 != list1.tail && node2 != list2.tail && node1 != node2 107 | node1 = node1.next 108 | node2 = node2.next 109 | end 110 | 111 | return nil if node1 = list1.tail 112 | node1 113 | end 114 | 115 | def list_length(list) 116 | node = list.first 117 | length = 0 118 | while node != list.tail 119 | length += 1 120 | end 121 | length 122 | end 123 | 124 | def advanced_by_k(k, node) 125 | count = 0 126 | while count < k 127 | node = node.next 128 | end 129 | node 130 | end 131 | 132 | # 8.6 Delete a node with O(n) time in a single-linked list - no need to loop through the list 133 | def node_to_delete(node) 134 | # Instead of deleting a node, we copy the key/val from the next node and delete the next node; 135 | # assuming we cannot use prev in a single-linked list 136 | node.key = node.next.key 137 | node.val = node.next.val 138 | 139 | node.next = node.next.next 140 | node.next.prev = node 141 | end 142 | 143 | # 8.7 Delete kth last node from a list 144 | # Use 2 intreators, one is k steps advanced than the other, 145 | # when it reaches the tail, the other one is at (k + 1)th last node. 146 | # O(n) time and O(1) space. 147 | def delete_kth_last_node(list, k) 148 | first = list.first 149 | second = list.first 150 | count = 0 151 | while count < k 152 | first = first.next 153 | end 154 | while first != tail 155 | first = first.next 156 | second = second.next 157 | end 158 | second.next = second.next.next 159 | second.next.prev = second 160 | return list 161 | end 162 | 163 | # 8.8 cyclically right shifted kth node in a singly-linked list 164 | # Find the length, and make a circle for the list 165 | # then move the head (length - k) steps to the next 166 | # O(n) time and O(1) space. 167 | def cyclically_right_shift_list(list, k) 168 | length = 0 169 | tail = list.first 170 | while tail.next != list.tail 171 | length += 1 172 | tail = tail.next 173 | end 174 | 175 | k %= length 176 | # We have the last node and length now. 177 | new_tail = tail 178 | step_to_head = length - k 179 | while step_to_head > 0 180 | new_tail = new_tail.next 181 | step_to_head -= 1 182 | end 183 | 184 | new_head = new_tail.next 185 | new_head.prev = list.head 186 | list.head.next = new_head 187 | new_tail.next = list.tail 188 | list.tail.prev = new_tail 189 | list 190 | end 191 | -------------------------------------------------------------------------------- /lib/ch9_stack.rb: -------------------------------------------------------------------------------- 1 | def rpn_calc(rpn_exp) 2 | # "1,2,-,5,*" 3 | stack_arr = [] 4 | rpn_arr = rpn_exp.split(',') 5 | rpn_arr.each do |el| 6 | if el.match(/[\+\-\*\/]/) 7 | b = stack_arr.pop 8 | a = stack_arr.pop 9 | result = 0 10 | case el 11 | when "+" 12 | result = a + b 13 | when "-" 14 | result = a - b 15 | when "*" 16 | result = a * b 17 | when "/" 18 | result = a / b 19 | end 20 | stack_arr.push(result) 21 | else 22 | stack_arr.push(el.to_i) 23 | end 24 | end 25 | stack_arr.last 26 | end 27 | 28 | def is_well_formed?(exp) 29 | return true unless exp =~ /[\(\)\{\}\[\]]/ 30 | left_chars = [] 31 | exp.each_char do |c| 32 | if c =~ /[\(\{\[]/ 33 | left_chars.push(c) 34 | elsif c =~/[\)\}\]]/ 35 | if c == ")" && left_chars.last != "(" || 36 | c == "]" && left_chars.last != "[" || 37 | c == "}" && left_chars.last != "{" 38 | return false 39 | else 40 | left_chars.pop 41 | end 42 | end 43 | end 44 | left_chars.empty? 45 | end 46 | 47 | class BTreeNode 48 | 49 | def initialize(value) 50 | @data = value 51 | @right = nil 52 | @left = nil 53 | end 54 | 55 | def add_right(value) 56 | right = BTreeNode.new(value) 57 | @right = right 58 | end 59 | 60 | def add_left(value) 61 | left = BTreeNode.new(value) 62 | @left = left 63 | end 64 | 65 | attr_accessor :data, :left, :right 66 | end 67 | 68 | 69 | def b_tree_depth_order(root) 70 | order_queue = [root] 71 | one_level = [] 72 | out_arr = [] 73 | curr_count = order_queue.length 74 | 75 | until order_queue.empty? 76 | curr_node = order_queue.shift 77 | one_level.push(curr_node.data) 78 | curr_count -= 1 79 | order_queue.push(curr_node.left) if curr_node.left != nil 80 | order_queue.push(curr_node.right) if curr_node.right != nil 81 | if curr_count == 0 82 | out_arr.push(one_level) 83 | one_level = [] 84 | curr_count = order_queue.length 85 | end 86 | end 87 | out_arr 88 | 89 | end 90 | -------------------------------------------------------------------------------- /lib/linked_list.rb: -------------------------------------------------------------------------------- 1 | class Link 2 | attr_accessor :key, :val, :next, :prev 3 | 4 | def initialize(key = nil, val = nil) 5 | @key = key 6 | @val = val 7 | @next = nil 8 | @prev = nil 9 | end 10 | 11 | def to_s 12 | "#{@key}: #{@val}" 13 | end 14 | 15 | end 16 | 17 | class LinkedList 18 | include Enumerable 19 | 20 | attr_reader :head, :tail 21 | def initialize 22 | @head = Link.new 23 | @tail = Link.new 24 | @head.next = @tail 25 | @tail.prev = @head 26 | end 27 | 28 | def [](i) 29 | each_with_index { |link, j| return link if i == j } 30 | nil 31 | end 32 | 33 | def first 34 | @head.next 35 | end 36 | 37 | def last 38 | @tail.prev 39 | end 40 | 41 | def empty? 42 | @head.next == @tail 43 | end 44 | 45 | def get(key) 46 | link = @head 47 | while link != @tail 48 | return link.val if link.key == key 49 | link = link.next 50 | end 51 | nil 52 | end 53 | 54 | def include?(key) 55 | return true unless get(key).nil? 56 | false 57 | end 58 | 59 | def add_link(new_link) 60 | last = @tail.prev 61 | last.next = new_link 62 | new_link.prev = last 63 | new_link.next = @tail 64 | @tail.prev = new_link 65 | end 66 | def insert(key, val) 67 | new_link = Link.new(key,val) 68 | last = @tail.prev 69 | last.next = new_link 70 | new_link.prev = last 71 | new_link.next = @tail 72 | @tail.prev = new_link 73 | end 74 | 75 | def remove(key) 76 | if include?(key) 77 | link = @head 78 | while link != @tail 79 | if link.key == key 80 | break 81 | end 82 | link = link.next 83 | end 84 | unless link == @tail 85 | last_link = link.prev 86 | next_link = link.next 87 | last_link.next = next_link 88 | next_link.prev = last_link 89 | end 90 | end 91 | nil 92 | end 93 | 94 | def each 95 | link = @head.next 96 | while link != @tail 97 | proc.call(link) 98 | link = link.next 99 | end 100 | end 101 | 102 | # uncomment when you have `each` working and `Enumerable` included 103 | def to_s 104 | inject([]) { |acc, link| acc << "[#{link.key}, #{link.val}]" }.join(", ") 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/myqueue.rb: -------------------------------------------------------------------------------- 1 | class MyQueue 2 | 3 | def initialize 4 | @store = [] 5 | end 6 | 7 | def enqueue(el) 8 | @store << el 9 | end 10 | 11 | def dequeue 12 | @store.shift 13 | end 14 | 15 | def peek 16 | @store.first 17 | end 18 | 19 | def size 20 | @store.length 21 | end 22 | 23 | def empty? 24 | @store.empty? 25 | end 26 | end 27 | 28 | class MyStack 29 | attr_reader :store 30 | def initialize 31 | @store = [] 32 | @max_stack = [] 33 | @min_stack = [] 34 | @max = nil 35 | @min = nil 36 | end 37 | 38 | def max 39 | @max 40 | end 41 | 42 | def min 43 | @min 44 | end 45 | 46 | def pop 47 | popped = @store.pop 48 | if popped == @max 49 | @max_stack.pop 50 | @max = @max_stack.last 51 | end 52 | 53 | if popped == @min 54 | @min_stack.pop 55 | @min = @min_stack.last 56 | end 57 | popped 58 | end 59 | 60 | def push(el) 61 | @store << el 62 | if @max.nil? || el > @max 63 | @max = el 64 | @max_stack << el 65 | end 66 | 67 | if @min.nil? || el < @min 68 | @min = el 69 | @min_stack << el 70 | end 71 | end 72 | 73 | def peek 74 | @store.last 75 | end 76 | 77 | def size 78 | @store.length 79 | end 80 | 81 | def empty? 82 | @store.empty? 83 | end 84 | end 85 | 86 | class MinMaxStackQueue 87 | attr_reader :my_stack 88 | def initialize 89 | @my_stack = MyStack.new 90 | @temp_stack = MyStack.new 91 | end 92 | 93 | def max 94 | @my_stack.max 95 | end 96 | 97 | def min 98 | @my_stack.min 99 | end 100 | 101 | def enqueue(el) 102 | @my_stack.push(el) 103 | end 104 | 105 | def dequeue 106 | until @my_stack.empty? 107 | @temp_stack.push(@my_stack.pop) 108 | end 109 | return_el = @temp_stack.pop # dequeue!!!! 110 | 111 | until @temp_stack.empty? 112 | @my_stack.push(@temp_stack.pop) 113 | end 114 | return_el 115 | end 116 | 117 | def size 118 | @my_stack.size 119 | end 120 | 121 | def empty? 122 | @my_stack.empty? 123 | end 124 | end 125 | 126 | class StackQueue 127 | 128 | def initialize 129 | @my_stack = MyStack.new 130 | @temp_stack = MyStack.new 131 | end 132 | 133 | def enqueue(el) 134 | @my_stack.push(el) 135 | end 136 | 137 | def dequeue 138 | until @my_stack.empty? 139 | @temp_stack.push(@my_stack.pop) 140 | end 141 | return_el = @temp_stack.pop # dequeue!!!! 142 | 143 | until @temp_stack.empty? 144 | @my_stack.push(@temp_stack.pop) 145 | end 146 | return_el 147 | end 148 | 149 | def size 150 | @my_stack.size 151 | end 152 | 153 | def empty? 154 | @my_stack.empty? 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /spec/ch6_array_spec.rb: -------------------------------------------------------------------------------- 1 | require 'ch6_array' 2 | 3 | describe "Array interview questions" do 4 | describe "#swap_even_odd" do 5 | it "swaps even number to the front of the array" do 6 | arr = [1,5,6,7,8,2,3] 7 | # p swap_even_odd(arr) 8 | expect(swap_even_odd(arr)).to eq([2, 8, 6, 7, 5, 3, 1]) 9 | end 10 | end 11 | describe "#dutch_flag_partition" do 12 | it "partitions an array into 3 sections" do 13 | arr = [8,2,9,7,6,1,3] 14 | 15 | expect(dutch_flag_partition(arr, 3)).to eq([3, 2, 1, 6, 7, 9, 8]) 16 | end 17 | end 18 | 19 | describe "#stock_max_profit" do 20 | it "calculates the maximum profit for a stock" do 21 | arr = [15,5,10,20,5,65,0,30,15,75,20] 22 | 23 | expect(stock_max_profit(arr)).to eq([6,9]) 24 | end 25 | end 26 | 27 | describe "#random_subset" do 28 | it "returns a random subset of an array" do 29 | arr = [1,2,3,4,5] 30 | 31 | expect(random_subset(arr, 3)).not_to eq([1,2,3]) 32 | end 33 | end 34 | 35 | describe "#matrix_spiral" do 36 | it "returns an array with spiral order of a 2D array" do 37 | arr = [[1,2,3],[4,5,6],[7,8,9 ]] 38 | 39 | expect(matrix_spiral(arr)).to eq([1,2,3,6,9,8,7,4,5]) 40 | end 41 | end 42 | 43 | describe "#rotate_matrix" do 44 | it "returns an array with clockwise/counter-clockwise order of a 2D array" do 45 | arr = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] 46 | out_arr1 = [[13,9,5,1],[14,10,6,2],[15,11,7,3],[16,12,8,4]] 47 | out_arr2 = [[4,8,12,16],[3,7,11,15],[2,6,10,14],[1,5,9,13]] 48 | 49 | expect(rotate_matrix(arr, true)).to eq(out_arr1) 50 | expect(rotate_matrix(arr, false)).to eq(out_arr2) 51 | end 52 | end 53 | describe "#pascal_triangle" do 54 | it "returns a pascal triangle array" do 55 | arr2 = [[1], [1,1]] 56 | arr3 = [[1], [1,1], [1,2,1]] 57 | arr4 = [[1], [1,1], [1,2,1], [1,3,3,1]] 58 | arr5 = [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]] 59 | 60 | expect(pascal_triangle(2)).to eq(arr2) 61 | expect(pascal_triangle(3)).to eq(arr3) 62 | expect(pascal_triangle(4)).to eq(arr4) 63 | expect(pascal_triangle(5)).to eq(arr5) 64 | 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/ch7_string_spec.rb: -------------------------------------------------------------------------------- 1 | require 'ch7_string' 2 | 3 | describe "String interview questions" do 4 | describe "#int_to_str" do 5 | it "converts a number to string" do 6 | expect(int_to_str(12345)).to eq("12345") 7 | expect(int_to_str(-12345)).to eq("-12345") 8 | end 9 | end 10 | 11 | describe "#str_to_int" do 12 | it "converts a string to number" do 13 | expect(str_to_int("12345")).to eq(12345) 14 | expect(str_to_int("-12345")).to eq(-12345) 15 | end 16 | end 17 | 18 | describe "#is_palindrome" do 19 | it "checks if a string is a palindrome" do 20 | expect(is_palindrome("abccba")).to be_truthy 21 | expect(is_palindrome("abCcba")).to be_truthy 22 | expect(is_palindrome("abcba")).to be_truthy 23 | expect(is_palindrome("abcd eba")).to be_falsey 24 | expect(is_palindrome("A man, a plan, a canal, Panama.")).to be_truthy 25 | end 26 | end 27 | 28 | describe "#reverse_words" do 29 | it "reverses the word order in a sentence." do 30 | expect(reverse_words("Victor likes Joanne")).to eq("Joanne likes Victor") 31 | end 32 | end 33 | 34 | describe "#look_and_say" do 35 | it "reverses the word order in a sentence." do 36 | expect(look_and_say(1)).to eq("11") 37 | expect(look_and_say(2)).to eq("21") 38 | expect(look_and_say(3)).to eq("1211") 39 | expect(look_and_say(4)).to eq("111221") 40 | end 41 | end 42 | 43 | describe "#roman_to_dec" do 44 | it "converts the roman word to decimal number." do 45 | expect(roman_to_dec('IC')).to eq(99) 46 | expect(roman_to_dec('XVI')).to eq(16) 47 | expect(roman_to_dec('LIC')).to eq(149) 48 | end 49 | end 50 | 51 | describe "#valid_ip?" do 52 | it "checks if an input str is a valid ip" do 53 | expect(valid_ip?("192.168")).to be(false) 54 | expect(valid_ip?("192.168.1")).to be(false) 55 | expect(valid_ip?("192.168.1.1")).to be(true) 56 | expect(valid_ip?("192.168.1.256")).to be(false) 57 | end 58 | end 59 | 60 | # describe "#snake_string?" do 61 | # it "outputs snake string array" do 62 | # arr = [["","e","","",""," ","","","","l","",""], 63 | # ["H","","l","","o","","W","","r","","d",""], 64 | # ["","","","l","","","","o","","","","!"], 65 | # ] 66 | # expect(snake_string("Hello World!")).to be(arr) 67 | # end 68 | # end 69 | 70 | describe "#rle_encoding" do 71 | it "encodes a string" do 72 | expect(rle_encoding("abcccdde")).to eq("1a1b3c2d1e") 73 | end 74 | end 75 | 76 | describe "#rle_decoding" do 77 | it "decodes a string" do 78 | expect(rle_decoding("1a1b3c2d1e")).to eq("abcccdde") 79 | end 80 | end 81 | 82 | describe "#substring_idx" do 83 | it "check the occourances of a substring" do 84 | expect(substring_idx("abc abcabcbabc", "abc")).to eq([0,4,7,11]) 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /spec/ch8_linked_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'ch8_linked_list' 2 | require 'linked_list' 3 | 4 | describe "Linked List Interview questions" do 5 | 6 | list = LinkedList.new 7 | list.insert(1,1) 8 | list.insert(2,2) 9 | list.insert(3,3) 10 | list.insert(5,5) 11 | list.insert(7,7) 12 | list.insert(8,8) 13 | list.insert(10,10) 14 | list.insert(11,11) 15 | list.insert(12,12) 16 | list.insert(13,13) 17 | list.insert(18,18) 18 | list.insert(19,19) 19 | list.insert(21,21) 20 | list.insert(22,22) 21 | list.insert(32,32) 22 | list.insert(33,33) 23 | 24 | describe "Merge 2 sorted List" do 25 | list1 = LinkedList.new 26 | list2 = LinkedList.new 27 | list1.insert(1,1) 28 | list2.insert(2,2) 29 | list1.insert(3,3) 30 | list2.insert(5,5) 31 | list2.insert(7,7) 32 | list1.insert(8,8) 33 | list2.insert(10,10) 34 | list1.insert(11,11) 35 | list1.insert(12,12) 36 | list1.insert(13,13) 37 | list1.insert(18,18) 38 | list2.insert(19,19) 39 | list1.insert(21,21) 40 | list1.insert(22,22) 41 | list2.insert(32,32) 42 | list1.insert(33,33) 43 | 44 | # it "checks if 2 sorted list are merged into one list" do 45 | # expect(merge_sorted_list(list1, list2)).to eq(list3) 46 | # end 47 | end 48 | 49 | describe "#reverse_sublist" do 50 | it "checks if it reverse a sublist of a list" do 51 | new_list = reverse_sublist(list, 3, 6) 52 | new_list.each do |el| 53 | p el.val 54 | end 55 | end 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /spec/ch9_stack_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorlin-houzz/elements-of-programming-interviews-in-ruby/76542bbd2cb8c86e5684f43bde98b513c76c4a53/spec/ch9_stack_spec.rb --------------------------------------------------------------------------------