From 2df6da02726c30e373b32419df09ef4d29178822 Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Thu, 4 Oct 2018 10:17:38 -0600 Subject: [PATCH 01/10] assignment completion --- .DS_Store | Bin 0 -> 6148 bytes 01-data-structures/.DS_Store | Bin 0 -> 6148 bytes .../line/line.rb | 9 ++++++- .../line/line_answers.txt | 23 ++++++++++++++++++ .../line/line_spec.rb | 4 +-- .../screen/pixel.rb | 12 +++++++++ .../screen/pixel_spec.rb | 12 ++++----- .../screen/screen.rb | 13 +++++++++- .../screen/screen_answers.txt | 12 +++++++++ .../screen/screen_spec.rb | 2 +- 10 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 .DS_Store create mode 100644 01-data-structures/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..61190987751e14f9ebda5129f8aa0770b7024463 GIT binary patch literal 6148 zcmeHK&59H;5U$LQni&xX5!Pcaf`<`ib{F>`LNl(1y$LOPP}%L?9^8huv-FRHjwAgT z`bNHikE35DnH5=uJq;>VQ2DBpstWlMk}e`rvo@O&jfhA?8G8dXzY!j1y&%2NvjtQx zM?wuHbf0RV$iKh<@7*n$&=k9tVefwKUqvmQ;Jc-Y=2RaR_9QKA3V%K<525$IJArk{ z9O zl0MJpi=)x)$F^*eyqs4qIM3#AdG#VMGrO4Cw#;gmC-wtc5A@(@w7p#J?2OIs-gGrK z%l-Yy*zD~-Sgi(n`|kA7;qlw|AC@0ipFX?Qk;3_Qax>r>zQDK?fA8X|ENuA({_9-p z9D>4tFdz(Unt^Z+>Fb-GSmqN3gn_@F0lps$lraj}d309?8dm}U`{;H8n|}{E#srK4 zb{;VTQ9cytLscj-ln+P034T$)&Z7?}6^aiPW>%p>QEqnRH_n|@eGU@a3oY4%7+y X4I98HVCNA%5cv^sG)N;1Y?Of?am#5d literal 0 HcmV?d00001 diff --git a/01-data-structures/.DS_Store b/01-data-structures/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..83496f7f59f6effe3e90933589641dc2da0e98e6 GIT binary patch literal 6148 zcmeHK&2G~`5T0!VI6B635i1@<&XA&kfKlyy&)OF0Z?n#7O`sV)IUUR2+DLe7y;K^dhKP`NhRG?)mO2>fdi;In&&I@F~$MYMc=k8qYFoaF>P1+a}%?ZNV3 zl7bI6d;?)W>}MF?j&J_e~0{OY-4Q^NIHH*`LHwUUWKN#Kvar+VvZj z)vy}38n4`OI&}-LDD#n*z2MptuNeE4ZCdp9{V#v$q zQ4*xnQCcQJF6VJohh^2R`hne?&3e6-(;sx_EoZj7+i5w2{?2?}x0-jm5BCq>oSx0z z&EKEP)sw*YQOcIaAMg>zrNsZOn8d9!1ysd=P0pHP@ABZf!(I=>aGzVF5ut) z4Zm06f35H>_}>AaZ>5mEua1|y^n7WTf_{ZW?@8M!z@J5czYh+Saj0>j zP<}d4$R_}>hGr?S`TA#$qj9Klp%6U~p{PJb75a%G6dmo39yipuP^jo6^y5Ql&qBXY zg!GR29XXuDP@ySJ1WW{0Be1EeHsAk`fBpVnC7GIufQi6=6#-G(bN5DAlD=El7RPt3 u13iSY@VJFSNkO5nV`=bJd>bkSeGVJIp~i(mv_Q;{fDD5vOa%UF1il03fXKH1 literal 0 HcmV?d00001 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..22d5a316 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,33 @@ def initialize end def join(person) + members.push(person) end def leave(person) + members.delete(person) end def front + members.first end def middle + members[members.length / 2] end def back + members.last end def search(person) + members.detect {|e| e === person} end private def index(person) + members.find_index(person) 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..46398e69 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,23 @@ + For this challenge regarding the line at an amusement park I opted to use an +array as the data structure. I came to this decision as an array is already much +like a line. + + You can easily add "people" to the end of the "line" using the array.push +method, similar to people joining a real world line at the end. An array of +is also linear like a line, and using some basic ruby method you can modify the +array to show people leaving and entering the line in addition to seeing who is +first, last and in the middle. + + Developers would be able to access and manipulate this data by using Ruby array +method in addition to searching by index. Developers could also search by name +using the detect method Ruby provides. + + I believe that an array was the right solution. I can also envision arrays +being a useful data structure for other situations such as a to-do list of items, +or the midi values and their related notes on a piano. Some other examples that +come to mind would be a repair queue at a phone shop, storing high scores on +sequential levels of an arcade game, etc. The list could go on and on as there +is much real-world data that an array could represent. + + While arrays are a more straight forward and simple data structure their use +and ability to represent data is very valuable to developers. diff --git a/01-data-structures/01-introduction-to-data-structures/line/line_spec.rb b/01-data-structures/01-introduction-to-data-structures/line/line_spec.rb index b755dabb..13435d11 100644 --- a/01-data-structures/01-introduction-to-data-structures/line/line_spec.rb +++ b/01-data-structures/01-introduction-to-data-structures/line/line_spec.rb @@ -1,6 +1,6 @@ include RSpec -require_relative 'line' +require_relative 'line' RSpec.describe Line, type: Class do let(:line) { Line.new } @@ -59,4 +59,4 @@ end end -end \ No newline at end of file +end diff --git a/01-data-structures/01-introduction-to-data-structures/screen/pixel.rb b/01-data-structures/01-introduction-to-data-structures/screen/pixel.rb index e286557e..5e087197 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,23 @@ 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 + 0 + elsif color > 255 + 255 + else + color + end end end diff --git a/01-data-structures/01-introduction-to-data-structures/screen/pixel_spec.rb b/01-data-structures/01-introduction-to-data-structures/screen/pixel_spec.rb index b3ab426e..3136cbed 100644 --- a/01-data-structures/01-introduction-to-data-structures/screen/pixel_spec.rb +++ b/01-data-structures/01-introduction-to-data-structures/screen/pixel_spec.rb @@ -4,24 +4,24 @@ describe "#initialize" do it "creates a pixel with the specified parameters" do pixel = Pixel.new(255, 200, 160, 5, 7) - + expect(pixel.red).to eq 255 expect(pixel.green).to eq 200 expect(pixel.blue).to eq 160 expect(pixel.x).to eq 5 expect(pixel.y).to eq 7 end - + it "corrects a red value if it's less than 0" do pixel = Pixel.new(-7, 100, 100, 5, 5) expect(pixel.red).to eq 0 end - + it "corrects a blue value if it's less than 0" do pixel = Pixel.new(100, -10, 100, 5, 5) expect(pixel.green).to eq 0 end - + it "corrects a green value if it's less than 0" do pixel = Pixel.new(100, 100, -12, 5, 5) expect(pixel.blue).to eq 0 @@ -31,12 +31,12 @@ pixel = Pixel.new(256, 100, 100, 5, 5) expect(pixel.red).to eq 255 end - + it "corrects a green value if it's greater than 255" do pixel = Pixel.new(100, 258, 100, 5, 5) expect(pixel.green).to eq 255 end - + it "corrects a blue value if it's greater than 255" do pixel = Pixel.new(100, 100, 300, 5, 5) expect(pixel.blue).to eq 255 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..32b0acf1 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,18 +6,29 @@ class Screen attr_accessor :matrix def initialize(width, height) + @matrix = Array.new(width) { Array.new(height, 0) } + @height = height + @width = width end # Insert a Pixel at x, y def insert(pixel, x, y) + @matrix[x][y] = pixel end def at(x, y) + inbounds(x, y) end private def inbounds(x, y) + if x > @width || y > @height || x < 0 || y < 0 + nil + else + @matrix[x][y] + end + 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..ca244897 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,12 @@ + For this challenge regarding the pixel and screen I opted to use a matrix made +up of an array containing arrays. This allowed me to represent the height and +width of the screen that will then contain the pixels. + + This data structure will allow for easy access to the data in the matrix. By +using matrix[coordinate-x][coordinate-y] you can easily manipulate or retrieve the data in the structure. Developers +would be able to easily search for pixels by coordinates. + + This data structure could be useful for other real world data representations +such as any game that utilizes a grid, for example battleship, connect 4 or +tick tack toe. In addition previous checkpoints showed that a matrix can be used +to represent graph data such as vertices and edges. diff --git a/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb b/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb index f05becb2..45a2e008 100644 --- a/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb +++ b/01-data-structures/01-introduction-to-data-structures/screen/screen_spec.rb @@ -9,7 +9,7 @@ screen.insert(pixel, 1, 1) expect(screen.at(1, 1)).to eq pixel - end + end it "retains color information upon insertion" do pixel = Pixel.new(255, 200, 175, 1, 1) From 70ea231014b860cc07bccdc756d0fd2e4fabdd9a Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Thu, 4 Oct 2018 15:53:41 -0600 Subject: [PATCH 02/10] assignment completion --- .../02-stacks-and-queues/myqueue/myqueue.rb | 15 ++++++++++++++- .../myqueue/myqueue_answers.txt | 14 ++++++++++++++ .../02-stacks-and-queues/mystack/mystack.rb | 13 ++++++++++++- .../mystack/mystack_answers.txt | 12 ++++++++++++ .../02-stacks-and-queues/mystack/mystack_spec.rb | 8 +++++++- 5 files changed, 59 insertions(+), 3 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..8f0817ef 100644 --- a/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb +++ b/01-data-structures/02-stacks-and-queues/myqueue/myqueue.rb @@ -8,11 +8,24 @@ def initialize end def enqueue(element) + @queue << element + @head = @queue[0] + @tail = @queue[-1] end def dequeue + temp = @head + @queue.delete_at(0) + @head = @queue[0] + @tail = @queue[-1] + temp end def empty? + if(@queue.length === 0) + true + else + false + end 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..f72c15d4 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,14 @@ + I have created a queue and based on the tests, in addition to my understanding +I believe it has what is necessary to function properly as a queue. + + My enqueue method appends the element to the end of the array. It will then +set the new head as the element at queue[0] and the tail as element at queue[-1]. + + My dequeue method allows for FIFO as it will remove the first item in the queue +after storing it temporarily(so that it can be returned). Following the removal of +the first element it will set the new head to the new item at queue[0] and the new +tail to queue[-1]. It will then return the previous head. + + My empty method will check to see if the queue length is equal to zero and if +it is we know the queue is empty and the method will return true. If not equal +to zero then the method will return false as it is not empty 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..61a96e0c 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,22 @@ def initialize end def push(item) + self.top = item + @stack << item end def pop + temp = self.top + @stack.delete_at(-1) + self.top = @stack[-1] + temp end def empty? + if(@stack.length === 0) + true + else + false + end 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..c0e80d9e 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,12 @@ + With my current understanding of a stack I believe my solution meets the necessary +requirements. + + For my push method I appended the item to the end of the stack. From there my +pop method will temporarily store the current top then delete the last element +in the array. It will then reset the top to the new last element and return +the old top. + + The empty? method will check to see if the array length is equal to zero and +if it is it means the stack is empty and will return true. If the length is not +equal to zero then that will mean there is at least one element in the array +and will return false. 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..37f2a39b 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 @@ -40,9 +40,15 @@ expect(stack.empty?).to eq true end + it "returns true when the stack is empty" do + stack.push("Rob") + stack.pop + expect(stack.empty?).to eq true + end + it "returns false when the stack is not empty" do stack.push("Rob") expect(stack.empty?).to eq false end end -end \ No newline at end of file +end From 76e7b01fdc929a7d5dfa6bbaf35729b360d535e8 Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Mon, 8 Oct 2018 16:06:13 -0600 Subject: [PATCH 03/10] linked list started --- .../03-linked-lists/linked-lists-answers.txt | 4 ++ .../03-linked-lists/linked_list.rb | 66 ++++++++++++++++++- .../03-linked-lists/linked_list_spec.rb | 2 +- 01-data-structures/03-linked-lists/node.rb | 4 +- 4 files changed, 73 insertions(+), 3 deletions(-) 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..ba89be7f 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,4 @@ +spacial Locality involves accessing items that are close together in memory, as +in an array. This benefits performance as it limits the number of RAM lookups which +are more time consuming. + diff --git a/01-data-structures/03-linked-lists/linked_list.rb b/01-data-structures/03-linked-lists/linked_list.rb index 5ee2a533..3dfea23c 100644 --- a/01-data-structures/03-linked-lists/linked_list.rb +++ b/01-data-structures/03-linked-lists/linked_list.rb @@ -4,27 +4,91 @@ class LinkedList attr_accessor :head attr_accessor :tail + def initialize + @head = nil + @tail = @head + end + # This method creates a new `Node` using `data`, and inserts it at the end of the list. def add_to_tail(node) + if !@head + @head = node + @tail = node + else + currentNode = @head + + while(currentNode.next != nil) + currentNode = currentNode.next + end + + currentNode.next = node + @tail = node + end end # This method removes the last node in the lists and must keep the rest of the list intact. def remove_tail + currentNode = @head + + while currentNode.next + previousNode = currentNode + currentNode = currentNode.next + end + + if @head.next + currentNode = nil + previousNode.next = nil + @tail = previousNode + else + @head = nil + @tail = nil + end + end # This method prints out a representation of the list. def print + currentNode = @head + + while currentNode + puts currentNode.data + currentNode = currentNode.next + end end # This method removes `node` from the list and must keep the rest of the list intact. def delete(node) + currentNode = @head + + if(node == @head) + remove_front + + elsif(node == @tail) + remove_tail + + else + + while currentNode != node + previousNode = currentNode + currentNode = currentNode.next + end + + previousNode.next = currentNode.next + currentNode = nil + end end # This method adds `node` to the front of the list and must set the list's head to `node`. def add_to_front(node) + old_head = @head + @head = node + @head.next = old_head 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 + front = @head + @head = @head.next + front 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 8d60abe511f835d05d788f290da3f653f8b642c0 Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Tue, 9 Oct 2018 12:53:46 -0600 Subject: [PATCH 04/10] assignment completion --- .../03-linked-lists/linked-lists-answers.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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 ba89be7f..28d717d9 100644 --- a/01-data-structures/03-linked-lists/linked-lists-answers.txt +++ b/01-data-structures/03-linked-lists/linked-lists-answers.txt @@ -1,4 +1,15 @@ -spacial Locality involves accessing items that are close together in memory, as + Spacial Locality involves accessing items that are close together in memory, as in an array. This benefits performance as it limits the number of RAM lookups which are more time consuming. - + + I have created the benchmark tests for the array and the linked list. I found +array out performed the linked list in all 3 tests. When creating the 10,000 item +array and appending 10,000 items to the linked list the array completed slightly +more then 4 seconds before the linked list. The list had to move through all previous +nodes to add to the tail node, explaining why it took so much longer. Accessing +the 5,000th element and deleting the 5,000 element were much closer as far as time +but the array was still faster in both tests. + + I have attached links to my benchmark tests on repl.it. +Array Benchmarks: https://repl.it/@ConSou/Array-Benchmark +Linked List Benchmarks: https://repl.it/@ConSou/Linked-List-Benchmark From 421fa9004873797828f97bee03d9383ae974a66a Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Tue, 9 Oct 2018 18:13:24 -0600 Subject: [PATCH 05/10] hashclass work --- .../04-hashes-part-1/hash_item.rb | 4 +- .../04-hashes-part-1/hashclass.rb | 36 +++++- .../04-hashes-part-1/hashclass_spec.rb | 114 +++++++++--------- 3 files changed, 95 insertions(+), 59 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..9a3f0063 100644 --- a/01-data-structures/04-hashes-part-1/hashclass.rb +++ b/01-data-structures/04-hashes-part-1/hashclass.rb @@ -5,23 +5,57 @@ def initialize(size) end def []=(key, value) + index_val = index(key, @items.length) + # p @items[index_val] + # p value + if @items[index_val] === nil || @items[index_val][1] === value + @items[index_val] = [key, value] + else + resize + end end def [](key) + puts "------------------" + p @items + index_val = index(key, @items.length) + @items[index_val][1] end def resize + @items += Array.new(@items.length) + + i = 0 + while i < @items.size + if @items[i] != nil + @items[index(@items[i][0], @items.size)] = [@items[i][0], @items[i][1]] + end + i += 1 + 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) + i = 0 + hash_code = 0 + + while(i < key.length) + hash_code += key[i].ord + i += 1 + end + + hash_code % size + + puts "------------------" + p hash_code % 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/hashclass_spec.rb b/01-data-structures/04-hashes-part-1/hashclass_spec.rb index 31b4ced6..9f9b618c 100644 --- a/01-data-structures/04-hashes-part-1/hashclass_spec.rb +++ b/01-data-structures/04-hashes-part-1/hashclass_spec.rb @@ -6,71 +6,71 @@ RSpec.describe HashClass, type: Class do let(:lotr_movies) { HashClass.new(6) } - describe "#index" do - it "creates a hash key based on the string value passed in" do - i = lotr_movies.index("The Lord of the Rings: The Fellowship of the Ring", 6) - expect(i).to eq 5 - end - end - - describe "#key" do - it "returns the sum of the ascii values of the string value" do - key = "test" - expect(lotr_movies.index(key, 6)).to eq 4 - end - end - - describe "#resize" do - it "doubles the size of the array when invoked" do - expect(lotr_movies.size).to eq 6 - lotr_movies.resize - expect(lotr_movies.size).to eq 12 - end - - it "copies existing values properly when the array is resized" do - movies = HashClass.new(30) - movies["A New Hope"] = "Average" - movies["Empire Strikes Back"] = "Excellent" - movies["Return of the Jedi"] = "The Best" - movies.resize - expect(movies.size).to eq 60 - expect(movies["A New Hope"]).to eq "Average" - expect(movies["Empire Strikes Back"]).to eq "Excellent" - expect(movies["Return of the Jedi"]).to eq "The Best" - end - end + # describe "#index" do + # it "creates a hash key based on the string value passed in" do + # i = lotr_movies.index("The Lord of the Rings: The Fellowship of the Ring", 6) + # expect(i).to eq 5 + # end + # end + # + # describe "#key" do + # it "returns the sum of the ascii values of the string value" do + # key = "test" + # expect(lotr_movies.index(key, 6)).to eq 4 + # end + # end + # + # describe "#resize" do + # it "doubles the size of the array when invoked" do + # expect(lotr_movies.size).to eq 6 + # lotr_movies.resize + # expect(lotr_movies.size).to eq 12 + # end + # + # it "copies existing values properly when the array is resized" do + # movies = HashClass.new(30) + # movies["A New Hope"] = "Average" + # movies["Empire Strikes Back"] = "Excellent" + # movies["Return of the Jedi"] = "The Best" + # movies.resize + # expect(movies.size).to eq 60 + # expect(movies["A New Hope"]).to eq "Average" + # expect(movies["Empire Strikes Back"]).to eq "Excellent" + # expect(movies["Return of the Jedi"]).to eq "The Best" + # end + # end describe "hash[key] = value" do - it "does not resize the array when a collision occurs and the values match" do - hash = HashClass.new(1) - hash["key"] = "value" - expect(hash.size).to eq 1 - hash["key"] = "value" - expect(hash.size).to eq 1 - end - - it "resizes the array when a collision occurs and the values do not match" do - hash = HashClass.new(1) - hash["key"] = "value" - expect(hash.size).to eq 1 - hash["key"] = "different" - expect(hash.size).to eq 2 - end + # it "does not resize the array when a collision occurs and the values match" do + # hash = HashClass.new(1) + # hash["key"] = "value" + # expect(hash.size).to eq 1 + # hash["key"] = "value" + # expect(hash.size).to eq 1 + # end + # + # it "resizes the array when a collision occurs and the values do not match" do + # hash = HashClass.new(1) + # hash["key"] = "value" + # expect(hash.size).to eq 1 + # hash["key"] = "different" + # expect(hash.size).to eq 2 + # end it "sets the value of key to value" do lotr_movies["The Lord of the Rings: The Fellowship of the Ring"] = "3 hours, 48 minutes" - lotr_movies["The Lord of the Rings: The Two Towers"] = "3 hours, 55 minutes" + #lotr_movies["The Lord of the Rings: The Two Towers"] = "3 hours, 55 minutes" lotr_movies["The Lord of the Rings: The Return of the King"] = "3 hours, 21 minutes" - lotr_movies["The Hobbit: An Unexpected Journey"] = "3 hours, 2 minutes" - lotr_movies["The Hobbit: The Desolation of Smaug"] = "3 hours, 7 minutes" - lotr_movies["The Hobbit: The Battle of Five Armies"] = "2 hours, 44 minutes" + #lotr_movies["The Hobbit: An Unexpected Journey"] = "3 hours, 2 minutes" + #lotr_movies["The Hobbit: The Desolation of Smaug"] = "3 hours, 7 minutes" + #lotr_movies["The Hobbit: The Battle of Five Armies"] = "2 hours, 44 minutes" expect(lotr_movies["The Lord of the Rings: The Fellowship of the Ring"]).to eq "3 hours, 48 minutes" - expect(lotr_movies["The Lord of the Rings: The Two Towers"]).to eq "3 hours, 55 minutes" + #expect(lotr_movies["The Lord of the Rings: The Two Towers"]).to eq "3 hours, 55 minutes" expect(lotr_movies["The Lord of the Rings: The Return of the King"]).to eq "3 hours, 21 minutes" - expect(lotr_movies["The Hobbit: An Unexpected Journey"]).to eq "3 hours, 2 minutes" - expect(lotr_movies["The Hobbit: The Desolation of Smaug"]).to eq "3 hours, 7 minutes" - expect(lotr_movies["The Hobbit: The Battle of Five Armies"]).to eq "2 hours, 44 minutes" + #expect(lotr_movies["The Hobbit: An Unexpected Journey"]).to eq "3 hours, 2 minutes" + #expect(lotr_movies["The Hobbit: The Desolation of Smaug"]).to eq "3 hours, 7 minutes" + #expect(lotr_movies["The Hobbit: The Battle of Five Armies"]).to eq "2 hours, 44 minutes" end end -end \ No newline at end of file +end From 3d6bdfe69af1d7eb6e00c4af8d85fe2dd4481a5a Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Tue, 9 Oct 2018 20:15:40 -0600 Subject: [PATCH 06/10] hashclass work --- .../04-hashes-part-1/hashclass_spec.rb | 112 +++++++++--------- .../04-hashes-part-1/hashes-1-answers.txt | 15 +++ 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/01-data-structures/04-hashes-part-1/hashclass_spec.rb b/01-data-structures/04-hashes-part-1/hashclass_spec.rb index 9f9b618c..5c7d0e97 100644 --- a/01-data-structures/04-hashes-part-1/hashclass_spec.rb +++ b/01-data-structures/04-hashes-part-1/hashclass_spec.rb @@ -6,71 +6,71 @@ RSpec.describe HashClass, type: Class do let(:lotr_movies) { HashClass.new(6) } - # describe "#index" do - # it "creates a hash key based on the string value passed in" do - # i = lotr_movies.index("The Lord of the Rings: The Fellowship of the Ring", 6) - # expect(i).to eq 5 - # end - # end - # - # describe "#key" do - # it "returns the sum of the ascii values of the string value" do - # key = "test" - # expect(lotr_movies.index(key, 6)).to eq 4 - # end - # end - # - # describe "#resize" do - # it "doubles the size of the array when invoked" do - # expect(lotr_movies.size).to eq 6 - # lotr_movies.resize - # expect(lotr_movies.size).to eq 12 - # end - # - # it "copies existing values properly when the array is resized" do - # movies = HashClass.new(30) - # movies["A New Hope"] = "Average" - # movies["Empire Strikes Back"] = "Excellent" - # movies["Return of the Jedi"] = "The Best" - # movies.resize - # expect(movies.size).to eq 60 - # expect(movies["A New Hope"]).to eq "Average" - # expect(movies["Empire Strikes Back"]).to eq "Excellent" - # expect(movies["Return of the Jedi"]).to eq "The Best" - # end - # end + describe "#index" do + it "creates a hash key based on the string value passed in" do + i = lotr_movies.index("The Lord of the Rings: The Fellowship of the Ring", 6) + expect(i).to eq 5 + end + end + + describe "#key" do + it "returns the sum of the ascii values of the string value" do + key = "test" + expect(lotr_movies.index(key, 6)).to eq 4 + end + end + + describe "#resize" do + it "doubles the size of the array when invoked" do + expect(lotr_movies.size).to eq 6 + lotr_movies.resize + expect(lotr_movies.size).to eq 12 + end + + it "copies existing values properly when the array is resized" do + movies = HashClass.new(30) + movies["A New Hope"] = "Average" + movies["Empire Strikes Back"] = "Excellent" + movies["Return of the Jedi"] = "The Best" + movies.resize + expect(movies.size).to eq 60 + expect(movies["A New Hope"]).to eq "Average" + expect(movies["Empire Strikes Back"]).to eq "Excellent" + expect(movies["Return of the Jedi"]).to eq "The Best" + end + end describe "hash[key] = value" do - # it "does not resize the array when a collision occurs and the values match" do - # hash = HashClass.new(1) - # hash["key"] = "value" - # expect(hash.size).to eq 1 - # hash["key"] = "value" - # expect(hash.size).to eq 1 - # end - # - # it "resizes the array when a collision occurs and the values do not match" do - # hash = HashClass.new(1) - # hash["key"] = "value" - # expect(hash.size).to eq 1 - # hash["key"] = "different" - # expect(hash.size).to eq 2 - # end + it "does not resize the array when a collision occurs and the values match" do + hash = HashClass.new(1) + hash["key"] = "value" + expect(hash.size).to eq 1 + hash["key"] = "value" + expect(hash.size).to eq 1 + end + + it "resizes the array when a collision occurs and the values do not match" do + hash = HashClass.new(1) + hash["key"] = "value" + expect(hash.size).to eq 1 + hash["key"] = "different" + expect(hash.size).to eq 2 + end it "sets the value of key to value" do lotr_movies["The Lord of the Rings: The Fellowship of the Ring"] = "3 hours, 48 minutes" - #lotr_movies["The Lord of the Rings: The Two Towers"] = "3 hours, 55 minutes" + lotr_movies["The Lord of the Rings: The Two Towers"] = "3 hours, 55 minutes" lotr_movies["The Lord of the Rings: The Return of the King"] = "3 hours, 21 minutes" - #lotr_movies["The Hobbit: An Unexpected Journey"] = "3 hours, 2 minutes" - #lotr_movies["The Hobbit: The Desolation of Smaug"] = "3 hours, 7 minutes" - #lotr_movies["The Hobbit: The Battle of Five Armies"] = "2 hours, 44 minutes" + lotr_movies["The Hobbit: An Unexpected Journey"] = "3 hours, 2 minutes" + lotr_movies["The Hobbit: The Desolation of Smaug"] = "3 hours, 7 minutes" + lotr_movies["The Hobbit: The Battle of Five Armies"] = "2 hours, 44 minutes" expect(lotr_movies["The Lord of the Rings: The Fellowship of the Ring"]).to eq "3 hours, 48 minutes" - #expect(lotr_movies["The Lord of the Rings: The Two Towers"]).to eq "3 hours, 55 minutes" + expect(lotr_movies["The Lord of the Rings: The Two Towers"]).to eq "3 hours, 55 minutes" expect(lotr_movies["The Lord of the Rings: The Return of the King"]).to eq "3 hours, 21 minutes" - #expect(lotr_movies["The Hobbit: An Unexpected Journey"]).to eq "3 hours, 2 minutes" - #expect(lotr_movies["The Hobbit: The Desolation of Smaug"]).to eq "3 hours, 7 minutes" - #expect(lotr_movies["The Hobbit: The Battle of Five Armies"]).to eq "2 hours, 44 minutes" + expect(lotr_movies["The Hobbit: An Unexpected Journey"]).to eq "3 hours, 2 minutes" + expect(lotr_movies["The Hobbit: The Desolation of Smaug"]).to eq "3 hours, 7 minutes" + expect(lotr_movies["The Hobbit: The Battle of Five Armies"]).to eq "2 hours, 44 minutes" end end 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..b7256158 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,15 @@ + In order to deal with collisions, this checkpoint instructed us to double the +length of the array and re-map the keys with a new array size. This made it +relatively simple to fix collisions. + + Although this is an easy way to deal with collisions it is not the most effective. +There are a couple things to consider about this option and why it may not be +the best. As you double the size of larger arrays they grow faster and faster, +potentially taking up too much space. This process is also allocating memory +which is not a quick process. In addition if collisions are happening frequently +the items in your array may be very spaced out within that array wasting all the space +in between, which would not be very efficient. + + While doubling the size of an array may work in certain scenarios there are +definitely better solutions to this problem, such as the expansion method described +in the checkpoint (increasing to a prime number near the next largest power of two). From adff546f3207e067ab8c724e301e11a7a6a8d21b Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Wed, 10 Oct 2018 14:41:50 -0600 Subject: [PATCH 07/10] assignment-4 completion --- .../04-hashes-part-1/hashclass.rb | 54 +++++++++---------- .../04-hashes-part-1/hashes-1-answers.txt | 13 ++--- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/01-data-structures/04-hashes-part-1/hashclass.rb b/01-data-structures/04-hashes-part-1/hashclass.rb index 9a3f0063..ca7ae639 100644 --- a/01-data-structures/04-hashes-part-1/hashclass.rb +++ b/01-data-structures/04-hashes-part-1/hashclass.rb @@ -5,52 +5,46 @@ def initialize(size) end def []=(key, value) - index_val = index(key, @items.length) - # p @items[index_val] - # p value - if @items[index_val] === nil || @items[index_val][1] === value - @items[index_val] = [key, value] - else - resize + index_val = index(key, size) + new_item = @items[index_val] + + if new_item == nil + @items[index_val] = HashItem.new(key, value) + elsif new_item.key != key + while @items[index(key, size)].key != nil && @items[index(key, size)].key != key + resize + i = index(key, size) + break if @items[i] == nil + end + self[key] = value + elsif new_item.key == key && new_item.value != value + resize() + new_item.value = value end end def [](key) - puts "------------------" - p @items - index_val = index(key, @items.length) - @items[index_val][1] + index_val = index(key, size) + @items[index_val].value end def resize - @items += Array.new(@items.length) - - i = 0 - while i < @items.size - if @items[i] != nil - @items[index(@items[i][0], @items.size)] = [@items[i][0], @items[i][1]] + resized_hash = @items += Array.new(size) + + @items.each do |i| + if i != nil + resized_hash[index(i.key, size)] = i end - i += 1 end + @items = resized_hash end # Returns a unique, deterministically reproducible index into an array # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) - i = 0 - hash_code = 0 - - while(i < key.length) - hash_code += key[i].ord - i += 1 - end - - hash_code % size - - puts "------------------" - p hash_code % size + key.sum % size end # Simple method to return the number of items in the hash 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 b7256158..c5b35311 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,14 +1,15 @@ In order to deal with collisions, this checkpoint instructed us to double the -length of the array and re-map the keys with a new array size. This made it +length of the array and re-map the keys with the new array size. This made it relatively simple to fix collisions. Although this is an easy way to deal with collisions it is not the most effective. There are a couple things to consider about this option and why it may not be -the best. As you double the size of larger arrays they grow faster and faster, -potentially taking up too much space. This process is also allocating memory -which is not a quick process. In addition if collisions are happening frequently -the items in your array may be very spaced out within that array wasting all the space -in between, which would not be very efficient. +the best. As you double the size of arrays they will grow faster and faster, +potentially taking up too much space. This process also requires allocating more +memory which is not a quick task. In addition if collisions are happening +frequently the array is doubling making it so items in your array are more spaced +out, leaving a lot of nil values in the array and wasting space, which would not be very +efficient. While doubling the size of an array may work in certain scenarios there are definitely better solutions to this problem, such as the expansion method described From 54fdce27625714d70f163ef7e48de12bbe2e7969 Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Wed, 10 Oct 2018 19:46:34 -0600 Subject: [PATCH 08/10] checkpoint-5 work --- .../05-hashes-part-2/open_addressing/node.rb | 4 +- .../open_addressing/open_addressing.rb | 55 ++++++++++++++++- .../separate_chaining/linked_list.rb | 60 ++++++++++++++++++- .../separate_chaining/node.rb | 5 +- .../separate_chaining/separate_chaining.rb | 22 +++++++ 5 files changed, 142 insertions(+), 4 deletions(-) diff --git a/01-data-structures/05-hashes-part-2/open_addressing/node.rb b/01-data-structures/05-hashes-part-2/open_addressing/node.rb index 07f867b7..ce37ad7b 100644 --- a/01-data-structures/05-hashes-part-2/open_addressing/node.rb +++ b/01-data-structures/05-hashes-part-2/open_addressing/node.rb @@ -4,5 +4,7 @@ class Node attr_accessor :value def initialize(key, value) + @key = key + @value = value end -end \ No newline at end of file +end diff --git a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb index 30db5754..8fe6efd9 100644 --- a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb +++ b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb @@ -1,30 +1,83 @@ +require 'prime' require_relative 'node' class OpenAddressing def initialize(size) + @items = Array.new(size) end def []=(key, value) + index = index(key, size) + + if @items[index] == nil + @items[index] = Node.new(key, value) + elsif next_open_index(0) == -1 + resize + self[key] = value + else + index = next_open_index(index) + if index == -1 + resize + self[key] = value + elsif @items[index] == nil + @items[index] = Node.new(key, value) + end + end end def [](key) + index = 0 + # puts "--------------------" + # p @items + # p index(key, size) + until index == size + if @items[index] == nil + index += 1 + elsif @items[index].key == key + return @items[index].value + else + index += 1 + end + end end # Returns a unique, deterministically reproducible index into an array # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) + key.sum % size end # Given an index, find the next open index in @items def next_open_index(index) + while @items[index] + index += 1 + if index === size + return -1 + end + end + index end # Simple method to return the number of items in the hash def size + @items.length end # Resize the hash def resize + resized_hash = Array.new(size * 2) + + @items.each do |i| + if i != nil + resized_hash[index(i.key, size)] = i + end + end + @items = resized_hash end -end \ No newline at end of file + + def print_hash + p @items + end + +end diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb b/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb index 5ee2a533..45eb0e3c 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb @@ -6,25 +6,83 @@ class LinkedList # This method creates a new `Node` using `data`, and inserts it at the end of the list. def add_to_tail(node) + if !@head + @head = node + @tail = node + else + currentNode = @head + + while(currentNode.next != nil) + currentNode = currentNode.next + end + + currentNode.next = node + @tail = node + end end # This method removes the last node in the lists and must keep the rest of the list intact. def remove_tail + currentNode = @head + + while currentNode.next + previousNode = currentNode + currentNode = currentNode.next + end + + if @head.next + currentNode = nil + previousNode.next = nil + @tail = previousNode + else + @head = nil + @tail = nil + end end # This method prints out a representation of the list. def print + currentNode = @head + + while currentNode + puts currentNode.data + currentNode = currentNode.next + end end # This method removes `node` from the list and must keep the rest of the list intact. def delete(node) + currentNode = @head + + if(node == @head) + remove_front + + elsif(node == @tail) + remove_tail + + else + + while currentNode != node + previousNode = currentNode + currentNode = currentNode.next + end + + previousNode.next = currentNode.next + currentNode = nil + end end # This method adds `node` to the front of the list and must set the list's head to `node`. def add_to_front(node) + old_head = @head + @head = node + @head.next = old_head 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 + front = @head + @head = @head.next + front end -end \ No newline at end of file +end diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/node.rb b/01-data-structures/05-hashes-part-2/separate_chaining/node.rb index 07f867b7..b8724673 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/node.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/node.rb @@ -4,5 +4,8 @@ class Node attr_accessor :value def initialize(key, value) + @key = key + @value = value + @next = nil end -end \ No newline at end of file +end diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb index 1b46c46d..e1a9d755 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb @@ -5,9 +5,20 @@ class SeparateChaining def initialize(size) @max_load_factor = 0.7 + @items = Array.new(size) + @item_count = 0 end def []=(key, value) + # index = index(key, size) + # new_node = Node.new(key, value) + # + # if @items[index] == nil + # @items[index] = new_node + # @head = new_node + # elsif load_factor < @max_load_factor + # @items[index].add_to_tail(new_node) + # end end def [](key) @@ -17,17 +28,28 @@ def [](key) # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) + key.sum % size end # Calculate the current load factor def load_factor + @item_count / size end # Simple method to return the number of items in the hash def size + @items.length end # Resize the hash def resize + resized_hash = Array.new(size * 2) + + @items.each do |i| + if i != nil + resized_hash[index(i.key, size)] = i + end + end + @items = resized_hash end end From b421bc2b4ed810ab75ea3bef25b97e979db353ae Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Thu, 11 Oct 2018 21:07:04 -0600 Subject: [PATCH 09/10] hashes-2 work --- .../hashes-part-2-answers.txt | 16 ++++ .../open_addressing/open_addressing.rb | 52 ++++++++----- .../open_addressing/open_addressing_spec.rb | 12 ++- .../separate_chaining/linked_list.rb | 13 ++-- .../separate_chaining/separate_chaining.rb | 78 ++++++++++++++++--- .../separate_chaining_spec.rb | 2 +- 6 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt diff --git a/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt b/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt new file mode 100644 index 00000000..73de2159 --- /dev/null +++ b/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt @@ -0,0 +1,16 @@ +Q: Describe three collision resolution strategies not mentioned here. + +A: The first collision resolution I found that wasn't mentioned in bloc's curriculum +is the Coalesced hashing strategy. It is described as a mix between chaining and +open addressing. This method allows for better use of space like open addressing. +Each slot gets a single element, and if a collision occurs the node in the slot +stores a link to the next open index value. This allows for a quicker look up like +you get with chaining. + + Another method not mentioned is "Separate chaining with list head cells". It is +very similar to the chaining method we discussed in the checkpoint. The standout +difference is that it stores the first element of each chain in the slot itself, +reducing the pointer traversals by one. While it is a slight difference it could +be valuable to get that extra boost of performance in certain scenarios. + + A third method that wasn't in the curriculum is Hopscotch hashing. diff --git a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb index 8fe6efd9..dd5b2463 100644 --- a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb +++ b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing.rb @@ -1,42 +1,37 @@ -require 'prime' require_relative 'node' class OpenAddressing def initialize(size) @items = Array.new(size) + @item_count = 0.0 end def []=(key, value) index = index(key, size) - if @items[index] == nil + if @items[index] == nil || @items[index].key === key @items[index] = Node.new(key, value) - elsif next_open_index(0) == -1 + elsif next_open_index(index) == -1 resize self[key] = value else - index = next_open_index(index) - if index == -1 - resize - self[key] = value - elsif @items[index] == nil + index = next_open_index(index) @items[index] = Node.new(key, value) - end end + @item_count += 1 + #hash_status end def [](key) - index = 0 - # puts "--------------------" - # p @items - # p index(key, size) + index = index(key, size) + + if @items[index].key == key + return @items[index].value + end until index == size - if @items[index] == nil index += 1 - elsif @items[index].key == key + if @items[index].key == key return @items[index].value - else - index += 1 end end end @@ -70,14 +65,31 @@ def resize @items.each do |i| if i != nil - resized_hash[index(i.key, size)] = i + resized_hash[index(i.key, resized_hash.length)] = i end end @items = resized_hash end - def print_hash - p @items + def load_factor + @item_count / size + end + + def hash_status + j = 0 + p "-------------------" + p "Load Factor: #{load_factor}" + @items.each do |i| + if i != nil + p "Index: #{j}" + p "Key: #{i.key}" + p "Value: #{i.value}" + p "~~~~~~~~~~~~~~~~~" + j += 1 + end + end + p "-------------------" + end end diff --git a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing_spec.rb b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing_spec.rb index 2611b64d..73b3f1d6 100644 --- a/01-data-structures/05-hashes-part-2/open_addressing/open_addressing_spec.rb +++ b/01-data-structures/05-hashes-part-2/open_addressing/open_addressing_spec.rb @@ -42,10 +42,20 @@ hash = OpenAddressing.new(1) hash["key"] = "value" expect(hash.size).to eq 1 - hash["key"] = "second value" + # issues in test, two like keys should not cause a collision. I have added + # additional testing + # hash["key"] = "second value" + # expect(hash.size).to eq 2 + hash["key_two"] = "second value" + expect(hash.size).to eq 2 + hash["key_two"] = "Change value" expect(hash.size).to eq 2 + expect(hash["key_two"]).to eq "Change value" + hash["key_three"] = "Three Value" + expect(hash.size).to eq 4 end + it "sets the value of key to value" do expect(star_wars_movies["Star Wars: The Phantom Menace"]).to eq "Number One" expect(star_wars_movies["Star Wars: Attack of the Clones"]).to eq "Number Two" diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb b/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb index 45eb0e3c..d8565a0a 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/linked_list.rb @@ -4,12 +4,13 @@ class LinkedList attr_accessor :head attr_accessor :tail + def initialize(head) + @head = head + @tail = @head + end + # This method creates a new `Node` using `data`, and inserts it at the end of the list. def add_to_tail(node) - if !@head - @head = node - @tail = node - else currentNode = @head while(currentNode.next != nil) @@ -18,7 +19,6 @@ def add_to_tail(node) currentNode.next = node @tail = node - end end # This method removes the last node in the lists and must keep the rest of the list intact. @@ -45,7 +45,8 @@ def print currentNode = @head while currentNode - puts currentNode.data + p "Key: #{currentNode.key}" + p "Value: #{currentNode.value}" currentNode = currentNode.next end end diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb index e1a9d755..8852d7dd 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining.rb @@ -6,22 +6,46 @@ class SeparateChaining def initialize(size) @max_load_factor = 0.7 @items = Array.new(size) - @item_count = 0 + @item_count = 0.0 end def []=(key, value) - # index = index(key, size) - # new_node = Node.new(key, value) - # - # if @items[index] == nil - # @items[index] = new_node - # @head = new_node - # elsif load_factor < @max_load_factor - # @items[index].add_to_tail(new_node) - # end + index = index(key, size) + new_node = Node.new(key, value) + + if @items[index] == nil + @items[index] = new_node + + elsif @items[index].instance_variable_defined?(:@next) + ll = LinkedList.new(@items[index]) + ll.add_to_tail(new_node) + @items[index] = ll + + elsif @items[index].instance_variable_defined?(:@head) + @items[index].add_to_tail(new_node) + end + + @item_count += 1 + + if load_factor > @max_load_factor + resize + end + + #hash_status end def [](key) + index = index(key, size) + + if @items[index].instance_variable_defined?(:@head) + current_node = @items[index].head + until current_node.key == key + current_node = current_node.next + end + return current_node.value + else + @items[index].value + end end # Returns a unique, deterministically reproducible index into an array @@ -46,10 +70,40 @@ def resize resized_hash = Array.new(size * 2) @items.each do |i| - if i != nil - resized_hash[index(i.key, size)] = i + if i != nil && i.instance_variable_defined?(:@next) + resized_hash[index(i.key, resized_hash.length)] = i + elsif i.instance_variable_defined?(:@head) + resized_hash[index(i.head.key, resized_hash.length)] = i end end @items = resized_hash end + + def hash_status + j = 0 + p "-------------------" + p "Load Factor: #{load_factor}" + @items.each do |i| + if i != nil + if i.instance_variable_defined?(:@next) + p i + p "Index: #{j}" + p "Key: #{i.key}" + p "Value: #{i.value}" + p "~~~~~~~~~~~~~~~~~" + j += 1 + end + if i.instance_variable_defined?(:@head) + p i + p "Index: #{j}" + p "Linked List:" + p i.print + p "~~~~~~~~~~~~~~~~~" + j += 1 + end + end + end + p "-------------------" + + end end diff --git a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining_spec.rb b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining_spec.rb index 4013ed2a..66a6e086 100644 --- a/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining_spec.rb +++ b/01-data-structures/05-hashes-part-2/separate_chaining/separate_chaining_spec.rb @@ -65,7 +65,7 @@ # Load factor should be .5 when two items are added expect(h.load_factor).to eq 0.5 - h["keytwo"] = "value" + h["keythree"] = "value" # Load factor goes down to .375 (3/8) since when third item is added, load factor goes to .75 # then the resize is triggered and load factor is recalculated From 4b102e19154f0200848e5e4f0fc6e0bcb38db8bc Mon Sep 17 00:00:00 2001 From: Conor Souhrada Date: Mon, 15 Oct 2018 15:41:24 -0600 Subject: [PATCH 10/10] assignemnt-5 completion --- .../hashes-part-2-answers.txt | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt b/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt index 73de2159..ae2fbf6d 100644 --- a/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt +++ b/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt @@ -13,4 +13,44 @@ difference is that it stores the first element of each chain in the slot itself, reducing the pointer traversals by one. While it is a slight difference it could be valuable to get that extra boost of performance in certain scenarios. - A third method that wasn't in the curriculum is Hopscotch hashing. + A third method that wasn't in the curriculum is Hopscotch hashing. This method +utilizes a single array and for each slot in the array it creates a "neighborhood" +of small consecutive slots. Because of the close proximity of the "neighborhood" +the cost of finding an item with in this area is close to the cost of finding the +desired slot itself. If there isn't a spot in the "neighborhood" it uses linear +probing to find an available slot then must displace items to move this open spot +closer to the original and into the "neighborhood", which can be an expensive process. + + +Q: Create your own collision resolution strategy and describe how it works. + +A: After researching many different collision resolution strategies, and spending +a fair amount of time brain storming one of my own I came with a couple ideas. +One that incorporates separate chaining but will potentially help with the time +it takes to recall an element when a collision has taken place. My second idea +uses binary search to help with the look up when multiple keys collide with +one index. Both will utilize load factors and dynamic resizing to ensure that they +are as performant as possible. + + My first method would use linked lists and would traverse the list to find other nodes that +have collided with this index. Though I would include a counter instance variable +that would increment as the node is accessed. The linked list would be ordered by +number of times items were need, placing the most accessed item in the list at +the index and linking the second most and so on down the list. As an item is accessed +more it will be in turn be faster to access. On the fist collision in an index the second +item added will be placed at the end of the linked list, as item[i] is called its +counter would increase and if it is accessed more it will become quicker to find, thus +reducing traversals for items that have been accessed more frequently. While this +method would be harder to implement I think it could add a slight performance boost +in the right situations. + + Another method I came up with would incorporate a binary search. When a collision +occurs an array would be placed in the index location. The keys that collided +would then be placed in alphabetical order. Upon look up a binary search would run +looking for the specific key. This would be useful in hash tables that have a lot +of collisions although it would be harder to configure and set up. + + Coming up with collision resolution strategies was a difficult assignment, as +many of the most performant and space conscious methods and ideas have already been +created. While I know my methods are not perfect I enjoyed thinking about this topic +and trying to come up with some of my own.