diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..61190987 Binary files /dev/null and b/.DS_Store differ diff --git a/01-data-structures/.DS_Store b/01-data-structures/.DS_Store new file mode 100644 index 00000000..83496f7f Binary files /dev/null and b/01-data-structures/.DS_Store differ 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) 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 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..28d717d9 100644 --- a/01-data-structures/03-linked-lists/linked-lists-answers.txt +++ b/01-data-structures/03-linked-lists/linked-lists-answers.txt @@ -0,0 +1,15 @@ + 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 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 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..ca7ae639 100644 --- a/01-data-structures/04-hashes-part-1/hashclass.rb +++ b/01-data-structures/04-hashes-part-1/hashclass.rb @@ -5,23 +5,51 @@ def initialize(size) end def []=(key, value) + 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) + index_val = index(key, size) + @items[index_val].value end def resize + resized_hash = @items += Array.new(size) + + @items.each do |i| + if i != nil + resized_hash[index(i.key, size)] = i + end + end + @items = resized_hash end # Returns a unique, deterministically reproducible index into an array # We are hashing based on strings, let's use the ascii value of each string as # a starting point. def index(key, size) + key.sum % size end # Simple method to return the number of items in the hash def size + @items.length end -end \ No newline at end of file +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..5c7d0e97 100644 --- a/01-data-structures/04-hashes-part-1/hashclass_spec.rb +++ b/01-data-structures/04-hashes-part-1/hashclass_spec.rb @@ -73,4 +73,4 @@ 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 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..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 @@ -0,0 +1,16 @@ + In order to deal with collisions, this checkpoint instructed us to double the +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 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 +in the checkpoint (increasing to a prime number near the next largest power of two). 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..ae2fbf6d --- /dev/null +++ b/01-data-structures/05-hashes-part-2/hashes-part-2-answers.txt @@ -0,0 +1,56 @@ +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. 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. 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..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 @@ -2,29 +2,94 @@ 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 || @items[index].key === key + @items[index] = Node.new(key, value) + elsif next_open_index(index) == -1 + resize + self[key] = value + else + index = next_open_index(index) + @items[index] = Node.new(key, value) + end + @item_count += 1 + #hash_status end def [](key) + index = index(key, size) + + if @items[index].key == key + return @items[index].value + end + until index == size + index += 1 + if @items[index].key == key + return @items[index].value + 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, resized_hash.length)] = i + end + end + @items = resized_hash + end + + def load_factor + @item_count / size end -end \ No newline at end of file + + 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 5ee2a533..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,27 +4,86 @@ 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) + currentNode = @head + + while(currentNode.next != nil) + currentNode = currentNode.next + end + + currentNode.next = node + @tail = node 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 + p "Key: #{currentNode.key}" + p "Value: #{currentNode.value}" + 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..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 @@ -5,29 +5,105 @@ class SeparateChaining def initialize(size) @max_load_factor = 0.7 + @items = Array.new(size) + @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 + + 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 # 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 && 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