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..851a2c26 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,10 @@ +1. How does your data structure allow developers to access and manipulate the data? +Ans: I'm using Array to represent line and matrix, simply retrieve the data via index number, and you may change it as you like. + There is other useful array method to insert/delete the element like push, delete, pop, shift, and unshift + + +2. If a developer wanted to find a specific element in your data structure, how would you search for it? +Ans: Going through every element of the array from the beginning to the end. + +3. What other real-world data can each structure represent? +Ans: Each network packet can store in the array. Chess can be store in a matrix. 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..88789595 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,26 +9,44 @@ def initialize end def join(person) + members.push(person) end def leave(person) + members.delete(person) end def front + members[0]; end def middle + members[members.length/2] end def back + members[members.length-1] end def search(person) + members.each do | member | + if member == person + return member + end + end + + return nil end private def index(person) + members.each_with_index do | member, index | + if member == person + return index + end + end + return nil end -end \ No newline at end of file +end diff --git a/01-data-structures/01-introduction-to-data-structures/line/line_answers.txt b/01-data-structures/01-introduction-to-data-structures/line/line_answers.txt index e69de29b..d5af73fc 100644 --- a/01-data-structures/01-introduction-to-data-structures/line/line_answers.txt +++ b/01-data-structures/01-introduction-to-data-structures/line/line_answers.txt @@ -0,0 +1,15 @@ +members is an array, and people is elemnet of the array. + +join method is to add element into an array. Using push command can achieve that. + +leave method is to remove an element. Method delete can do that. + +front method is to show the first person of the array. Simply array[0] will do. + +middle method is to show the middle person of the array. The length of array divid by 2 is the answer. + +back method is to show the last person of the array which is length of array minus 1. + +search method is to search the person. Using each method of array to search every element of array. If matches then return, if no then return nil. + +I'm not sure where can I utilize the index private method, but I think it's to get the index of the person, so I use each_with_index method of array to implement it. 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..efc04b6f 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 @@ -12,11 +12,22 @@ class Pixel def initialize(red, green, blue, x, y) + @red = validate_color(red) + @green = validate_color(green) + @blue = validate_color(blue) + @x = x + @y = y end private def validate_color(color) + if color < 0 + color = 0 + elsif color > 255 + color = 255 + end + color 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..9b7efc98 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 @@ -6,13 +6,22 @@ class Screen attr_accessor :matrix def initialize(width, height) + self.matrix = Array.new(width){Array.new(height){ nil }} end # Insert a Pixel at x, y def insert(pixel, x, y) + pixel.x = x + pixel.y = y + matrix[x][y] = pixel end def at(x, y) + if x > 0 && y > 0 + matrix[x][y] + else + nil + end end private @@ -20,4 +29,4 @@ def at(x, y) def inbounds(x, y) end -end \ No newline at end of file +end diff --git a/01-data-structures/01-introduction-to-data-structures/screen/screen_answers.txt b/01-data-structures/01-introduction-to-data-structures/screen/screen_answers.txt index e69de29b..571fed1e 100644 --- a/01-data-structures/01-introduction-to-data-structures/screen/screen_answers.txt +++ b/01-data-structures/01-introduction-to-data-structures/screen/screen_answers.txt @@ -0,0 +1,7 @@ +To initialize the pixel, before put the value into the color, need to make sure that the value is with in 0-255. Call the validate_color to modify the color value within 0-255. + +To initialize a screen, we need to create a matrix. I use 2 dimension array to create a matrix with default value nil. + +For the insert method, simply put the pixel into the designated matrix. Before putting in just make sure pixel's x and y coordinate are the same as given x, y. + +For the at method, if given x and y are integer, then return the designated matrix. 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..ef4dd467 100644 --- a/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb +++ b/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb @@ -4,15 +4,23 @@ class MyQueue def initialize @queue = Array.new - @head = @queue[0] end def enqueue(element) + @queue[@queue.length] = element + @head = @queue[0] + @tail = @queue[-1] end def dequeue + item = @head + @queue = @queue[1..-1] + @head = @queue[0] + @tail = @queue[-1] + item end def empty? + @queue.length == 0 end -end \ No newline at end of file +end diff --git a/01-data-structures/02-stacks-and-queues/myqueue/myqueue_answers.txt b/01-data-structures/02-stacks-and-queues/myqueue/myqueue_answers.txt index e69de29b..1801bdd5 100644 --- a/01-data-structures/02-stacks-and-queues/myqueue/myqueue_answers.txt +++ b/01-data-structures/02-stacks-and-queues/myqueue/myqueue_answers.txt @@ -0,0 +1,7 @@ +Queue is first in first out, so element comes in at the end and goes out at the first of the array. + +enqueue method is to add an element at the end of the queue, and set the new head to the first the new queue and new tail to the end of the new queue. + +dequeue method is to get first element of the queue which is head. Then remove the first element of the array, everybody left shifts. Finally, set the new head to the first the new queue and new tail to the end of the new queue. + +empty? method is to check if the array is empty via its length. 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..03b3c200 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,18 @@ def initialize end def push(item) + @stack[@stack.length] = item + self.top = item end def pop + item = self.top + @stack = @stack.first (@stack.length - 1) + self.top = @stack[@stack.length-1] + item end def empty? + @stack.length == 0 end -end \ No newline at end of file +end diff --git a/01-data-structures/02-stacks-and-queues/mystack/mystack_answers.txt b/01-data-structures/02-stacks-and-queues/mystack/mystack_answers.txt index e69de29b..63d85604 100644 --- a/01-data-structures/02-stacks-and-queues/mystack/mystack_answers.txt +++ b/01-data-structures/02-stacks-and-queues/mystack/mystack_answers.txt @@ -0,0 +1,7 @@ +Implemented stack using array. top is the pointer to show the top of the stack. + +push method is to add one element at the end of the stack, and top pointer to the item + +pop method is to release the top of the stack, so the pop's element should be the top. Then remove the last element of array, and top moves to the last element of array. + +empty method is to check if array is empty via checking length of the array. diff --git a/01-data-structures/02-stacks-and-queues/mystack/mystack_spec.rb b/01-data-structures/02-stacks-and-queues/mystack/mystack_spec.rb index c0d1af80..98224ace 100644 --- a/01-data-structures/02-stacks-and-queues/mystack/mystack_spec.rb +++ b/01-data-structures/02-stacks-and-queues/mystack/mystack_spec.rb @@ -45,4 +45,4 @@ expect(stack.empty?).to eq false end end -end \ No newline at end of file +end diff --git a/01-data-structures/03-linked-lists/benchmark.rb b/01-data-structures/03-linked-lists/benchmark.rb new file mode 100644 index 00000000..65e5a313 --- /dev/null +++ b/01-data-structures/03-linked-lists/benchmark.rb @@ -0,0 +1,29 @@ +require 'benchmark' +require_relative 'linked_list' + +n = 10_000_000 +node = Node.new("benchmark") +llist = LinkedList.new +Benchmark.bm do |x| + x.report("Array:") { array = Array.new(n) } + x.report("LinkedList:") { for i in 1..n; llist.add_to_tail(node); end } +end + +array = Array.new(n) +temp_node = llist.head + +Benchmark.bm do |y| + y.report("Array:") { array[5000]} + y.report("LinkedList:") { for i in 1..5000; temp_node.next; end} +end + +Benchmark.bm do |z| + z.report("Array:") { array.delete_at(5000)} + z.report("LinkedList:") { + for i in 1..4999 do + if i == 4999 + temp_node.next = temp_node.next.next + end + temp_node.next + end } +end 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..fe14148b 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,35 @@ +1.What is Spatial Locality and why does it benefit performance? +If a particular storage is used at a particular time, then it's more likely the nearby memory locations will be used later on. +If the system puts those data in nearby memory into cache, system can reference those data fast because it's in the cache. + +2.Compare the performance of an Array to a Linked List using the Benchmark module. + - Compare the time it takes to create a 10,000 item Array to appending 10,000 items to a Linked List. + user system total real +Array: 0.000000 0.000000 0.000000 ( 0.000046) +LinkedList: 0.010000 0.000000 0.010000 ( 0.001606) + +This is 10,000,000 item: + user system total real +Array: 0.020000 0.020000 0.040000 ( 0.048662) +LinkedList: 1.450000 0.010000 1.460000 ( 1.461256) + + - Compare the time it takes to access the 5000th element of the Array and the 5000th Node in the Linked List. + user system total real +Array: 0.000000 0.000000 0.000000 ( 0.000003) +LinkedList: 0.000000 0.000000 0.000000 ( 0.000265) + +This is 10,000,000 item: + user system total real +Array: 0.000000 0.000000 0.000000 ( 0.000006) +LinkedList: 0.000000 0.000000 0.000000 ( 0.000499) + + - Compare the time it takes to remove the 5000th element from the Array to removing the 5000th Node in the Linked List. + - In the Array, the 5001st item becomes the 5000th, and so on. + user system total real +Array: 0.000000 0.000000 0.000000 ( 0.000006) +LinkedList: 0.000000 0.000000 0.000000 ( 0.000425) + +This is 10,000,000 item: + user system total real +Array: 0.010000 0.000000 0.010000 ( 0.011722) +LinkedList: 0.000000 0.000000 0.000000 ( 0.000313) diff --git a/01-data-structures/03-linked-lists/linked_list.rb b/01-data-structures/03-linked-lists/linked_list.rb index 5ee2a533..4f87754f 100644 --- a/01-data-structures/03-linked-lists/linked_list.rb +++ b/01-data-structures/03-linked-lists/linked_list.rb @@ -6,25 +6,76 @@ 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 + temp_node = self.head + + while temp_node + if self.head == self.tail + self.head = nil + self.tail = nil + elsif temp_node.next == self.tail + self.tail = temp_node + temp_node.next = nil + end + temp_node = temp_node.next + end end # This method prints out a representation of the list. def print + temp_node = self.head + + while temp_node + puts temp_node.data + temp_node = temp_node.next + end end # This method removes `node` from the list and must keep the rest of the list intact. def delete(node) + temp_node = self.head + + if self.head == node && self.tail == node + self.head = nil + self.tail = nil + elsif self.head == node + self.head = temp_node.next + temp_node = nil + end + + while temp_node + if temp_node.next == node && self.tail == node + temp_node.next = node.next + self.tail = temp_node + elsif temp_node.next == node + temp_node.next = node.next + end + + temp_node = temp_node.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) + node.next = self.head + 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 + temp_node = self.head + self.head = temp_node.next + temp_node.next = nil + temp_node end -end \ No newline at end of file +end diff --git a/01-data-structures/03-linked-lists/linked_list_spec.rb b/01-data-structures/03-linked-lists/linked_list_spec.rb index cabef225..bf236ace 100644 --- a/01-data-structures/03-linked-lists/linked_list_spec.rb +++ b/01-data-structures/03-linked-lists/linked_list_spec.rb @@ -89,4 +89,4 @@ expect(llist.head).to eq nil end end -end \ No newline at end of file +end diff --git a/01-data-structures/03-linked-lists/node.rb b/01-data-structures/03-linked-lists/node.rb index 016acb90..326bdcc0 100644 --- a/01-data-structures/03-linked-lists/node.rb +++ b/01-data-structures/03-linked-lists/node.rb @@ -3,5 +3,7 @@ class Node attr_accessor :data def initialize(data) + @data = data + @next = nil end -end \ No newline at end of file +end 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..5953907a 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 +end diff --git a/01-data-structures/04-hashes-part-1/hashclass.rb b/01-data-structures/04-hashes-part-1/hashclass.rb index e538428a..808f2451 100644 --- a/01-data-structures/04-hashes-part-1/hashclass.rb +++ b/01-data-structures/04-hashes-part-1/hashclass.rb @@ -2,26 +2,43 @@ class HashClass def initialize(size) @items = Array.new(size) + @hash = Hash.new() end def []=(key, value) + if @items[index(key,size)].nil? + @items[index(key,size)] = value + elsif @items[index(key,size)] == value + else + resize + @items[index(key,size)] = value + end + + @hash[key] = value end def [](key) + @items[index(key, size)] end def resize + @items = Array.new(size * 2) + @hash.each do |key, value| + @items[index(key, size)] = value + 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 # Simple method to return the number of items in the hash def size + @items.length end -end \ No newline at end of file +end 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..d259d887 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,2 @@ +1.Explain why doubling the size of the underlying array of your HashClass may be a poor idea. +Ans: It's because it may lead to longer time of search for the designated value, and creating an array is quite wasting of memory and time consumption. It will be even slower if the size is not the power of 2. Finally, double the size of the array may easily lead to the same hash result since new size is divisible by old size. It's easier have collision. diff --git a/01-data-structures/06-trees/binary_tree/benchmark.rb b/01-data-structures/06-trees/binary_tree/benchmark.rb new file mode 100644 index 00000000..0e2303da --- /dev/null +++ b/01-data-structures/06-trees/binary_tree/benchmark.rb @@ -0,0 +1,42 @@ +require 'benchmark' +require_relative 'binary_search_tree' +require_relative '../min_binary_heap/min_binary_heap' + +n = 10000 +a = [] + +for i in 0..n-1 do + a[i] = Node.new(i, i) + #puts a[i].rating +end +bst = BinarySearchTree.new(a[0]) +mbh = MinBinaryHeap.new(a[0]) +Benchmark.bm do |x| + x.report("Binary Search Tree") { for i in 1..n-1; bst.insert(a[0],a[i]); end } +end + +Benchmark.bm do |y| + y.report("Binary Search Tree:") { bst.find(a[0],5000)} +end + +Benchmark.bm do |z| + z.report("Binary Search Tree:") { bst.delete(a[0],5000)} + z.report("Binary Search Tree:") { bst.delete(a[0],9999)} + z.report("Binary Search Tree:") { bst.delete(a[0],3000)} +end + + + +Benchmark.bm do |x| + x.report("Min Binary Heap") { for i in 1..n-1; mbh.insert(a[0],a[i]); end } +end + +Benchmark.bm do |y| + y.report("Min Binary Heap:") { mbh.find(a[0],5000)} +end + +Benchmark.bm do |z| + z.report("Min Binary Heap:") { mbh.delete(a[0],5000)} + z.report("Min Binary Heap:") { mbh.delete(a[0],9999)} + z.report("Min Binary Heap:") { mbh.delete(a[0],3000)} +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..5a94b8c7 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 @@ -3,19 +3,132 @@ class BinarySearchTree def initialize(root) + @root = root end def insert(root, node) + temp_node = root + if temp_node.rating > node.rating && temp_node.left + insert(temp_node.left, node) + elsif temp_node.rating > node.rating + temp_node.left = node + #node.parent = temp_node + elsif temp_node.rating <= node.rating && temp_node.right + insert(temp_node.right, node) + else + temp_node.right = node + #node.parent = temp_node + end end # Recursive Depth First Search def find(root, data) + if data.nil? || root.nil? + return nil + end + stack = [root] + visited = [] + + while !stack.empty? + current = stack.last + visited << current + + if current.title == data + return current + elsif !current.left.nil? && !visited.include?(current.left) + if current.left.title == data + return current.left + else + visited << current.left + stack << current.left + end + elsif !current.right.nil? && !visited.include?(current.right) + if current.right.title == data + return current.right + else + visited << current.right + stack << current.right + end + else + stack.pop + end + + end + + return nil end def delete(root, data) + target = find(root, data) + return nil if data == nil || target == nil + parent = find_parent(@root, target) + + if target.left && target.right + successor = find_min(target.right) + target.title = successor.title + target.rating = successor.rating + delete(successor, successor.title) + elsif target.left + target.rating = target.left.rating + target.title = target.left.title + target.right = target.left.right + target.left = target.left.left + + elsif target.right + target.rating = target.right.rating + target.title = target.right.title + target.left = target.right.left + target.right = target.right.right + + else + target.title = nil + target.rating = nil + if parent.nil? + + elsif parent.left == target + parent.left = nil + elsif parent.right == target + parent.right = nil + end + end end # Recursive Breadth First Search def printf(children=nil) + return nil if @root.nil? + + queue = Queue.new + queue.enq(@root) + + while queue.size != 0 + node = queue.deq + puts "#{node.title}: #{node.rating}" + queue.enq(node.left) if node.left + queue.enq(node.right) if node.right + end + end + + private + + def find_min(root) + current_node = root + while current_node.left + current_node = current_node.left + end + current_node + end + + def find_parent(root, node) + parent = root + return nil if @root == node + if parent.left == node || parent.right == node + return parent + elsif parent.rating > node.rating && parent.left + find_parent(parent.left, node) + elsif parent.rating <= node.rating && parent.right + find_parent(parent.right, node) + else + return nil + end end end diff --git a/01-data-structures/06-trees/binary_tree/binary_search_tree_spec.rb b/01-data-structures/06-trees/binary_tree/binary_search_tree_spec.rb index 72e19528..c660825e 100644 --- a/01-data-structures/06-trees/binary_tree/binary_search_tree_spec.rb +++ b/01-data-structures/06-trees/binary_tree/binary_search_tree_spec.rb @@ -94,6 +94,21 @@ tree.insert(root, mad_max_2) expect(tree.find(root, mad_max_2.title).title).to eq "Mad Max 2: The Road Warrior" end + + it "properly finds a node" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + expect(tree.find(root, inception.title).title).to eq "Inception" + expect(tree.find(root, shawshank.title).title).to eq "The Shawshank Redemption" + end end describe "#delete(data)" do @@ -140,6 +155,66 @@ tree.delete(root, mad_max_2.title) expect(tree.find(root, mad_max_2.title)).to be_nil end + + it "properly deletes a root node" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + tree.delete(root, root.title) + expect(tree.find(root, "The Matrix")).to be_nil + end + + it "properly deletes a node with right children" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + tree.delete(root, pacific_rim.title) + expect(tree.find(root, "Pacific Rim")).to be_nil + end + + it "properly deletes a node with left children" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + tree.delete(root, shawshank.title) + expect(tree.find(root, "The Shawshank Redemption")).to be_nil + end + + it "properly deletes a node with left and right children" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + tree.delete(root, jedi.title) + expect(tree.find(root, "Star Wars: Return of the Jedi")).to be_nil + end end describe "#printf" do @@ -170,6 +245,7 @@ tree.insert(root, jedi) tree.insert(root, empire) tree.insert(root, hope) + tree.printf expect { tree.printf }.to output(expected_output).to_stdout } 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..84155e50 100644 --- a/01-data-structures/06-trees/binary_tree/node.rb +++ b/01-data-structures/06-trees/binary_tree/node.rb @@ -5,5 +5,9 @@ class Node attr_accessor :right def initialize(title, rating) + @title = title + @rating = rating + @left = nil + @right = nil end -end \ No newline at end of file +end 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..2f6fee19 --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb @@ -0,0 +1,170 @@ +require_relative 'node' + +class MinBinaryHeap + attr_reader :root + + def initialize(root) + @root = root + end + + def insert(root, node) + return nil if node.nil? + + if @root.nil? + @root = node + else + + queue = Queue.new + queue.enq(@root) + + #find empty spot of min heap + while !queue.empty? + current = queue.deq + + if current.left && current.right + queue.enq(current.left) + queue.enq(current.right) + else + break + end + end + + #insert node to left or right child + if current.left.nil? + current.left = node + elsif current.right.nil? + current.right = node + end + + while !current.nil? && node.rating < current.rating + title = current.title + rating = current.rating + current.rating = node.rating + current.title = node.title + node.rating = rating + node.title = title + node = current + current = find_parent(@root, node) + end + end + end + + # Recursive Depth First Search + def find(root, data) + return nil if data.nil? + return root if root.title == data + left = find(root.left, data) if root.left + if left + return left + else + right = find(root.right, data) if root.right + return right if right + end + end + + def delete(root, data) + return nil if data == nil + target = find(root, data) + #if root.left && root.right + last_node = find_last(@root) + if last_node + parent = find_parent(@root,last_node) + #puts parent.title + if parent.nil? + elsif parent.left == last_node + parent.left = nil + else + parent.right = nil + end + target.title = last_node.title + target.rating = last_node.rating + last_node.title = nil + last_node.rating = nil + end + + while target.left || target.right + if target.left && target.right + if target.left.rating < target.right.rating + if target.left.rating < target.rating + swap(target, target.left) + else + break + end + else + if target.right.rating < target.rating + swap(target, target.right) + else + break + end + end + elsif target.left + if target.left.rating < target.rating + swap(target, target.left) + else + break + end + elsif target.right + if target.right.rating < target.rating + swap(target, target.right) + else + break + end + else + break + end + end + end + + # Recursive Breadth First Search + def printf(children=nil) + return nil if @root.nil? + + queue = Queue.new + queue.enq(@root) + + while queue.size != 0 + node = queue.deq + puts "#{node.title}: #{node.rating}" + queue.enq(node.left) if node.left + queue.enq(node.right) if node.right + end + end + + private + + def find_last(root) + queue = Queue.new + queue.enq(root) + + while queue.size != 0 + node = queue.deq + queue.enq(node.left) if node.left + queue.enq(node.right) if node.right + end + node + end + + def find_parent(root, node) + parent = root + return nil if @root == node + if parent.left == node || parent.right == node + return parent + end + + left = find_parent(parent.left, node) if parent.left + right = find_parent(parent.right, node) if parent.right + + left || right + + end + + def swap(target, child) + rating = child.rating + title = child.title + child.rating = target.rating + child.title = target.title + target.rating = rating + target.title = title + target = child + end +end 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..9fd87d5b --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/min_binary_heap_spec.rb @@ -0,0 +1,259 @@ +include RSpec + +require_relative 'min_binary_heap' + +RSpec.describe MinBinaryHeap, type: Class do + let (:root) { Node.new("The Matrix", 87) } + let (:tree) { MinBinaryHeap.new(root) } + let (:pacific_rim) { Node.new("Pacific Rim", 72) } + let (:braveheart) { Node.new("Braveheart", 78) } + 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 new root" do + expected_output = "Pacific Rim: 72\nThe Matrix: 87\n" + tree.insert(root, pacific_rim) + expect { tree.printf }.to output(expected_output).to_stdout + expect(tree.root.left.title).to eq "The Matrix" + end + + it "properly inserts new nodes as a left child and right child" do + tree.insert(root, braveheart) + tree.insert(root, pacific_rim) + expect(tree.root.right.title).to eq "Braveheart" + expect(tree.root.left.title).to eq "The Matrix" + end + + it "properly inserts 4 new nodes" do + tree.insert(root, donnie) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, jedi) + expected_output = "Pacific Rim: 72\nStar Wars: Return of the Jedi: 80\nInception: 86\nThe Matrix: 87\nDonnie Darko: 85\n" + expect(tree.root.left.right.title).to eq "Donnie Darko" + expect{ tree.printf }.to output(expected_output).to_stdout + end + + it "properly inserts 7 new nodes" do + tree.insert(root, donnie) + tree.insert(root, mad_max_2) + tree.insert(root, empire) + tree.insert(root, hope) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, jedi) + expect(tree.root.title).to eq "Pacific Rim" + expect(tree.root.left.title).to eq "Star Wars: Return of the Jedi" + expect(tree.root.left.left.left.title).to eq "Star Wars: The Empire Strikes Back" + expect(tree.root.right.right.title).to eq "Inception" + + end + + it "properly inserts a 2 nodes" do + tree.insert(root, hope) + tree.insert(root, martian) + expect(tree.root.title).to eq "The Matrix" + end + + it "properly inserts 10 nodes" do + tree.insert(root, mad_max_2) + tree.insert(root, district) + tree.insert(root, shawshank) + tree.insert(root, braveheart) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, martian) + tree.insert(root, jedi) + tree.insert(root, empire) + tree.insert(root, hope) + expect(tree.root.right.right.title).to eq "Inception" + expect(tree.root.left.right.right.title).to eq "Star Wars: A New Hope" + expect(tree.root.left.left.left.title).to eq "Mad Max 2: The Road Warrior" + end + end + + describe "#find(data)" do + it "handles nil gracefully" do + tree.insert(root, empire) + tree.insert(root, mad_max_2) + expect(tree.find(tree.root, nil)).to eq nil + end + + it "properly finds a left node" do + tree.insert(root, pacific_rim) + expect(tree.find(tree.root, "Pacific Rim").title).to eq "Pacific Rim" + end + + it "properly finds a node with 5 entries" do + tree.insert(root, donnie) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, jedi) + expect(tree.find(tree.root, "Donnie Darko").title).to eq "Donnie Darko" + end + + it "properly finds a node with 8 entries" do + tree.insert(root, donnie) + tree.insert(root, mad_max_2) + tree.insert(root, empire) + tree.insert(root, hope) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, jedi) + expect(tree.find(tree.root, "Star Wars: The Empire Strikes Back").title).to eq "Star Wars: The Empire Strikes Back" + expect(tree.find(tree.root, "Mad Max 2: The Road Warrior").title).to eq "Mad Max 2: The Road Warrior" + end + + it "properly finds a right node" do + tree.insert(root, hope) + tree.insert(root, martian) + expect(tree.find(tree.root, "The Martian").title).to eq "The Martian" + end + + it "properly finds a node with 11 entries" do + tree.insert(root, mad_max_2) + tree.insert(root, district) + tree.insert(root, shawshank) + tree.insert(root, braveheart) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, martian) + tree.insert(root, jedi) + tree.insert(root, empire) + tree.insert(root, hope) + expect(tree.find(tree.root, "Star Wars: The Empire Strikes Back").title).to eq "Star Wars: The Empire Strikes Back" + expect(tree.find(tree.root, "Mad Max 2: The Road Warrior").title).to eq "Mad Max 2: The Road Warrior" + end + end + + describe "#delete(data)" do + it "handles nil gracefully" do + expect(tree.delete(tree.root, nil)).to eq nil + end + + it "properly deletes a left node" do + tree.insert(root, hope) + tree.delete(root, hope.title) + expect(tree.find(tree.root, hope.title)).to be_nil + end + + it "properly deletes a left-left node" do + tree.insert(root, braveheart) + tree.insert(root, pacific_rim) + tree.delete(root, "Pacific Rim") + expect(tree.find(tree.root, "Pacific Rim")).to be_nil + end + + it "properly deletes a left-right node" do + tree.insert(root, donnie) + tree.insert(root, inception) + tree.delete(root, "Inception") + expect(tree.find(tree.root, "Inception")).to be_nil + end + + it "properly deletes a right node" do + tree.insert(root, district) + tree.delete(root, "District 9") + expect(tree.find(tree.root, "District 9")).to be_nil + end + + it "properly deletes a right-left node" do + tree.insert(root, hope) + tree.insert(root, martian) + tree.delete(root, "The Martian") + expect(tree.find(tree.root, "The Martian")).to be_nil + end + + it "properly deletes a right-right node" do + tree.insert(root, empire) + tree.insert(root, mad_max_2) + tree.delete(root, mad_max_2.title) + expect(tree.find(tree.root, mad_max_2.title)).to be_nil + end + + it "properly deletes a root node" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + tree.delete(root, root.title) + expect(tree.find(tree.root, "Pacific Rim")).to be_nil + end + + it "properly deletes a node with children" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + tree.delete(tree.root, "District 9") + expect(tree.find(tree.root, "District 9")).to be_nil + end + + it "properly deletes a node with left children" do + tree.insert(root, hope) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.delete(tree.root, "District 9") + expect(tree.find(tree.root, "District 9")).to be_nil + end + end + + describe "#printf" do + specify { + expected_output = "Pacific Rim: 72\nBraveheart: 78\nStar Wars: Return of the Jedi: 80\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) + tree.insert(root, empire) + tree.insert(root, jedi) + tree.insert(root, martian) + tree.insert(root, pacific_rim) + tree.insert(root, inception) + tree.insert(root, braveheart) + tree.insert(root, shawshank) + tree.insert(root, district) + tree.insert(root, mad_max_2) + 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) + tree.insert(root, district) + tree.insert(root, shawshank) + tree.insert(root, braveheart) + tree.insert(root, inception) + tree.insert(root, pacific_rim) + tree.insert(root, martian) + tree.insert(root, jedi) + tree.insert(root, empire) + tree.insert(root, hope) + tree.printf + expect { tree.printf }.to output(expected_output).to_stdout + } + end +end 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..d509c30a --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/node.rb @@ -0,0 +1,14 @@ +class Node + attr_accessor :title + attr_accessor :rating + attr_accessor :left + attr_accessor :right + + + def initialize(title, rating) + @title = title + @rating = rating + @left = nil + @right = 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..9662de55 100644 --- a/01-data-structures/06-trees/tree-answers.txt +++ b/01-data-structures/06-trees/tree-answers.txt @@ -0,0 +1,61 @@ +1.Print both Trees to the console and compare the difference between your Binary Search Tree and your Heap. +Binary Search Tree only check right child is bigger than parent, and left child is smaller than parent: +The Matrix: 87 +Braveheart: 78 +Mad Max 2: The Road Warrior: 98 +Pacific Rim: 72 +Inception: 86 +District 9: 90 +Star Wars: Return of the Jedi: 80 +The Shawshank Redemption: 91 +The Martian: 92 +Star Wars: The Empire Strikes Back: 94 +Star Wars: A New Hope: 93 + +Min Binary Heap: The root must be the smallest, and tree needs to fill up every child before insert node into next layer. +Pacific Rim: 72 +Star Wars: Return of the Jedi: 80 +Braveheart: 78 +The Matrix: 87 +The Shawshank Redemption: 91 +District 9: 90 +Inception: 86 +Mad Max 2: The Road Warrior: 98 +The Martian: 92 +Star Wars: The Empire Strikes Back: 94 +Star Wars: A New Hope: 93 + + +2.Insert all numbers from 1 to 100000 into both Trees, then use Benchmark to compare performance: +How much time does an average insertion consume in the Binary Search Tree compared to the Heap? + +For 10k node: + user system total real +Binary Search Tree 6.650000 0.010000 6.660000 ( 6.674591) + user system total real +Min Binary Heap 4.830000 0.020000 4.850000 ( 4.848211) + +How much time does finding 50000 in the Binary Search Tree consume compared to the Heap? +To find 5000th node: + user system total real +Binary Search Tree: 0.360000 0.000000 0.360000 ( 0.361991) +Min Binary Heap: 0.000000 0.000000 0.000000 ( 0.000504) + +How much time does the average deletion consume in a Binary Search Tree compared to the Heap? + user system total real +Binary Search Tree: 0.370000 0.010000 0.380000 ( 0.375541) +Binary Search Tree: 1.470000 0.000000 1.470000 ( 1.477066) +Binary Search Tree: 0.130000 0.000000 0.130000 ( 0.134650) + +Min Binary Heap: 0.000000 0.000000 0.000000 ( 0.003238) +Min Binary Heap: 0.000000 0.000000 0.000000 ( 0.003088) +Min Binary Heap: 0.010000 0.000000 0.010000 ( 0.003349) + +When would you use a Binary Search Tree and why? +Search time is better than Heap(O(log(n)) v.s. O(n)) +BinarySearchTree can do both findMin and findMax in O(1), heap can only do one in O(1) + +When would you use an Heap and why? +Average time insertion is better than BinarySearchTree(O(1) v.s. O(log(n))) +Binary heaps can be efficiently implemented on top of arrays, BinarySearchTree cannot, saving memory for constant factor. +Binary heap creation is O(n) worst case, BinarySearchTree is O(nlog(n))