├── hamming_distance.rb ├── bubble_sort.rb ├── qsort.rb ├── selection_sort.rb ├── fibonacci.rb ├── document_distance.rb ├── linked_list_spec.rb ├── document_distance_spec.rb ├── luhn.rb ├── qsort_test.rb ├── binary_search.rb ├── undirected_graph_spec.rb ├── linked_list.rb ├── binary_search_tree_spec.rb ├── undirected_graph.rb └── binary_search_tree.rb /hamming_distance.rb: -------------------------------------------------------------------------------- 1 | class Hamming 2 | def self.compute(word1, word2) 3 | word1.size.times.count { |i| word1[i] != word2[i] } 4 | end 5 | end 6 | 7 | -------------------------------------------------------------------------------- /bubble_sort.rb: -------------------------------------------------------------------------------- 1 | 2 | nums = [4,7,1,2,9,3,5] 3 | 4 | indexes = 5 | nums.map.with_index { |n, idx| idx }.each_cons(2) 6 | 7 | nums.size.times do 8 | indexes.each do |a, b| 9 | nums[a], nums[b] = nums[b], nums[a] if nums[a] > nums[b] 10 | end 11 | end 12 | 13 | p nums 14 | 15 | -------------------------------------------------------------------------------- /qsort.rb: -------------------------------------------------------------------------------- 1 | def quick_sort(list) 2 | return [] if list.empty? 3 | 4 | groups = list.group_by { |n| n <=> list.first } 5 | 6 | less_than = groups[-1] || [] 7 | first = groups[0] || [] 8 | greater_than = groups[1] || [] 9 | 10 | quick_sort(less_than) + first + quick_sort(greater_than) 11 | end 12 | -------------------------------------------------------------------------------- /selection_sort.rb: -------------------------------------------------------------------------------- 1 | 2 | def selection_sort(input) 3 | input.each_with_index do |_, i| 4 | min = i 5 | 6 | (i...input.size).each do |k| 7 | min = k if input[k] < input[min] 8 | end 9 | 10 | input[min], input[i] = input[i], input[min] 11 | end 12 | end 13 | 14 | p selection_sort [1,9,4,7,3,2,37,21] 15 | -------------------------------------------------------------------------------- /fibonacci.rb: -------------------------------------------------------------------------------- 1 | # Recursive solution 2 | 3 | @cache = [] 4 | 5 | def fib(num = 150) 6 | return @cache[num] if @cache[num] 7 | return 1 if num == 1 8 | return 0 if num == 0 9 | 10 | @cache[num] = fib(num-1) + fib(num-2) 11 | end 12 | 13 | p fib 14 | 15 | # Iterative solution 16 | 17 | def fib2(num = 20) 18 | (0..num).each_with_object([]) do |i, memo| 19 | memo[i] = i < 2 ? i : memo[i-1] + memo[i-2] 20 | end 21 | end 22 | 23 | p fib2 24 | -------------------------------------------------------------------------------- /document_distance.rb: -------------------------------------------------------------------------------- 1 | require 'forwardable' 2 | 3 | class Document 4 | attr_reader :words, :count 5 | 6 | extend Forwardable 7 | def_delegators :@count, :each, :fetch 8 | 9 | def initialize(document) 10 | @words = document.scan(/\w+/) 11 | @count = count_words 12 | end 13 | 14 | def count_words 15 | @words.each_with_object(Hash.new(0)) { |w, hash| hash[w] += 1 } 16 | end 17 | 18 | def self.distance(doc1, doc2) 19 | total = 0 20 | 21 | doc1.each do |word, count| 22 | total += count * doc2.fetch(word, 0) 23 | end 24 | 25 | total 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /linked_list_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'linked_list' 2 | 3 | describe LinkedList do 4 | it 'can append new items to the list' do 5 | subject.append 'test' 6 | expect(subject.count).to eq 1 7 | subject.append 'cat' 8 | expect(subject.count).to eq 2 9 | end 10 | 11 | it 'can find an item' do 12 | subject.append 'test' 13 | subject.append 'cat' 14 | subject.append 'atom' 15 | 16 | expect(subject.find('test')).to be_truthy 17 | expect(subject.find('cat')).to be_truthy 18 | expect(subject.find('dog')).to be_falsy 19 | expect(subject.find('atom')).to be_truthy 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /document_distance_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'document_distance' 2 | 3 | describe Document do 4 | it 'splits document into words' do 5 | doc = Document.new "the cat is black" 6 | expect(doc.words).to eq %w(the cat is black) 7 | end 8 | 9 | it 'counts words' do 10 | doc = Document.new "test test test" 11 | expect(doc.count['test']).to eq 3 12 | end 13 | 14 | it 'calculates the distance between two documents' do 15 | doc1 = Document.new "the cat is black" 16 | doc2 = Document.new "the cat is white" 17 | results = Document.distance(doc1, doc2) 18 | 19 | expect(results).to eq 3 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /luhn.rb: -------------------------------------------------------------------------------- 1 | 2 | class Luhn 3 | def self.check(nums) 4 | nums = nums.split(//).map(&:to_i) 5 | 6 | doubled = double_odds(nums) 7 | total = separate_and_sum(doubled) 8 | total % 10 == 0 9 | end 10 | 11 | def self.double_odds(nums) 12 | nums.each_with_index do |_, idx| 13 | nums[idx] *= 2 if idx.odd? 14 | end 15 | end 16 | 17 | def self.mod_ten(n) 18 | (n % 10) + 1 19 | end 20 | 21 | def self.separate_and_sum(nums) 22 | nums.inject do |sum, n| 23 | sum += n > 9 ? mod_ten(n) : n 24 | end 25 | end 26 | end 27 | 28 | p Luhn.check "1762483" # Should be true 29 | p Luhn.check "1762485" # Should be false 30 | -------------------------------------------------------------------------------- /qsort_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | class QuickSortTest < Minitest::Test 4 | def test_simple 5 | assert_equal [1,2,3,4,5], quick_sort([2,3,4,5,1]) 6 | end 7 | 8 | def test_repeated_numbers 9 | assert_equal [1,1,1,2,2,3], quick_sort([2,2,3,1,1,1]) 10 | end 11 | 12 | def test_negative_numbers 13 | assert_equal [-1,2,3,4,5], quick_sort([5,3,2,-1,4]) 14 | end 15 | 16 | def test_sorting_twice 17 | list = [5,4,3,2,1] 18 | 19 | assert_equal [1,2,3,4,5], quick_sort(list) 20 | assert_equal [1,2,3,4,5], quick_sort(list) 21 | end 22 | 23 | def test_empty_list 24 | assert_equal [], quick_sort([]) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /binary_search.rb: -------------------------------------------------------------------------------- 1 | 2 | def binary_search(input, target) 3 | mid = (input.size / 2).floor 4 | 5 | return true if input[mid] == target 6 | return false if input.size <= 1 7 | 8 | if target < input[mid] 9 | binary_search(input[0...mid], target) 10 | else 11 | binary_search(input[mid..-1], target) 12 | end 13 | end 14 | 15 | input = [1, 2, 4, 7, 9, 12, 15] 16 | 17 | p binary_search(input, 15) == true 18 | p binary_search(input, 4) == true 19 | p binary_search(input, 20) == false 20 | p binary_search(input, 1) == true 21 | p binary_search(input, -1) == false 22 | p binary_search(input, 0) == false 23 | p binary_search(input, 999) == false 24 | 25 | p binary_search([], 15) == false 26 | -------------------------------------------------------------------------------- /undirected_graph_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'undirected_graph' 2 | 3 | describe UndirectedGraph do 4 | let(:graph) { UndirectedGraph.new } 5 | 6 | it "counts number of nodes" do 7 | graph.insert("B", "A") 8 | expect(graph.node_count).to eq 2 9 | end 10 | 11 | it "counts number of edges" do 12 | graph.insert("B", "A") 13 | expect(graph.edge_count).to eq 1 14 | end 15 | 16 | it "finds the correct path using BFS" do 17 | graph.insert("B", "A") 18 | graph.insert("C", "A") 19 | graph.insert("D", "C") 20 | graph.insert("G", "C") 21 | 22 | expect(graph.bfs(["A"])).to eq ["A", "B", "C", "D", "G"] 23 | end 24 | 25 | it "can delete nodes" do 26 | graph.insert("B", "A") 27 | graph.insert("C", "A") 28 | graph.insert("D", "C") 29 | graph.delete("C") 30 | 31 | expect(graph.bfs(["A"])).to eq ["A", "B"] 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /linked_list.rb: -------------------------------------------------------------------------------- 1 | class LinkedList 2 | attr_reader :count 3 | 4 | include Enumerable 5 | 6 | def initialize 7 | @head = nil 8 | @count = 0 9 | end 10 | 11 | def each(&block) 12 | @head.each_node(&block) 13 | end 14 | 15 | def find(value) 16 | each.find { |node| node.value == value } 17 | end 18 | 19 | def append(value) 20 | @count += 1 21 | new_node = Node.new(value) 22 | 23 | @head ? @head.append(new_node) : @head = new_node 24 | end 25 | 26 | alias_method :<<, :append 27 | end 28 | 29 | class Node 30 | attr_accessor :next, :value 31 | 32 | def initialize(value) 33 | @value = value 34 | @next = nil 35 | end 36 | 37 | def append(new_node) 38 | each_node { |node| node.next = new_node and break if !node.next } 39 | end 40 | 41 | def each_node 42 | return enum_for(:each_node) unless block_given? 43 | 44 | yield self 45 | 46 | node = self 47 | 48 | yield node while (node = node.next) 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /binary_search_tree_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'binary_search_tree' 2 | 3 | def build_tree(numbers, root) 4 | tree = BinaryTree.new(root) 5 | numbers.each { |n| tree.insert(n) } 6 | tree 7 | end 8 | 9 | describe BinaryTree do 10 | let(:tree) { BinaryTree.new(10) } 11 | 12 | let(:bigger_tree) { build_tree([4, 9, 5, 12, 2], 10) } 13 | let(:right_leaf_tree) { build_tree([4, 9, 10, 12, 2], 11) } 14 | 15 | it "should be able to initialize with a root node" do 16 | expect(tree.root.value).to eq 10 17 | expect(tree.root.left).to be_nil 18 | expect(tree.root.right).to be_nil 19 | end 20 | 21 | it "should be able to find values inserted into the tree" do 22 | expect(bigger_tree.search(5)).to be_a Node 23 | end 24 | 25 | it "should be able to delete a leaf node" do 26 | bigger_tree.delete(5) 27 | expect(bigger_tree.search(5)).to be_falsey 28 | end 29 | 30 | it "should be able to delete a node with one children" do 31 | bigger_tree.delete(9) 32 | expect(bigger_tree.search(5)).to be_truthy 33 | expect(bigger_tree.search(9)).to be_falsey 34 | end 35 | 36 | # 11 is root, 10 is our leaf 37 | it "should be able to delete a node with children on the right" do 38 | right_leaf_tree.delete(9) 39 | expect(right_leaf_tree.search(10)).to be_truthy 40 | expect(right_leaf_tree.search(9)).to be_falsey 41 | end 42 | 43 | it "should be able to traverse the tree in 'level-order'" do 44 | expect(bigger_tree.breadth_first).to eq [10, 4, 12, 2, 9, 5] 45 | end 46 | 47 | it "should be able to traverse the tree in 'pre-order'" do 48 | expect(bigger_tree.depth_first).to eq [10, 4, 2, 9, 5, 12] 49 | end 50 | 51 | it "should be able to traverse the tree in 'in-order'" do 52 | expect(bigger_tree.in_order).to eq [2, 4, 5, 9, 10, 12] 53 | end 54 | end 55 | 56 | # tree = BinaryTree.new 8 57 | # nums = [4, 9, 5, 12, 2] 58 | # nums.each { |n| tree.insert(n) } 59 | # 60 | # puts "Level-order: #{tree.breadth_first}" 61 | # puts "Pre-order: #{tree.depth_first}" 62 | # puts "In-order: #{tree.in_order}" 63 | # 64 | # puts "Number 50 is in the tree: #{tree.search 50}" 65 | # puts "Number 4 is in the tree: #{tree.search 4}" 66 | # puts "Number 5 is in the tree: #{tree.search 5}" 67 | -------------------------------------------------------------------------------- /undirected_graph.rb: -------------------------------------------------------------------------------- 1 | class UndirectedGraph 2 | attr_reader :edge_count 3 | 4 | def initialize 5 | @list = {} 6 | @edge_count = 0 7 | end 8 | 9 | def insert(new_node, neighbor) 10 | add_node(new_node) unless @list[new_node] 11 | add_node(neighbor) unless @list[neighbor] 12 | 13 | add_neighbor(new_node, neighbor) 14 | add_neighbor(neighbor, new_node) 15 | @edge_count += 1 16 | end 17 | 18 | def insert_multiple(value, neighbors) 19 | if get_node(value) 20 | @list[value].neighbors += neighbors 21 | else 22 | @list[value] = Node.new(value, neighbors) 23 | end 24 | 25 | # Register new node with his neighbors 26 | neighbors.each do |n| 27 | link_nodes(n, value) 28 | end 29 | end 30 | 31 | def delete(value) 32 | node = get_node(value) 33 | node.each_neighbor { |n| get_node(n).delete_neighbor(value) } 34 | @list.delete(value) 35 | end 36 | 37 | # Breadth-first search 38 | def bfs(queue, values = []) 39 | 40 | while queue.any? 41 | node = get_node(queue.shift) 42 | values << node.value 43 | 44 | node.each_neighbor do |n| 45 | visited = values.include?(n) && !queue.include?(n) 46 | queue << n unless visited 47 | end 48 | end 49 | 50 | values 51 | end 52 | 53 | def node_count 54 | @list.size 55 | end 56 | 57 | private 58 | 59 | def add_neighbor(node1, node2) 60 | node = get_node(node1) 61 | node.add_neighbor(node2) unless node.contains(node2) 62 | end 63 | 64 | def get_node(node_str) 65 | @list[node_str] 66 | end 67 | 68 | def add_node(value) 69 | @list[value] = Node.new(value, []) 70 | end 71 | 72 | def link_nodes(n, value) 73 | node = get_node(n) 74 | return unless node 75 | 76 | node.add_neighbor(value) unless node.contains(value) 77 | end 78 | end 79 | 80 | class Node 81 | attr_accessor :value, :neighbors, :visited 82 | 83 | def initialize(value, neighbors) 84 | @value = value 85 | @neighbors = neighbors 86 | @visited = false 87 | end 88 | 89 | def contains(other_node) 90 | @neighbors.include? other_node 91 | end 92 | 93 | def add_neighbor(other_node) 94 | @neighbors << other_node 95 | end 96 | 97 | def delete_neighbor(other_node) 98 | @neighbors.delete(other_node) 99 | end 100 | 101 | def each_neighbor 102 | @neighbors.each { |n| yield n } 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /binary_search_tree.rb: -------------------------------------------------------------------------------- 1 | 2 | class BinaryTree 3 | attr_reader :root, :search_path 4 | 5 | def initialize(root_value) 6 | @root = Node.new(root_value) 7 | @search_path = [] 8 | end 9 | 10 | def insert(value, node = @root) 11 | if value > node.value 12 | insert_right(node, value) 13 | else 14 | insert_left(node, value) 15 | end 16 | end 17 | 18 | # Binary search 19 | def search(num) 20 | stack = [] 21 | stack << @root 22 | @search_path.clear 23 | 24 | while stack.size > 0 25 | node = stack.shift 26 | @search_path << node 27 | 28 | return node if node.value == num 29 | return false if node.left.nil? && node.right.nil? 30 | 31 | if num < node.value 32 | stack << node.left 33 | else 34 | stack << node.right 35 | end 36 | end 37 | end 38 | 39 | def delete(value) 40 | return unless search(value) 41 | 42 | if (children = @search_path.last.left || @search_path.last.right) 43 | # One children 44 | update_parent_node(value, children) 45 | else 46 | # Leaf node 47 | update_parent_node(value) 48 | end 49 | 50 | @search_path.last.value = nil 51 | end 52 | 53 | def in_order(focus_node = @root, values = []) 54 | unless focus_node.nil? 55 | in_order(focus_node.left, values) 56 | values << focus_node.value 57 | in_order(focus_node.right, values) 58 | end 59 | 60 | values 61 | end 62 | 63 | # Also know as level order in binary tree parlance 64 | def breadth_first 65 | queue = [] 66 | values = [] 67 | queue << @root 68 | 69 | while queue.size > 0 70 | node = queue.shift 71 | queue << node.left unless node.left.nil? 72 | queue << node.right unless node.right.nil? 73 | 74 | values << node.value 75 | end 76 | 77 | values 78 | end 79 | 80 | # Also know as pre-order traversal in binary tree parlance 81 | def depth_first(node = @root, values = []) 82 | if node 83 | values << node.value 84 | 85 | depth_first(node.left, values) 86 | depth_first(node.right, values) 87 | end 88 | 89 | values 90 | end 91 | 92 | private 93 | 94 | def insert_left(node, value) 95 | if node.left.nil? 96 | node.left = Node.new(value) 97 | else 98 | insert(value, node.left) 99 | end 100 | end 101 | 102 | def insert_right(node, value) 103 | if node.right.nil? 104 | node.right = Node.new(value) 105 | else 106 | insert(value, node.right) 107 | end 108 | end 109 | 110 | def update_parent_node(search_value, new_value = nil) 111 | if @search_path[-2].left == search_value 112 | @search_path[-2].left = new_value 113 | else 114 | @search_path[-2].right = new_value 115 | end 116 | end 117 | end 118 | 119 | class Node 120 | attr_accessor :value, :left, :right 121 | 122 | def initialize(value) 123 | @value = value 124 | @left = nil 125 | @right = nil 126 | end 127 | end 128 | --------------------------------------------------------------------------------