From 6ec891c58625bd62fe84ea82d11a63698aae5155 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Sat, 26 May 2018 21:55:03 -0700 Subject: [PATCH 01/10] finished line using array data structure --- .../line/line.rb | 20 ++++++++++++++++++- .../line/line_answers.txt | 15 ++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) 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. From 6a0d2e71f84515db608085df7f38327c733ad036 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Sat, 26 May 2018 22:54:17 -0700 Subject: [PATCH 02/10] finish screen using 2 dimension array --- .../intro-answers.txt | 10 ++++++++++ .../screen/pixel.rb | 11 +++++++++++ .../screen/screen.rb | 11 ++++++++++- .../screen/screen_answers.txt | 7 +++++++ 4 files changed, 38 insertions(+), 1 deletion(-) 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/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. From bb36682f099d7874ef568693537087dcccd6940e Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Sun, 27 May 2018 13:01:11 -0700 Subject: [PATCH 03/10] finish stack --- .../02-stacks-and-queues/mystack/mystack.rb | 9 ++++++++- .../02-stacks-and-queues/mystack/mystack_answers.txt | 7 +++++++ .../02-stacks-and-queues/mystack/mystack_spec.rb | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) 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 From fc774248dba22d765003d151ce64921c4b888d88 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Sun, 27 May 2018 13:19:24 -0700 Subject: [PATCH 04/10] finish queue --- .../02-stacks-and-queues/myqueue/myqueue.rb | 12 ++++++++++-- .../02-stacks-and-queues/myqueue/myqueue_answers.txt | 7 +++++++ 2 files changed, 17 insertions(+), 2 deletions(-) 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. From 457b56629517f127e2c0210298c2c268c51f59f7 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Sun, 27 May 2018 23:09:10 -0700 Subject: [PATCH 05/10] finish linked list --- .../03-linked-lists/benchmark.rb | 29 ++++++++++ .../03-linked-lists/linked-lists-answers.txt | 35 ++++++++++++ .../03-linked-lists/linked_list.rb | 53 ++++++++++++++++++- .../03-linked-lists/linked_list_spec.rb | 2 +- 01-data-structures/03-linked-lists/node.rb | 4 +- 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 01-data-structures/03-linked-lists/benchmark.rb 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 From 34b0d44ab6429eba402226aadba7c8bea524f0e3 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Mon, 28 May 2018 19:11:55 -0700 Subject: [PATCH 06/10] finish hash 1 --- .../04-hashes-part-1/hash_item.rb | 4 +++- .../04-hashes-part-1/hashclass.rb | 19 ++++++++++++++++++- .../04-hashes-part-1/hashes-1-answers.txt | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) 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..e7ab3187 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. From 8a5c3087fb9f60823579bcf6cbf20e7ac1873144 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Mon, 28 May 2018 19:23:43 -0700 Subject: [PATCH 07/10] edit answer --- 01-data-structures/04-hashes-part-1/hashes-1-answers.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e7ab3187..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 @@ -1,2 +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. +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. From c30720d8ec0dcf4515ca63bbad2dba106e343882 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Tue, 5 Jun 2018 23:29:58 -0700 Subject: [PATCH 08/10] finish binary search tree --- .../binary_tree/binary_search_tree.rb | 75 +++++++++++++++++++ .../06-trees/binary_tree/node.rb | 6 +- 2 files changed, 80 insertions(+), 1 deletion(-) 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..7ea63d37 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,94 @@ class BinarySearchTree def initialize(root) + @root = root end def insert(root, node) + temp_node = root + while temp_node + if temp_node.rating > node.rating && temp_node.left + temp_node = temp_node.left + elsif temp_node.rating > node.rating + temp_node.left = node + temp_node = nil + elsif temp_node.rating <= node.rating && temp_node.right + temp_node = temp_node.right + else + temp_node.right = node + temp_node = nil + end + end end # Recursive Depth First Search def find(root, data) + if data == nil || root.nil? + return nil + end + + if root.title != data && root.left + root = find(root.left, data) + + + elsif root.title != data && root.right + root = find(root.right, data) + + elsif root.title == data + return root + end + end def delete(root, data) + return nil if data == nil + + if root.title == data + if root.left && root.right + successor = root.right.find_min(root.right) + root = successor + successor.delete(successor, successor.title) + elsif root.left + root = root.left + elsif root.right + root = root.right + else + root.title = nil + root.rating = nil + end + + elsif root.title != data && root.left + delete(root.left, data) + + + elsif root.title != data && root.right + delete(root.right, data) + + 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 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 From cbb2b634a207eb74bbaa26533476c100e95aee22 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Tue, 12 Jun 2018 22:44:40 -0700 Subject: [PATCH 09/10] binary search tree with parent --- .../binary_tree/binary_search_tree.rb | 132 ++++++--- .../binary_tree/binary_search_tree_spec.rb | 60 ++++ .../06-trees/binary_tree/node.rb | 2 + .../min_binary_heap/min_binary_heap.rb | 174 ++++++++++++ .../min_binary_heap/min_binary_heap_spec.rb | 259 ++++++++++++++++++ .../06-trees/min_binary_heap/node.rb | 14 + 6 files changed, 597 insertions(+), 44 deletions(-) create mode 100644 01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb create mode 100644 01-data-structures/06-trees/min_binary_heap/min_binary_heap_spec.rb create mode 100644 01-data-structures/06-trees/min_binary_heap/node.rb 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 7ea63d37..01a6ccdd 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 @@ -8,64 +8,108 @@ def initialize(root) def insert(root, node) temp_node = root - while temp_node - if temp_node.rating > node.rating && temp_node.left - temp_node = temp_node.left - elsif temp_node.rating > node.rating - temp_node.left = node - temp_node = nil - elsif temp_node.rating <= node.rating && temp_node.right - temp_node = temp_node.right - else - temp_node.right = node - temp_node = nil - end + 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? + 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 - if root.title != data && root.left - root = find(root.left, data) - - - elsif root.title != data && root.right - root = find(root.right, data) - - elsif root.title == data - return root end + return nil end def delete(root, data) - return nil if data == nil - - if root.title == data - if root.left && root.right - successor = root.right.find_min(root.right) - root = successor - successor.delete(successor, successor.title) - elsif root.left - root = root.left - elsif root.right - root = root.right - else - root.title = nil - root.rating = nil + target = find(root, data) + return nil if data == nil || target == nil + + if target.left && target.right + successor = find_min(target.right) + target.title = successor.title + target.rating = successor.rating + if target.parent.nil? + + elsif target.parent.left == target + target.parent.left = successor + elsif target.parent.right == target + target.parent.right = successor + end + delete(successor, successor.title) + elsif target.left + if target.parent.nil? + + elsif target.parent.left == target + target.parent.left = target.left + elsif target.parent.right == target + target.parent.right = target.left + end + target.rating = target.left.rating + target.title = target.left.title + target.right = target.left.right + target.left = target.left.left + + elsif target.right + if target.parent.nil? + + elsif target.parent.left == target + target.parent.left = target.right + elsif target.parent.right == target + target.parent.right = target.right + end + 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 target.parent.nil? + + elsif target.parent.left == target + target.parent.left = nil + elsif target.parent.right == target + target.parent.right = nil end - - elsif root.title != data && root.left - delete(root.left, data) - - - elsif root.title != data && root.right - delete(root.right, data) - 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..a1916cf0 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,51 @@ 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 end describe "#printf" do diff --git a/01-data-structures/06-trees/binary_tree/node.rb b/01-data-structures/06-trees/binary_tree/node.rb index 84155e50..eaa62e1d 100644 --- a/01-data-structures/06-trees/binary_tree/node.rb +++ b/01-data-structures/06-trees/binary_tree/node.rb @@ -3,11 +3,13 @@ class Node 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/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..cde0db7b --- /dev/null +++ b/01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb @@ -0,0 +1,174 @@ +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 + node.parent = current + elsif current.right.nil? + current.right = node + node.parent = current + end + + # puts "#{current.title} & #{current.parent.title}" if current.parent + while !current.nil? && node.rating < current.rating + if current == @root + @root = node + end + + #current = current.parent + #puts temp_node.title + if current.left == node + replace_child_parent(node, current) + temp_right = node.right + + node.right = current.right + node.right.parent = node if node.right + current.right = temp_right + current.left = node.left + node.left = current + current.parent = node + current.right.parent = current if current.right + current.left.parent = current if current.left + #puts "#{temp_node.title} & #{temp_node.parent.title}" if temp_node.parent + elsif current.right == node + replace_child_parent(node, current) + temp_left = node.left + node.left = current.left + node.left.parent = node if node.left + current.left = temp_left + current.right = node.right + node.right = current + current.parent = node + current.right.parent = current if current.right + current.left.parent = current if current.left + end + current = node.parent + end + #puts @root.title + end + end + + # Recursive Depth First Search + def find(root, data) + puts root.title + 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, "Pacific Rim") + + #if root.left && root.right + last_node = find_last(target) + + if last_node + if target.parent + last_node.parent = target.parent + if last_node.parent.left == target + last_node.parent.left = last_node + elsif last_node.parent.right == target + last_node.parent.right = last_node + end + end + + last_node.left = target.left + last_node.right = target.right + target.left.parent = target if target.left + target.right.parent = target if target.right + target.title = nil + target.rating = nil + target.left = nil + target.right = nil + target.parent = nil + end + #root = successor + #successor.delete(successor, successor.title) + # #elsif root.left + # root = root.left + # elsif root.right + # root = root.right + # else + # root.title = nil + # root.rating = nil + # 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 replace_child_parent(node, node_to_replace) + node_to_replace.parent ? node.parent = node_to_replace.parent : node.parent = nil + if node.parent.nil? + elsif node.parent.left == node_to_replace + node.parent.left = node + else + node.parent.right = node + end + 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..601e139f --- /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).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.title).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, empire.title).title).to eq "Star Wars: The Empire Strikes Back" + # expect(tree.find(tree.root, mad_max_2.title).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, martian.title).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, empire.title).title).to eq "Star Wars: The Empire Strikes Back" + #expect(tree.find(tree.root, mad_max_2.title).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.title) + # expect(tree.find(tree.root, pacific_rim.title)).to be_nil + # end + # + # it "properly deletes a left-right node" do + # tree.insert(root, donnie) + # tree.insert(root, inception) + # tree.delete(root, inception.title) + # expect(tree.find(tree.root, inception.title)).to be_nil + # end + # + # it "properly deletes a right node" do + # tree.insert(root, district) + # tree.delete(root, district.title) + # expect(tree.find(tree.root, district.title)).to be_nil + # end + # + # it "properly deletes a right-left node" do + # tree.insert(root, hope) + # tree.insert(root, martian) + # tree.delete(root, martian.title) + # expect(tree.find(tree.root, martian.title)).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.title) + # 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.printf + tree.delete(tree.root, district.title) + tree.printf + 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) + 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..7a7a4d22 --- /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 + attr_accessor :parent + + def initialize(title, rating) + @title = title + @rating = rating + @left = nil + @right = nil + end +end From 377562ceb621a967635a40a68de66a4571546d54 Mon Sep 17 00:00:00 2001 From: ShangMing Cherng Date: Sat, 16 Jun 2018 17:55:58 -0700 Subject: [PATCH 10/10] Finished tree --- .../06-trees/binary_tree/benchmark.rb | 42 ++++ .../binary_tree/binary_search_tree.rb | 50 ++-- .../binary_tree/binary_search_tree_spec.rb | 16 ++ .../06-trees/binary_tree/node.rb | 2 - .../min_binary_heap/min_binary_heap.rb | 150 ++++++------ .../min_binary_heap/min_binary_heap_spec.rb | 228 +++++++++--------- .../06-trees/min_binary_heap/node.rb | 2 +- 01-data-structures/06-trees/tree-answers.txt | 61 +++++ 8 files changed, 329 insertions(+), 222 deletions(-) create mode 100644 01-data-structures/06-trees/binary_tree/benchmark.rb 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 01a6ccdd..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 @@ -12,12 +12,12 @@ def insert(root, node) insert(temp_node.left, node) elsif temp_node.rating > node.rating temp_node.left = node - node.parent = temp_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 + #node.parent = temp_node end end @@ -61,40 +61,20 @@ def find(root, data) 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 - if target.parent.nil? - - elsif target.parent.left == target - target.parent.left = successor - elsif target.parent.right == target - target.parent.right = successor - end delete(successor, successor.title) elsif target.left - if target.parent.nil? - - elsif target.parent.left == target - target.parent.left = target.left - elsif target.parent.right == target - target.parent.right = target.left - end target.rating = target.left.rating target.title = target.left.title target.right = target.left.right target.left = target.left.left elsif target.right - if target.parent.nil? - - elsif target.parent.left == target - target.parent.left = target.right - elsif target.parent.right == target - target.parent.right = target.right - end target.rating = target.right.rating target.title = target.right.title target.left = target.right.left @@ -103,12 +83,12 @@ def delete(root, data) else target.title = nil target.rating = nil - if target.parent.nil? + if parent.nil? - elsif target.parent.left == target - target.parent.left = nil - elsif target.parent.right == target - target.parent.right = nil + elsif parent.left == target + parent.left = nil + elsif parent.right == target + parent.right = nil end end end @@ -137,4 +117,18 @@ def find_min(root) 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 a1916cf0..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 @@ -200,6 +200,21 @@ 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 @@ -230,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 eaa62e1d..84155e50 100644 --- a/01-data-structures/06-trees/binary_tree/node.rb +++ b/01-data-structures/06-trees/binary_tree/node.rb @@ -3,13 +3,11 @@ class Node 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/min_binary_heap/min_binary_heap.rb b/01-data-structures/06-trees/min_binary_heap/min_binary_heap.rb index cde0db7b..2f6fee19 100644 --- 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 @@ -32,60 +32,28 @@ def insert(root, node) #insert node to left or right child if current.left.nil? current.left = node - node.parent = current elsif current.right.nil? current.right = node - node.parent = current end - # puts "#{current.title} & #{current.parent.title}" if current.parent while !current.nil? && node.rating < current.rating - if current == @root - @root = node - end - - #current = current.parent - #puts temp_node.title - if current.left == node - replace_child_parent(node, current) - temp_right = node.right - - node.right = current.right - node.right.parent = node if node.right - current.right = temp_right - current.left = node.left - node.left = current - current.parent = node - current.right.parent = current if current.right - current.left.parent = current if current.left - #puts "#{temp_node.title} & #{temp_node.parent.title}" if temp_node.parent - elsif current.right == node - replace_child_parent(node, current) - temp_left = node.left - node.left = current.left - node.left.parent = node if node.left - current.left = temp_left - current.right = node.right - node.right = current - current.parent = node - current.right.parent = current if current.right - current.left.parent = current if current.left - end - current = node.parent + 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 - #puts @root.title end end # Recursive Depth First Search def find(root, data) - puts root.title return nil if data.nil? - return root if root.title == data left = find(root.left, data) if root.left - - if left return left else @@ -96,41 +64,55 @@ def find(root, data) def delete(root, data) return nil if data == nil - target = find(root, "Pacific Rim") - + target = find(root, data) #if root.left && root.right - last_node = find_last(target) - + last_node = find_last(@root) if last_node - if target.parent - last_node.parent = target.parent - if last_node.parent.left == target - last_node.parent.left = last_node - elsif last_node.parent.right == target - last_node.parent.right = last_node - end + 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 - last_node.left = target.left - last_node.right = target.right - target.left.parent = target if target.left - target.right.parent = target if target.right - target.title = nil - target.rating = nil - target.left = nil - target.right = nil - target.parent = nil + 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 - #root = successor - #successor.delete(successor, successor.title) - # #elsif root.left - # root = root.left - # elsif root.right - # root = root.right - # else - # root.title = nil - # root.rating = nil - # end end # Recursive Breadth First Search @@ -162,13 +144,27 @@ def find_last(root) node end - def replace_child_parent(node, node_to_replace) - node_to_replace.parent ? node.parent = node_to_replace.parent : node.parent = nil - if node.parent.nil? - elsif node.parent.left == node_to_replace - node.parent.left = node - else - node.parent.right = node + 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 index 601e139f..9fd87d5b 100644 --- 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 @@ -54,6 +54,7 @@ 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 @@ -80,42 +81,42 @@ 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).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.title).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, empire.title).title).to eq "Star Wars: The Empire Strikes Back" - # expect(tree.find(tree.root, mad_max_2.title).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, martian.title).title).to eq "The Martian" - # end + 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) @@ -128,85 +129,85 @@ tree.insert(root, jedi) tree.insert(root, empire) tree.insert(root, hope) - expect(tree.find(tree.root, empire.title).title).to eq "Star Wars: The Empire Strikes Back" - #expect(tree.find(tree.root, mad_max_2.title).title).to eq "Mad Max 2: The Road Warrior" + 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.title) - # expect(tree.find(tree.root, pacific_rim.title)).to be_nil - # end - # - # it "properly deletes a left-right node" do - # tree.insert(root, donnie) - # tree.insert(root, inception) - # tree.delete(root, inception.title) - # expect(tree.find(tree.root, inception.title)).to be_nil - # end - # - # it "properly deletes a right node" do - # tree.insert(root, district) - # tree.delete(root, district.title) - # expect(tree.find(tree.root, district.title)).to be_nil - # end - # - # it "properly deletes a right-left node" do - # tree.insert(root, hope) - # tree.insert(root, martian) - # tree.delete(root, martian.title) - # expect(tree.find(tree.root, martian.title)).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.title) - # expect(tree.find(tree.root, "District 9")).to be_nil - # end + 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) @@ -218,9 +219,7 @@ tree.insert(root, braveheart) tree.insert(root, shawshank) tree.insert(root, district) - #tree.printf - tree.delete(tree.root, district.title) - tree.printf + tree.delete(tree.root, "District 9") expect(tree.find(tree.root, "District 9")).to be_nil end end @@ -253,6 +252,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/min_binary_heap/node.rb b/01-data-structures/06-trees/min_binary_heap/node.rb index 7a7a4d22..d509c30a 100644 --- a/01-data-structures/06-trees/min_binary_heap/node.rb +++ b/01-data-structures/06-trees/min_binary_heap/node.rb @@ -3,7 +3,7 @@ class Node attr_accessor :rating attr_accessor :left attr_accessor :right - attr_accessor :parent + def initialize(title, rating) @title = title 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))