diff --git a/lib/linked_list.rb b/lib/linked_list.rb index 136d8ac9..5e658fbc 100644 --- a/lib/linked_list.rb +++ b/lib/linked_list.rb @@ -2,7 +2,7 @@ class Node attr_reader :data # allow external entities to read value but not write attr_accessor :next, :previous # allow external entities to read or write next node - + def initialize(value, next_node = nil, previous_node = nil) @data = value @next = next_node @@ -12,331 +12,331 @@ def initialize(value, next_node = nil, previous_node = nil) # Defines the singly linked list class LinkedList - def initialize - @head = nil # keep the head private. Not accessible outside this class - @tail = nil - end - - # method to add a new node with the specific data value in the linked list - # insert the new node at the beginning of the linked list - # Time Complexity: O(1) - # Space Complexity O(1) - def add_first(value) - new_node = Node.new(value) - new_node.next = @head - - @head.previous = new_node unless @head.nil? - @head = new_node - if @tail.nil? - @tail = @head - end - end - - def remove_first() - raise ArgumentError, "Empty" if self.empty? - - value = @head.data - @head = @head.next - @head.previous = nil - return value + def initialize + @head = nil # keep the head private. Not accessible outside this class + @tail = nil + end + + # method to add a new node with the specific data value in the linked list + # insert the new node at the beginning of the linked list + # Time Complexity: O(1) + # Space Complexity O(1) + def add_first(value) + new_node = Node.new(value) + new_node.next = @head + + @head.previous = new_node unless @head.nil? + @head = new_node + if @tail.nil? + @tail = @head end - - def empty? - return @head.nil? + end + + def remove_first() + raise ArgumentError, "Empty" if self.empty? + + value = @head.data + @head = @head.next + @head.previous = nil unless @head.nil? + return value + end + + def empty? + return @head.nil? + end + + # method to find if the linked list contains a node with specified value + # returns true if found, false otherwise + # Time Complexity: O(n) + # Space Complexity: O(1) + def search(value) + return false if @head.nil? + return true if @head.data = value + current = @head + until current.data.nil? + return true if current.data = value + current = current.next end - - # method to find if the linked list contains a node with specified value - # returns true if found, false otherwise - # Time Complexity: O(n) - # Space Complexity: O(1) - def search(value) - return false if @head.nil? - return true if @head.data = value - current = @head - until current.data.nil? - return true if current.data = value - current = current.next - end + end + + # method to return the max value in the linked list + # returns the data value and not the node + # Time Complexity: O(n) + # Space Complexity: O(1) + def find_max + return nil if @head.nil? + current = @head + max = current.data + until current.nil? + max = current.data if current.data > max + current = current.next end - - # method to return the max value in the linked list - # returns the data value and not the node - # Time Complexity: O(n) - # Space Complexity: O(1) - def find_max - return nil if @head.nil? - current = @head - max = current.data - until current.nil? - max = current.data if current.data > max - current = current.next + return max + end + + # method to return the min value in the linked list + # returns the data value and not the node + # Time Complexity: O(n) + # Space Complexity: O(1) + def find_min + return nil if @head.nil? + current = @head + min = current.data + until current.nil? + if current.data < min + min = current.data end - return max + current = current.next end - - # method to return the min value in the linked list - # returns the data value and not the node - # Time Complexity: O(n) - # Space Complexity: O(1) - def find_min - return nil if @head.nil? - current = @head - min = current.data - until current.nil? - if current.data < min - min = current.data - end - current = current.next - end - return min + return min + end + + + # method that returns the length of the singly linked list + # Time Complexity: O(n) + # Space Complexity: O(1) + def length + return 0 if @head.nil? + length = 0 + current = @head + until current.nil? + current = current.next + length += 1 end - - - # method that returns the length of the singly linked list - # Time Complexity: O(n) - # Space Complexity: O(1) - def length - return 0 if @head.nil? - length = 0 - current = @head - until current.nil? - current = current.next - length += 1 - end - return length - + return length + + end + + # method that returns the value at a given index in the linked list + # index count starts at 0 + # returns nil if there are fewer nodes in the linked list than the index value + # Time Complexity: O(n) + # Space Complexity: O(1) + def get_at_index(index) + return nil if length <= index + current = @head + count = 0 + until current.nil? + return current.data if count == index + current = current.next + count += 1 end - - # method that returns the value at a given index in the linked list - # index count starts at 0 - # returns nil if there are fewer nodes in the linked list than the index value - # Time Complexity: O(n) - # Space Complexity: O(1) - def get_at_index(index) - return nil if length <= index - current = @head - count = 0 - until current.nil? - return current.data if count == index - current = current.next - count += 1 - end + end + + # method to print all the values in the linked list + # Time Complexity: O(n) + # Space Complexity: O(1) + def visit + current = @head + until current.nil? + puts current.data + current = current.next end - - # method to print all the values in the linked list - # Time Complexity: O(n) - # Space Complexity: O(1) - def visit - current = @head - until current.nil? - puts current.data - current = current.next - end + end + + # method to delete the first node found with specified value + # Time Complexity: O(n) where n is the number of nodes + # Space Complexity: O(1) + def delete(value) + return if @head.nil? + current = @head + + if current.data == value + @head = current.next + @head.previous = nil unless @head.nil? + return end - - # method to delete the first node found with specified value - # Time Complexity: O(n) where n is the number of nodes - # Space Complexity: O(1) - def delete(value) - return if @head.nil? - current = @head - + + prev = current + until current.nil? if current.data == value - @head = current.next - @head.previous = nil unless @head.nil? - return + prev.next = current.next + current.next.previous = prev unless current.next.nil? + @tail = prev if @tail == current + else + prev = current end - + current = current.next + end + end + + # method to reverse the singly linked list + # note: the nodes should be moved and not just the values in the nodes + # Time Complexity: O(n) where n is the number of nodes + # Space Complexity: O(1) + def reverse + return nil if @head.nil? + prev = nil + current = @head + until current.nil? + temp = current.next + current.next = prev prev = current - until current.nil? - if current.data == value - prev.next = current.next - current.next.previous = prev unless current.next.nil? - @tail = prev if @tail == current - else - prev = current - end - current = current.next - end + current = temp + prev.previous = current + end - - # method to reverse the singly linked list - # note: the nodes should be moved and not just the values in the nodes - # Time Complexity: O(n) where n is the number of nodes - # Space Complexity: O(1) - def reverse - return nil if @head.nil? - prev = nil - current = @head - until current.nil? - temp = current.next - current.next = prev - prev = current - current = temp - prev.previous = current - - end - - @head = prev - + + @head = prev + + end + + + ## Advanced Exercises + # returns the value at the middle element in the singly linked list + # Time Complexity: O(n) + # Space Complexity: O(1) + def find_middle_value + return nil if @head.nil? + current = @head + if length % 2 == 0 + return nil + else + middle_index = length / 2 end - - - ## Advanced Exercises - # returns the value at the middle element in the singly linked list - # Time Complexity: O(n) - # Space Complexity: O(1) - def find_middle_value - return nil if @head.nil? - current = @head - if length % 2 == 0 - return nil - else - middle_index = length / 2 - end - index = 0 - until current.nil? - return current.data if index == middle_index - current = current.next - index += 1 - end - + index = 0 + until current.nil? + return current.data if index == middle_index + current = current.next + index += 1 end - - # find the nth node from the end and return its value - # assume indexing starts at 0 while counting to n - # Time Complexity: O(n) - # Space Complexity: O(1) - def find_nth_from_end(n) - return nil if @head.nil? - current = @head - index = 0 - until current.nil? - if index == length - n - 1 - return current.data - end - index += 1 - current = current.next + + end + + # find the nth node from the end and return its value + # assume indexing starts at 0 while counting to n + # Time Complexity: O(n) + # Space Complexity: O(1) + def find_nth_from_end(n) + return nil if @head.nil? + current = @head + index = 0 + until current.nil? + if index == length - n - 1 + return current.data end + index += 1 + current = current.next end - - # checks if the linked list has a cycle. A cycle exists if any node in the - # linked list links to a node already visited. - # returns true if a cycle is found, false otherwise. - # Time Complexity: O(n) - # Space Complexity: O(1) - def has_cycle - return nil if @head.nil? - slow_p = @head - fast_p = @head - while slow_p != nil && fast_p != nil and fast_p.next != nil - slow_p = slow_p.next - fast_p = fast_p.next.next - if slow_p == fast_p - return true - end - return false + end + + # checks if the linked list has a cycle. A cycle exists if any node in the + # linked list links to a node already visited. + # returns true if a cycle is found, false otherwise. + # Time Complexity: O(n) + # Space Complexity: O(1) + def has_cycle + return nil if @head.nil? + slow_p = @head + fast_p = @head + while slow_p != nil && fast_p != nil and fast_p.next != nil + slow_p = slow_p.next + fast_p = fast_p.next.next + if slow_p == fast_p + return true end + return false end - - - # Additional Exercises - # returns the value in the first node - # returns nil if the list is empty - # Time Complexity: O(1) - # Space Complexity: O(1) - def get_first - return nil if @head.nil? - return @head.data - end - - # method that inserts a given value as a new last node in the linked list - # Time Complexity: O(n) where n is the number of nodes - # Space Complexity: O(1) - def add_last(value) - new_node = Node.new(value) - if @head.nil? - self.add_first(value) - return - end - @tail.next = new_node - new_node.previous = @tail - @tail = new_node + end + + + # Additional Exercises + # returns the value in the first node + # returns nil if the list is empty + # Time Complexity: O(1) + # Space Complexity: O(1) + def get_first + return nil if @head.nil? + return @head.data + end + + # method that inserts a given value as a new last node in the linked list + # Time Complexity: O(n) where n is the number of nodes + # Space Complexity: O(1) + def add_last(value) + new_node = Node.new(value) + if @head.nil? + self.add_first(value) + return end - - def remove_last() - value = @tail.data - if @head == @tail - @head = @tail = nil - else - @tail = @tail.previous - @tail.next = nil - end - - return value + @tail.next = new_node + new_node.previous = @tail + @tail = new_node + end + + def remove_last() + value = @tail.data + if @head == @tail + @head = @tail = nil + else + @tail = @tail.previous + @tail.next = nil end - - # method that returns the value of the last node in the linked list - # returns nil if the linked list is empty - # Time Complexity: O(n) where n is the number of nodes - # Space Complexity: O(1) - def get_last - return nil if @head.nil? - return @tail.data + + return value + end + + # method that returns the value of the last node in the linked list + # returns nil if the linked list is empty + # Time Complexity: O(n) where n is the number of nodes + # Space Complexity: O(1) + def get_last + return nil if @head.nil? + return @tail.data + end + + # method to insert a new node with specific data value, assuming the linked + # list is sorted in ascending order + # Time Complexity: O(n) + # Space Complexity: O(1) + def insert_ascending(value) + new_node = Node.new(value) + add_first(value) if @head.nil? + current = @head + if current.data > new_node.data + temp = @head + @head = new_node + temp.previous = @head + new_node.next = temp end - - # method to insert a new node with specific data value, assuming the linked - # list is sorted in ascending order - # Time Complexity: O(n) - # Space Complexity: O(1) - def insert_ascending(value) - new_node = Node.new(value) - add_first(value) if @head.nil? - current = @head - if current.data > new_node.data - temp = @head - @head = new_node - temp.previous = @head + + until current.nil? + if current.data <= value && current.next.data > value + temp = current.next + current.next = new_node new_node.next = temp + new_node.previous = current + temp.previous = new_node + return end - - until current.nil? - if current.data <= value && current.next.data > value - temp = current.next - current.next = new_node - new_node.next = temp - new_node.previous = current - temp.previous = new_node - return - end - current = current.next - end + current = current.next end - - # Helper method for tests - # Creates a cycle in the linked list for testing purposes - # Assumes the linked list has at least one node - def create_cycle - return if @head == nil # don't do anything if the linked list is empty - - # navigate to last node - current = @head - while current.next != nil - current = current.next - end - - current.next = @head # make the last node link to first node + end + + # Helper method for tests + # Creates a cycle in the linked list for testing purposes + # Assumes the linked list has at least one node + def create_cycle + return if @head == nil # don't do anything if the linked list is empty + + # navigate to last node + current = @head + while current.next != nil + current = current.next end - - def to_s - list = [] - - current = @head - until current.nil? - list << current.data - current = current.next - end - - return list.to_s + + current.next = @head # make the last node link to first node + end + + def to_s + list = [] + + current = @head + until current.nil? + list << current.data + current = current.next end -end \ No newline at end of file + + return list.to_s + end +end diff --git a/lib/problems.rb b/lib/problems.rb index 5085953d..f0e0d658 100644 --- a/lib/problems.rb +++ b/lib/problems.rb @@ -1,13 +1,69 @@ require_relative './stack.rb' -# Time Complexity: ? -# Space Complexity: ? +# Time Complexity: O(n), push and pop should be O(1) operations since the linked list has both a head and tail pointer +# Space Complexity: O(n) if the string is not balanced and comprised wholly of open brackets, also O(n) if it actually closes def balanced(string) - raise NotImplementedError, "Not implemented yet" + return false if string.length % 2 == 1 + return true if string.length == 0 + + stack = Stack.new + + string.each_char do |char| + case char + when "{" + stack.push("}") + when "[" + stack.push("]") + when "(" + stack.push(")") + when "}" + return false if stack.pop != "}" + when "]" + return false if stack.pop != "]" + when ")" + return false if stack.pop != ")" + else + return false + end + end + + return stack.empty? end -# Time Complexity: ? -# Space Complexity: ? +# Time Complexity: O(n) +# Space Complexity: O(n) def evaluate_postfix(postfix_expression) - raise NotImplementedError, "Not implemented yet" + return nil if postfix_expression.length == 0 + + stack = Stack.new + + postfix_expression.each_char do |char| + int_conversion = char.to_i + if int_conversion > 0 + stack.push(int_conversion) + elsif int_conversion == 0 && char == "0" + stack.push(int_conversion) + else + postfix_operation(char, stack) + end + end + + return stack.pop +end + +def postfix_operation(operator, stack) + operand_2 = stack.pop + operand_1 = stack.pop + + case operator + when "+" + stack.push(operand_1 + operand_2) + when "-" + stack.push(operand_1 - operand_2) + when "*" + stack.push(operand_1 * operand_2) + when "/" + raise ArgumentError, 'Cannot divide by 0' if (operand_1 == 0 || operand_2 == 0) + stack.push(operand_1 / operand_2) + end end diff --git a/lib/queue.rb b/lib/queue.rb index 828217c6..abb8303c 100644 --- a/lib/queue.rb +++ b/lib/queue.rb @@ -1,30 +1,47 @@ -class Queue +require_relative './linked_list' +class Queue + def initialize - # @store = ... - raise NotImplementedError, "Not yet implemented" + # @store = Array.new(10) + # -1 is a flag to just say that this queue is empty + # @front = @back = -1 + + @store = LinkedList.new end - + def enqueue(element) - raise NotImplementedError, "Not yet implemented" + # if @front == -1 + # @front = 0 + # @back = 1 + # end + + # if @front == @back + # # decide + # end + + # @store[@back] = element + # @back = (@back + 1) % @store.length + + @store.add_last(element) end - - def dequeue - raise NotImplementedError, "Not yet implemented" + + def dequeue + @store.remove_first end - + def front raise NotImplementedError, "Not yet implemented" end - + def size raise NotImplementedError, "Not yet implemented" end - + def empty? - raise NotImplementedError, "Not yet implemented" + @store.empty? end - + def to_s return @store.to_s end diff --git a/lib/queue2.rb b/lib/queue2.rb new file mode 100644 index 00000000..ac6b4aef --- /dev/null +++ b/lib/queue2.rb @@ -0,0 +1,50 @@ +class Queue + + def initialize + @store = Array.new(30) + @front = @back = 0 + # full when front == back + end + + def enqueue(element) + # don't care when queue is empty + # only care if it's full + + if @front == (@back + 1) % @store.length + raise ArgumentError, 'queue is currently full' + end + + @store[@back] = element + @back = (@back + 1) % @store.length + end + + def dequeue + # only care if queue is empty + if @front == @back + raise ArgumentError, 'the queue is empty' + end + + removed = @store[@front] + + @store[@front] = nil + @front = (@front + 1) % @store.length + + return removed + end + + def front + raise NotImplementedError, "Not yet implemented" + end + + def size + raise NotImplementedError, "Not yet implemented" + end + + def empty? + return @front == @back + end + + def to_s + return @store.compact.to_s + end +end diff --git a/lib/stack.rb b/lib/stack.rb index cfc6ef0f..b90127fb 100644 --- a/lib/stack.rb +++ b/lib/stack.rb @@ -1,21 +1,22 @@ +require_relative './linked_list' + class Stack def initialize - # @store = ... - raise NotImplementedError, "Not yet implemented" + @store = LinkedList.new end - + def push(element) - raise NotImplementedError, "Not yet implemented" + @store.add_last(element) end - + def pop - raise NotImplementedError, "Not yet implemented" + @store.remove_last end - + def empty? - raise NotImplementedError, "Not yet implemented" + @store.empty? end - + def to_s return @store.to_s end diff --git a/test/problems_test.rb b/test/problems_test.rb index f851f1d2..8f0557ad 100644 --- a/test/problems_test.rb +++ b/test/problems_test.rb @@ -7,45 +7,38 @@ describe "Test wave 3 problems" do describe "balanced" do it "Given balanced strings it should return true" do - skip expect(balanced('(({}))')).must_equal true end - + it "regards an empty string as balanced" do - skip expect(balanced('')).must_equal true end - + it "will return false for an unbalanced set of parens" do - skip expect(balanced('(()')).must_equal false expect(balanced('(()}')).must_equal false expect(balanced('([]]')).must_equal false end - + it "also works for {} and []" do - skip expect(balanced('[]')).must_equal true expect(balanced('{}')).must_equal true end - + it "also works if the string has opens and closes in the beginning and end" do - skip expect(balanced('[]()')).must_equal true end end - + describe "postfix" do it "can add a 2 numbers together" do - skip expect(evaluate_postfix("34+")).must_equal 7 expect(evaluate_postfix("34*")).must_equal 12 expect(evaluate_postfix("34-")).must_equal -1 expect(evaluate_postfix("34/")).must_equal 0 end - + it "can add a evaluate a more complicated expression" do - skip expect(evaluate_postfix("34+2*")).must_equal 14 expect(evaluate_postfix("34*2/")).must_equal 6 expect(evaluate_postfix("34-1+")).must_equal 0 @@ -54,4 +47,4 @@ expect(evaluate_postfix("62/5+")).must_equal 8 end end -end \ No newline at end of file +end diff --git a/test/queue2_test.rb b/test/queue2_test.rb new file mode 100644 index 00000000..35f13fd7 --- /dev/null +++ b/test/queue2_test.rb @@ -0,0 +1,101 @@ +require 'minitest/autorun' +require 'minitest/reporters' +require_relative '../lib/queue2' + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +describe "Test Queue Implementation" do + it "creates a Queue" do + q = Queue.new + q.class.must_equal Queue + end + + it "adds something to an empty Queue" do + q = Queue.new + q.enqueue(10) + q.to_s.must_equal "[10]" + end + + it "adds multiple somethings to a Queue" do + q = Queue.new + q.enqueue(10) + q.enqueue(20) + q.enqueue(30) + q.to_s.must_equal "[10, 20, 30]" + end + + it "starts the size of a Queue at 0" do + q = Queue.new + q.empty?.must_equal true + end + + it "removes something from the Queue" do + q = Queue.new + q.enqueue(5) + q.to_s.must_equal "[5]" + removed = q.dequeue + removed.must_equal 5 + q.empty?.must_equal true + end + + it "removes the right something (LIFO)" do + q = Queue.new + q.enqueue(5) + q.enqueue(3) + q.enqueue(7) + removed = q.dequeue + removed.must_equal 5 + q.to_s.must_equal "[3, 7]" + end + + it "properly adjusts the size with enqueueing and dequeueing" do + q = Queue.new + q.empty?.must_equal true + q.enqueue(-1) + q.enqueue(-60) + q.empty?.must_equal false + q.dequeue + q.dequeue + q.empty?.must_equal true + end + + it "returns the front element in the Queue" do + q = Queue.new + q.enqueue(40) + q.enqueue(22) + q.enqueue(3) + q.dequeue + expect(q.dequeue).must_equal 22 + end + + it "works for a large Queue" do + q = Queue.new + q.enqueue(10) + q.enqueue(20) + q.enqueue(30) + expect(q.dequeue).must_equal 10 + expect(q.dequeue).must_equal 20 + q.enqueue(40) + q.enqueue(50) + q.enqueue(60) + q.enqueue(70) + q.enqueue(80) + q.enqueue(90) + q.enqueue(100) + q.enqueue(110) + q.enqueue(120) + q.enqueue(130) + q.enqueue(140) + q.enqueue(150) + q.enqueue(150) + q.enqueue(160) + q.enqueue(170) + q.enqueue(180) + q.enqueue(190) + q.enqueue(200) + q.enqueue(210) + q.dequeue + + expect(q.to_s).must_equal('[40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 150, 160, 170, 180, 190, 200, 210]') + end +end diff --git a/test/queue_test.rb b/test/queue_test.rb index 8a7dcd9c..f298d8d2 100644 --- a/test/queue_test.rb +++ b/test/queue_test.rb @@ -9,40 +9,35 @@ q = Queue.new q.class.must_equal Queue end - + it "adds something to an empty Queue" do - skip q = Queue.new q.enqueue(10) q.to_s.must_equal "[10]" end - + it "adds multiple somethings to a Queue" do - skip q = Queue.new q.enqueue(10) q.enqueue(20) q.enqueue(30) q.to_s.must_equal "[10, 20, 30]" end - + it "starts the size of a Queue at 0" do - skip q = Queue.new q.empty?.must_equal true end - + it "removes something from the Queue" do - skip q = Queue.new q.enqueue(5) removed = q.dequeue removed.must_equal 5 q.empty?.must_equal true end - + it "removes the right something (LIFO)" do - skip q = Queue.new q.enqueue(5) q.enqueue(3) @@ -51,9 +46,8 @@ removed.must_equal 5 q.to_s.must_equal "[3, 7]" end - + it "properly adjusts the size with enqueueing and dequeueing" do - skip q = Queue.new q.empty?.must_equal true q.enqueue(-1) @@ -63,9 +57,8 @@ q.dequeue q.empty?.must_equal true end - + it "returns the front element in the Queue" do - skip q = Queue.new q.enqueue(40) q.enqueue(22) @@ -73,6 +66,7 @@ q.dequeue expect(q.dequeue).must_equal 22 end + it "works for a large Queue" do q = Queue.new q.enqueue(10) @@ -100,7 +94,7 @@ q.enqueue(200) q.enqueue(210) q.dequeue - - expect(q.to_s).must_equal('[40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240]') + + expect(q.to_s).must_equal('[40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 150, 160, 170, 180, 190, 200, 210]') end end diff --git a/test/stack_test.rb b/test/stack_test.rb index df5046c8..1e8f3346 100644 --- a/test/stack_test.rb +++ b/test/stack_test.rb @@ -8,40 +8,35 @@ s = Stack.new s.class.must_equal Stack end - + it "pushes something onto a empty Stack" do - skip s = Stack.new s.push(10) s.to_s.must_equal "[10]" end - + it "pushes multiple somethings onto a Stack" do - skip s = Stack.new s.push(10) s.push(20) s.push(30) s.to_s.must_equal "[10, 20, 30]" end - + it "starts the stack empty" do - skip s = Stack.new s.empty?.must_equal true end - + it "removes something from the stack" do - skip s = Stack.new s.push(5) removed = s.pop removed.must_equal 5 s.empty?.must_equal true end - + it "removes the right something (LIFO)" do - skip s = Stack.new s.push(5) s.push(3) @@ -50,4 +45,4 @@ removed.must_equal 7 s.to_s.must_equal "[5, 3]" end -end \ No newline at end of file +end