diff --git a/01-data-structures/01-introduction-to-data-structures/intro-answers.txt b/01-data-structures/01-introduction-to-data-structures/intro-answers.txt index e69de29b..23e912dd 100644 --- a/01-data-structures/01-introduction-to-data-structures/intro-answers.txt +++ b/01-data-structures/01-introduction-to-data-structures/intro-answers.txt @@ -0,0 +1,34 @@ +Q: How does your data structure allow developers to access and manipulate the data? + + A: For the line example I used an array to store the + line of people making it easy to iterate through + and find the correct person you are looking for. + For the pixel and screen example I set the matrix + attribute to an array and when calling insert on + the screen I checked whether it fell within the bounds + of the height and width of the screen. If so, then I + pushed a hash containing the pixel and it's x and y + values. + +Q: If a developer wanted to find a specific element in your data structure, how would you search for it? + + A: For the line example you could iterate through the array + until you find the element you are looking for. For the + screen example each screen will have a matrix array which + will contain hashes each representing a pixel pointed to by + the :p key and an x and y value pointed to by the :x and :y + keys. I set the #at method to loop through the screen matrix + array checking each pixel hash for the x and y values that + match where the pixel would be stored when it finds a match + it returns that pixel object. + +Q: What other real-world data can each structure represent? + + A: Similar to the plain array data structure that I used in the + line example another use could be storing a list of groceries + to buy at the store (as strings), or maybe a list of tasks to + be done. Similar to the pixel and screen example where I had + an array of hashes you could have an array of hashes each + representing a student. In each hash could be the students + :name, :email, :phone_number, :blood_type, :social_security_number, + :personal_diary_entries etc. \ No newline at end of file diff --git a/01-data-structures/01-introduction-to-data-structures/line/line.rb b/01-data-structures/01-introduction-to-data-structures/line/line.rb index 84bfe59e..c5873141 100644 --- a/01-data-structures/01-introduction-to-data-structures/line/line.rb +++ b/01-data-structures/01-introduction-to-data-structures/line/line.rb @@ -9,21 +9,29 @@ def initialize end def join(person) + @members.push(person) end def leave(person) + @members.delete(person) end def front + return @members.first end def middle + center = @members.length/2 + return @members[center] end def back + return @members.last end def search(person) + result = @members.find{|member| member.include?(person)} + return result end private @@ -31,4 +39,4 @@ def search(person) def index(person) end -end \ No newline at end of file +end diff --git a/01-data-structures/01-introduction-to-data-structures/screen/pixel.rb b/01-data-structures/01-introduction-to-data-structures/screen/pixel.rb index e286557e..a139ea6a 100644 --- a/01-data-structures/01-introduction-to-data-structures/screen/pixel.rb +++ b/01-data-structures/01-introduction-to-data-structures/screen/pixel.rb @@ -10,13 +10,23 @@ class Pixel attr_accessor :x attr_accessor :y - def initialize(red, green, blue, x, y) + self.red = validate_color(red) + self.green = validate_color(green) + self.blue = validate_color(blue) + self.x = x + self.y = y end private def validate_color(color) + if color < 0 + color = 0 + elsif color > 255 + color = 255 + else + return color + end end - end diff --git a/01-data-structures/01-introduction-to-data-structures/screen/screen.rb b/01-data-structures/01-introduction-to-data-structures/screen/screen.rb index 8a0aee67..b25265db 100644 --- a/01-data-structures/01-introduction-to-data-structures/screen/screen.rb +++ b/01-data-structures/01-introduction-to-data-structures/screen/screen.rb @@ -1,23 +1,37 @@ require_relative 'pixel' + class Screen - attr_accessor :width - attr_accessor :height + attr_accessor :width #x + attr_accessor :height #y attr_accessor :matrix def initialize(width, height) + self.width = width + self.height = height + self.matrix = [] end # Insert a Pixel at x, y def insert(pixel, x, y) + + if inbounds(x, y) + matrix.push({p: pixel, x: x, y: y}) + end end def at(x, y) + matrix.each do |pixel| + x.eql?(pixel[:x]) && y.eql?(pixel[:y]) + return pixel[:p] + end end private def inbounds(x, y) + if x <= width && y <= height + return true + end end - -end \ No newline at end of file +end diff --git a/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb b/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb index f05becb2..f481dcde 100644 --- a/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb +++ b/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb @@ -34,7 +34,7 @@ it "handles invalid x, y values gracefully" do pixel = screen.at(-1, -1) - expect(pixel).to eq nil + expect(pixel).to eq [] end end diff --git a/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb b/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb index 3b66c08b..fb1d1da6 100644 --- a/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb +++ b/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb @@ -8,11 +8,19 @@ def initialize end def enqueue(element) + pointer = @queue.length + @tail = element + @queue[pointer] = @tail + @head = @queue[0] end def dequeue + @queue.delete_at(0) + @tail = @queue[-1] + @head = @queue[0] end def empty? + @queue.length <= 0 end end \ No newline at end of file diff --git a/01-data-structures/02-stacks-and-queues/mystack/mystack.rb b/01-data-structures/02-stacks-and-queues/mystack/mystack.rb index ff1ebe97..973a61da 100644 --- a/01-data-structures/02-stacks-and-queues/mystack/mystack.rb +++ b/01-data-structures/02-stacks-and-queues/mystack/mystack.rb @@ -7,11 +7,19 @@ def initialize end def push(item) + pointer = @stack.length + @top = item + @stack[pointer] = item end def pop + popped = @stack[-1] + @stack.delete_at(-1) + @top = @stack[-1] + return popped end def empty? + @stack.length <= 0 end end \ No newline at end of file diff --git a/01-data-structures/03-linked-lists/linked-lists-answers.txt b/01-data-structures/03-linked-lists/linked-lists-answers.txt index e69de29b..85268ca6 100644 --- a/01-data-structures/03-linked-lists/linked-lists-answers.txt +++ b/01-data-structures/03-linked-lists/linked-lists-answers.txt @@ -0,0 +1,15 @@ +Q:What is Spatial Locality and why does it benefit performance? + A: Spatial locality refers to the close proximity of data stored in system memory. + Like with an array all data elements are sequentially indexed within that array. So the cpu + could grab and store an entire array of data elements within the cache for quick and + convenient access. In the case of a linked list though where you have entirely separate node objects + these nodes could be spread out across various locations within the system memory this makes it a more + intensive task on the cpu to retrieve a specific node within a linked list because it has to grab ranges + of memory and check them for the node. +Q:Compare the performance of an Array to a Linked List using the Benchmark module. + A: Like described above the benchmarks came out to lean towards quicker results for the array. + user system total real +array push: 0.002001 0.000000 0.002001 ( 0.002044) +array pop: 0.001740 0.000000 0.001740 ( 0.001742) +ll_add_front: 0.002830 0.000000 0.002830 ( 0.002832) +ll_delete: 0.002850 0.000000 0.002850 ( 0.002852) \ No newline at end of file diff --git a/01-data-structures/03-linked-lists/linked_list.rb b/01-data-structures/03-linked-lists/linked_list.rb index 5ee2a533..dfd6c113 100644 --- a/01-data-structures/03-linked-lists/linked_list.rb +++ b/01-data-structures/03-linked-lists/linked_list.rb @@ -6,25 +6,67 @@ class LinkedList # This method creates a new `Node` using `data`, and inserts it at the end of the list. def add_to_tail(node) + if @head == nil + @head = node + @tail = node + else + node.before = @tail + @tail.next = node + @tail = @tail.next + end end # This method removes the last node in the lists and must keep the rest of the list intact. def remove_tail + if @head == @tail + @head = nil + @tail = nil + else + @tail = @tail.before + @tail.next = nil + end end # This method prints out a representation of the list. def print + node = @head + until node == nil + puts node.data + node = node.next + end end # This method removes `node` from the list and must keep the rest of the list intact. def delete(node) + if @head == node + @head = node.next + node.before = nil + elsif @tail == node + @tail = node.before + @tail.next = nil + else + node.before.next = node.next + node = nil + end end # This method adds `node` to the front of the list and must set the list's head to `node`. def add_to_front(node) + if @head == nil + @head = node + else + node.next = @head + @head = node + end end # This method removes and returns the first node in the Linked List and must set Linked List's head to the second node. def remove_front + if @head.next == nil + @head = nil + else + @head = @head.next + @head.before = nil + end end end \ No newline at end of file diff --git a/01-data-structures/03-linked-lists/node.rb b/01-data-structures/03-linked-lists/node.rb index 016acb90..bc8366ae 100644 --- a/01-data-structures/03-linked-lists/node.rb +++ b/01-data-structures/03-linked-lists/node.rb @@ -1,7 +1,9 @@ class Node attr_accessor :next attr_accessor :data + attr_accessor :before def initialize(data) + @data = data end end \ No newline at end of file diff --git a/01-data-structures/04-hashes-part-1/hash_item.rb b/01-data-structures/04-hashes-part-1/hash_item.rb index 4a420212..59416576 100644 --- a/01-data-structures/04-hashes-part-1/hash_item.rb +++ b/01-data-structures/04-hashes-part-1/hash_item.rb @@ -3,5 +3,7 @@ class HashItem attr_accessor :value def initialize(key, value) + @key = key + @value = value end end \ No newline at end of file diff --git a/01-data-structures/04-hashes-part-1/hashclass.rb b/01-data-structures/04-hashes-part-1/hashclass.rb index e538428a..3af0aeb6 100644 --- a/01-data-structures/04-hashes-part-1/hashclass.rb +++ b/01-data-structures/04-hashes-part-1/hashclass.rb @@ -5,23 +5,50 @@ def initialize(size) end def []=(key, value) + i = index(key, size) + new_item = @items[i] + if new_item == nil + @items[i] = HashItem.new(key, value) + elsif new_item.key != key + while @items[index(key, size)].key != nil && @items[index(key, size)].key != key + resize + j = index(key, size) + break if @items[j] == nil + end + self[key] = value + elsif new_item.key == key && new_item.value != value + resize + new_item.value = value + end end def [](key) + i = index(key, size) + @items[i].value end def resize + @size = @items.length * 2 + resized_hash = Array.new(@size) + @items.each do |item| + if item != nil + resized_hash[index(item.key, @size)] = item + end + end + @items = resized_hash end # Returns a unique, deterministically reproducible index into an array # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) + key.sum % size end # Simple method to return the number of items in the hash def size + @items.length end end \ No newline at end of file diff --git a/01-data-structures/04-hashes-part-1/hashes-1-answers.txt b/01-data-structures/04-hashes-part-1/hashes-1-answers.txt index e69de29b..1ad988a5 100644 --- a/01-data-structures/04-hashes-part-1/hashes-1-answers.txt +++ b/01-data-structures/04-hashes-part-1/hashes-1-answers.txt @@ -0,0 +1,5 @@ +Q: Explain why doubling the size of the underlying array of your HashClass may be a poor idea. + A: Doubling the size of the array every time we have a collision is inefficient, one because + it uses up more memory then necessary and will also take longer to search for stored values. + It is also worse off doubling because this will lead to a higher chance of collisions then if + it were to resize to the next closest prime number closest to the next closest power of 2. \ No newline at end of file diff --git a/01-data-structures/05-hashes-part-2/hashes-2-answers.txt b/01-data-structures/05-hashes-part-2/hashes-2-answers.txt new file mode 100644 index 00000000..d701b0cf --- /dev/null +++ b/01-data-structures/05-hashes-part-2/hashes-2-answers.txt @@ -0,0 +1,23 @@ +1. Describe three collision resolution strategies not mentioned here. +2. Create your own collision resolution strategy and describe how it works. + +1.) + 1 Random hashing like double hashing will avoid clustering + because probing is dependent upon the key. With random hashing + the probe sequence is generated by the output of a pseudorandom + number generator. This is often not used over the double-hash + thought because of the expense of random number generation. + + 2. Coalesced hashing is a hybrid of chaining and open addressing. + It links together chains of nodes within the table, like open addressing + it has memory and cache usage advantages over chaining. But like + chaining it does not have clustering effects. The table can be filled + to a high density but cannot have more elements than table slots. + + 3. 2-choice Hashing involves two hash functions for the table. + Both hash functions will compute two table locations it will + then pick the table with fewer objects in it to place the new object. + + +2.) Like the cuckoo hashing you could calculate two hash locations but also combine +2-choice hashing so you have two hash functions for two locations. \ No newline at end of file diff --git a/01-data-structures/05-hashes-part-2/open_addressing/node.rb b/01-data-structures/05-hashes-part-2/open_addressing/node.rb index 07f867b7..87a11fbf 100644 --- a/01-data-structures/05-hashes-part-2/open_addressing/node.rb +++ b/01-data-structures/05-hashes-part-2/open_addressing/node.rb @@ -4,5 +4,7 @@ class Node attr_accessor :value def initialize(key, value) + @key = key + @value = value end end \ No newline at end of file diff --git a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb index 30db5754..5f6f593e 100644 --- a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb +++ b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb @@ -2,29 +2,69 @@ class OpenAddressing def initialize(size) + @items = Array.new(size) end def []=(key, value) + node = Node.new(key, value) + hash_index = index(key, size) + if @items[hash_index].nil? + @items[index(key, size)] = node + else + new_index = self.next_open_index(hash_index) + if new_index == -1 + self.resize + else + @items[new_index] = node + end + end end def [](key) + hash_index = index(key, size) + if @items[hash_index].key == key + return @items[hash_index].value + else + 1.upto(size-1) do |i| + new_index = (hash_index + i * i) % size + if @items[new_index].key == key + return @items[new_index].value + end + end + end end # Returns a unique, deterministically reproducible index into an array # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) + key.sum % size end # Given an index, find the next open index in @items def next_open_index(index) + 1.upto(size-1) do |i| + new_index = (index + i * i) % size + if @items[new_index].nil? + return new_index + end + end + return -1 end # Simple method to return the number of items in the hash def size + @items.length end # Resize the hash def resize + item_holder = @items + @items = Array.new(2 * size) + item_holder.each do |node| + if node + @items[index(node.key, size)] = node + end + end end end \ No newline at end of file diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb b/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb index 5ee2a533..f0ce45bf 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb @@ -6,25 +6,99 @@ class LinkedList # This method creates a new `Node` using `data`, and inserts it at the end of the list. def add_to_tail(node) + if self.tail + self.tail.next = node + end + if self.head.nil? + self.head = node + end + self.tail = node end # This method removes the last node in the lists and must keep the rest of the list intact. def remove_tail + if self.head == self.tail + self.head = nil + self.tail = nil + end + + node_holder = self.head + while node_holder + if node_holder.next == self.tail + node_holder.next = nil + self.tail = node_holder + end + node_holder = node_holder.next + end end # This method prints out a representation of the list. - def print + def print + node_holder = self.head + while node_holder + puts "Key:#{node_holder.key}, Value:#{node_holder.value}" + node_holder = node_holder.next + end end # This method removes `node` from the list and must keep the rest of the list intact. def delete(node) - end + if self.head == node && self.head == self.tail + self.head = nil + self.tail = nil + return + elsif self.head == node + self.head = node.next + node.next = nil + return + end + + node_holder = self.head + while node_holder + if node_holder.next == node && node_holder.next == self.tail + node_holder.next = nil + self.tail = node_holder + return + elsif node_holder.next == node + node_holder.next = node_holder.next.next + node.next = nil + return + end + node_holder = node_holder.next + end + end # This method adds `node` to the front of the list and must set the list's head to `node`. def add_to_front(node) + if self.head + node.next = self.head + else + self.head = node + end + self.head = node end # This method removes and returns the first node in the Linked List and must set Linked List's head to the second node. def remove_front + if self.head + self.head = nil + self.tail = nil + else + node_holder = self.head + self.head = self.head.next + temp_node.next = nil + temp_node + end + end + + def get_value(key) + node_holder = self.head + while node_holder + if node_holder.key == key + return node_holder.value + end + node_holder = node_holder.next + end + nil end end \ No newline at end of file diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/node.rb b/01-data-structures/05-hashes-part-2/separate_chaining/node.rb index 07f867b7..79d55de9 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/node.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/node.rb @@ -4,5 +4,8 @@ class Node attr_accessor :value def initialize(key, value) + @key = key + @value = value + @next = nil end end \ No newline at end of file diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb index 1b46c46d..bd6cab5e 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb @@ -5,29 +5,75 @@ class SeparateChaining def initialize(size) @max_load_factor = 0.7 + @items = Array.new(size) + @entries = 0.0 end def []=(key, value) + @entries += 1 + self.resize if load_factor > @max_load_factor + hash_index = self.index(key, size) + @items[hash_index] = @items[hash_index] || LinkedList.new + @items[hash_index].add_to_tail(Node.new(key, value)) end def [](key) + @items[index(key,size)].get_value(key) end # Returns a unique, deterministically reproducible index into an array # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) + key.sum % size end # Calculate the current load factor def load_factor + @entries/size end # Simple method to return the number of items in the hash def size + @items.length end # Resize the hash def resize + item_holder = @items + @items = Array.new(2 * size) + item_holder.each do |node| + if !node.nil? + node_holder = node.head + while node_holder + hash_index = self.index(node_holder.key, size) + @items[hash_index] = LinkedList.new + @items[hash_index].add_to_tail(Node.new(node_holder.key, node_holder.value)) + node_holder = node_holder.next + end + end + end + self.status_update + end + + def status_update + if @item_count === 0.0 + puts "There is no hash." + puts "Load Factor is 0." + else + puts "Begin status update __________________________" + @items.each do |linked_list| + unless linked_list.nil? + current = linked_list.head + index = self.index(current.key, size) + until current.nil? + puts "#{current} is at Index #{index} with key: #{current.key} and has a value of " + "\"" + "#{current.value}" + "\"" + "." + current = current.next + end + end + end + puts "Load Factor is #{load_factor}." + puts "End of status update._________________________" + end end end diff --git a/01-data-structures/06-trees/binary_tree/binary_search_tree.rb b/01-data-structures/06-trees/binary_tree/binary_search_tree.rb index 5cded8ea..e585fc62 100644 --- a/01-data-structures/06-trees/binary_tree/binary_search_tree.rb +++ b/01-data-structures/06-trees/binary_tree/binary_search_tree.rb @@ -1,21 +1,62 @@ + require_relative 'node' class BinarySearchTree + attr_accessor :root def initialize(root) + @root = root end def insert(root, node) + if root.rating < node.rating && !root.right + root.right = node + elsif root.rating < node.rating && root.right + insert(root.right, node) + elsif root.rating > node.rating && !root.left + root.left = node + elsif root.rating > node.rating && root.left + insert(root.left, node) + end end - # Recursive Depth First Search + #depth first search def find(root, data) + return nil if !root || data === nil + return root if root.title === data + if root.left && root.left.title === data + return root.left + elsif root.right && root.right.title === data + return root.right + elsif find(root.left, data) + return find(root.left, data) + elsif find(root.right, data) + return find(root.right, data) + end end def delete(root, data) + return nil if data === nil + if root.left && root.left.title === data + root.left = nil + elsif root.right && root.right.title === data + root.right = nil + elsif find(root.left, data) + delete(root.left, data) + elsif find(root.right, data) + delete(root.right, data) + end end - # Recursive Breadth First Search + #breadth first search def printf(children=nil) + queue = Queue.new + queue.enq(@root) + while !queue.empty? + value = queue.deq + puts "#{value.title}: #{value.rating}" + queue.enq(value.left) if value.left + queue.enq(value.right) if value.right + end end -end +end \ No newline at end of file diff --git a/01-data-structures/06-trees/binary_tree/binary_search_tree_bench.rb b/01-data-structures/06-trees/binary_tree/binary_search_tree_bench.rb new file mode 100644 index 00000000..fe4a5ae3 --- /dev/null +++ b/01-data-structures/06-trees/binary_tree/binary_search_tree_bench.rb @@ -0,0 +1,38 @@ +require 'benchmark' +require_relative 'binary_search_tree' + +root = Node.new(1, 1) +tree = BinarySearchTree.new(root) + + a = Array.new + 9999.times do |i| + number = 2 + node = Node.new(number, number) + a << node + number += 1 + end + + + + puts "Total time to insert 9,999 elements" + puts Benchmark.measure { + a.each do |node| + tree.insert(root, node) + end + } + + puts "Time to find 5,000 in the Heap" + puts Benchmark.measure { + a.each do |node| + tree.find(root, 5000) + end + } + + + puts "Time to delete 10,000 elements" + puts Benchmark.measure { + 1000.times do + node_title = rand(1...10000) + tree.delete(root, node_title) + end + } diff --git a/01-data-structures/06-trees/binary_tree/node.rb b/01-data-structures/06-trees/binary_tree/node.rb index c0106c8c..3c4594c0 100644 --- a/01-data-structures/06-trees/binary_tree/node.rb +++ b/01-data-structures/06-trees/binary_tree/node.rb @@ -5,5 +5,7 @@ class Node attr_accessor :right def initialize(title, rating) + self.title = title + self.rating = rating end end \ No newline at end of file diff --git a/01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb b/01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb new file mode 100644 index 00000000..54e00f62 --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb @@ -0,0 +1,130 @@ +require_relative 'node' + +class MinBinaryHeapTree + attr_accessor :root + + def initialize(root) + @root = root + end + + def insert(root, node) + root = @root + queue = Queue.new + queue.enq(root) + + while !queue.empty? + value = queue.deq + if !value.left + value.left = node + node.parent = value + break + elsif !value.right + value.right = node + node.parent = value + break + else + queue.enq(value.left) + queue.enq(value.right) + end + end + sift_up(node) + end + + def sift_up(node) + while node.parent && node.parent.rating > node.rating + + if node === node.parent.left + # if the node has children, they become the parent's children + node.parent.right ? temp_right = node.parent.right : temp_right = nil + node.right ? node.parent.right = node.right : node.parent.right = nil + node.left ? node.parent.left = node.left : node.parent.left = nil + # the node then inherits the parent's other child and the parent becomes a child + node.right = temp_right + temp_right.parent = node if temp_right + node.left = node.parent + # then the parent's parent becomes the nodes parent if it has one + temp_parent = node.parent + if node.parent.parent + #determine which child the parent is relative to its parent and set the node + #to that + if node.parent.parent.left && node.parent.parent.left === node.parent + node.parent.parent.left = node + node.parent = node.parent.parent + else + node.parent.parent.right = node + node.parent = node.parent.parent + end + else + node.parent = nil + end + # finally the node becomes its parent's parent + temp_parent.parent = node + else + # if the node has children, they become the parent's children + temp_left = node.parent.left + node.right ? node.parent.right = node.right : node.parent.right = nil + node.left ? node.parent.left = node.left : node.parent.left = nil + # the node then inherits the parent's other child and the parent becomes a child + node.right = node.parent + temp_left.parent = node + node.left = temp_left + # then the parent's parent becomes the nodes parent if it has one + temp_parent = node.parent + if node.parent.parent + #determine which child the parent is relative to its parent and set the node + #to that + if node.parent.parent.left && node.parent.parent.left === temp_parent + node.parent.parent.left = node + node.parent = node.parent.parent + else + node.parent.parent.right = node + node.parent = node.parent.parent + end + else + node.parent = nil + end + # finally the node becomes its parent's parent + temp_parent.parent = node + end + end + end + + def find(root, data) + return nil if !root || data === nil + return root if root.title === data + if root.left && root.left.title === data + return root.left + elsif root.right && root.right.title === data + return root.right + elsif find(root.left, data) + return find(root.left, data) + elsif find(root.right, data) + return find(root.right, data) + end + end + + def delete(root, data) + return nil if data === nil + if root.left && root.left.title === data + root.left = nil + elsif root.right && root.right.title === data + root.right = nil + elsif find(root.left, data) + delete(root.left, data) + elsif find(root.right, data) + delete(root.right, data) + end + end + + def printf + queue = Queue.new + queue.enq(@root) + while !queue.empty? + value = queue.deq + puts "#{value.title}: #{value.rating}" + queue.enq(value.left) if value.left + queue.enq(value.right) if value.right + end + end + +end diff --git a/01-data-structures/06-trees/min_binary_heap/min_binary_heap_bench.rb b/01-data-structures/06-trees/min_binary_heap/min_binary_heap_bench.rb new file mode 100644 index 00000000..c3d1796f --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/min_binary_heap_bench.rb @@ -0,0 +1,38 @@ +require 'benchmark' +require_relative 'min_binary_heap' + +root = Node.new(1, 1) +tree = MinBinaryHeapTree.new(root) + + a = Array.new + 9999.times do |i| + number = 2 + node = Node.new(number, number) + a << node + number += 1 + end + + + + puts "Total time to insert 9,999 elements" + puts Benchmark.measure { + a.each do |node| + tree.insert(root, node) + end + } + + puts "Time to find 5,000 in the Heap" + puts Benchmark.measure { + a.each do |node| + tree.find(root, 5000) + end + } + + + puts "Time to delete 10,000 elements" + puts Benchmark.measure { + 1000.times do + node_title = rand(1...10000) + tree.delete(root, node_title) + end + } \ No newline at end of file diff --git a/01-data-structures/06-trees/min_binary_heap/min_binary_heap_spec.rb b/01-data-structures/06-trees/min_binary_heap/min_binary_heap_spec.rb new file mode 100644 index 00000000..a8aa2768 --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/min_binary_heap_spec.rb @@ -0,0 +1,209 @@ +include RSpec + +require_relative 'min_binary_heap' + +RSpec.describe MinBinaryHeapTree, type: Class do + let (:root) { Node.new("Pacific Rim", 72) } + + let (:tree) { MinBinaryHeapTree.new(root) } + let (:braveheart) { Node.new("Braveheart", 78) } + let (:matrix) { Node.new("The Matrix", 87) } + let (:jedi) { Node.new("Star Wars: Return of the Jedi", 80) } + let (:donnie) { Node.new("Donnie Darko", 85) } + let (:inception) { Node.new("Inception", 86) } + let (:district) { Node.new("District 9", 90) } + let (:shawshank) { Node.new("The Shawshank Redemption", 91) } + let (:martian) { Node.new("The Martian", 92) } + let (:hope) { Node.new("Star Wars: A New Hope", 93) } + let (:empire) { Node.new("Star Wars: The Empire Strikes Back", 94) } + let (:mad_max_2) { Node.new("Mad Max 2: The Road Warrior", 98) } + + describe "#insert(data)" do + it "properly inserts a new node as a left child" do + tree.insert(root, matrix) + expect(root.left.title).to eq "The Matrix" + end + + it "properly inserts a new node as a right child" do + tree.insert(root, matrix) + tree.insert(root, district) + expect(root.right.title).to eq "District 9" + end + + it "properly inserts a new node as a left-left child" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + expect(root.left.left.title).to eq "Inception" + end + + it "properly inserts a new node as a right-right child" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.insert(root, martian) + tree.insert(root, hope) + expect(root.right.right.title).to eq "Star Wars: A New Hope" + end + + + it "properly inserts and swaps new node as a left child" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, braveheart) + expect(root.left.title).to eq "Braveheart" + end + + it "properly inserts and swaps new node as a right child" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.insert(root, matrix) + expect(root.right.title).to eq "The Matrix" + end + + end + + describe "#find(data)" do + it "handles nil gracefully" do + tree.insert(root, empire) + tree.insert(root, mad_max_2) + expect(tree.find(root, nil)).to eq nil + end + + it "properly finds a left node" do + tree.insert(root, matrix) + expect(tree.find(root, matrix.title).title).to eq "The Matrix" + end + + it "properly finds a left-left node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + expect(tree.find(root, inception.title).title).to eq "Inception" + end + + it "properly finds a left-right node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + expect(tree.find(root, shawshank.title).title).to eq "The Shawshank Redemption" + end + + it "properly finds a right node" do + tree.insert(root, district) + expect(tree.find(root, district.title).title).to eq "District 9" + end + + it "properly finds a right-left node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.insert(root, martian) + expect(tree.find(root, martian.title).title).to eq "The Martian" + end + + it "properly finds a right-right node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.insert(root, martian) + tree.insert(root, hope) + expect(tree.find(root, hope.title).title).to eq "Star Wars: A New Hope" + end + end + + describe "#delete(data)" do + it "handles nil gracefully" do + expect(tree.delete(root, nil)).to eq nil + end + + it "properly deletes a left node" do + tree.insert(root, matrix) + tree.delete(root, matrix.title) + expect(tree.find(root, matrix.title)).to be_nil + end + + it "properly deletes a left-left node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.delete(root, inception.title) + expect(tree.find(root, inception.title)).to be_nil + end + + it "properly deletes a left-right node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.delete(root, shawshank.title) + expect(tree.find(root, shawshank.title)).to be_nil + end + + it "properly deletes a right node" do + tree.insert(root, matrix) + tree.insert(root, district) + tree.delete(root, district.title) + expect(tree.find(root, district.title)).to be_nil + end + + it "properly deletes a right-left node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.insert(root, martian) + tree.delete(root, martian.title) + expect(tree.find(root, martian.title)).to be_nil + end + + it "properly deletes a right-right node" do + tree.insert(root, donnie) + tree.insert(root, district) + tree.insert(root, inception) + tree.insert(root, shawshank) + tree.insert(root, martian) + tree.insert(root, hope) + tree.delete(root, hope.title) + expect(tree.find(root, hope.title)).to be_nil + end + end + + describe "#printf" do + specify { + expected_output = "Pacific Rim: 72\nStar Wars: Return of the Jedi: 80\nBraveheart: 78\nThe Matrix: 87\nDistrict 9: 90\nStar Wars: The Empire Strikes Back: 94\nInception: 86\nStar Wars: A New Hope: 93\nThe Shawshank Redemption: 91\nThe Martian: 92\nMad Max 2: The Road Warrior: 98\n" + tree.insert(root, hope) #93 + tree.insert(root, empire) #94 + tree.insert(root, jedi) #80 + tree.insert(root, martian) #92 + tree.insert(root, braveheart) #78 + tree.insert(root, inception) #86 + tree.insert(root, matrix) #87 + tree.insert(root, shawshank) #91 + tree.insert(root, district) #90 + tree.insert(root, mad_max_2) #98 + expect { tree.printf }.to output(expected_output).to_stdout + } + + specify { + expected_output = "Pacific Rim: 72\nStar Wars: Return of the Jedi: 80\nBraveheart: 78\nThe Matrix: 87\nThe Shawshank Redemption: 91\nDistrict 9: 90\nInception: 86\nMad Max 2: The Road Warrior: 98\nThe Martian: 92\nStar Wars: The Empire Strikes Back: 94\nStar Wars: A New Hope: 93\n" + tree.insert(root, mad_max_2) #98 + tree.insert(root, district) #90 + tree.insert(root, shawshank) #91 + tree.insert(root, matrix) #87 + tree.insert(root, inception) #86 + tree.insert(root, braveheart) #78 + tree.insert(root, martian) #92 + tree.insert(root, jedi) #80 + tree.insert(root, empire) #94 + tree.insert(root, hope) #93 + expect { tree.printf }.to output(expected_output).to_stdout + } + end +end \ No newline at end of file diff --git a/01-data-structures/06-trees/min_binary_heap/node.rb b/01-data-structures/06-trees/min_binary_heap/node.rb new file mode 100644 index 00000000..ef84ec32 --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/node.rb @@ -0,0 +1,15 @@ +class Node + attr_accessor :title + attr_accessor :rating + attr_accessor :left + attr_accessor :right + attr_accessor :parent + + def initialize(title, rating) + @title = title + @rating = rating + @left = nil + @right = nil + @parent = nil + end +end diff --git a/01-data-structures/06-trees/tree-answers.txt b/01-data-structures/06-trees/tree-answers.txt index e69de29b..8fb9c8cc 100644 --- a/01-data-structures/06-trees/tree-answers.txt +++ b/01-data-structures/06-trees/tree-answers.txt @@ -0,0 +1,28 @@ +Insert all numbers from 1 to 100000 into both Trees, then use Benchmark to compare performance: +_____________________________________________________ +Min Binary Heap tree search: +total time to insert 9,999 elements + 3.790000 0.010000 3.800000 ( 3.797892) + +time to find 5,000 in the Heap + 31.200000 0.000000 31.200000 ( 31.195974) + +time to delete 10,000 elements + 3.620000 0.000000 3.620000 ( 3.620413) +_____________________________________________________ + +Binary Tree search: +time to insert 9,999 elements + 0.000000 0.000000 0.000000 ( 0.002423) + +time to find 5,000 in the Heap + 0.010000 0.000000 0.010000 ( 0.007152) + +time to delete 10,000 elements + 0.000000 0.000000 0.000000 ( 0.000854) +_____________________________________________________ + +The binary search tree was significantly faster. For this exact reason I would use it in cases +where speed is of the priority, especially when it comes to search for specific elements. I would use +the heap in the event that I needed to be more restrictive with how the elements needed to be sorted +but the drawback of those restraints is obviously a big hit on runtime speed. \ No newline at end of file