diff --git a/lib/linked_list.rb b/lib/linked_list.rb index 136d8ac9..19b5f450 100644 --- a/lib/linked_list.rb +++ b/lib/linked_list.rb @@ -12,331 +12,329 @@ 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 + def initialize + @head = nil # keep the head private. Not accessible outside this class + @tail = nil + end - @head.previous = new_node unless @head.nil? - @head = new_node - if @tail.nil? - @tail = @head - 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? + def remove_first() + raise ArgumentError, "Empty" if self.empty? - value = @head.data - @head = @head.next - @head.previous = nil - return value - end + value = @head.data + @head = @head.next + @head.previous = nil if @head + return value + end - def empty? - return @head.nil? - 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 - return max + # 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 + 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 - current = current.next + # 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 min + current = current.next end + 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 - return length - + # 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 + 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 + # 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 + 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? + # placeholder for node #2 + temp = current.next + # next node is node #-1 + 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 + 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 + # 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 + 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 + # 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 + 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 - # 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 + # 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 - # 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 + def remove_last() + value = @tail.data + if @head == @tail + @head = @tail = nil + else + @tail = @tail.previous + @tail.next = nil end - def remove_last() - value = @tail.data - if @head == @tail - @head = @tail = nil - else - @tail = @tail.previous - @tail.next = nil - end + return value + end - 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 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 + # 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? + # until current.next == nil, will run into nil > value error otherwise, or until current == @tail + 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 + # add node as tail + end - # navigate to last node - current = @head - while current.next != nil - 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 - current.next = @head # make the last node link to first node + # navigate to last node + current = @head + while current.next != nil + current = current.next end - def to_s - list = [] + current.next = @head # make the last node link to first node + end - current = @head - until current.nil? - list << current.data - current = current.next - end + def to_s + list = [] - return list.to_s + 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 61bcaa5a..06999bc7 100644 --- a/lib/problems.rb +++ b/lib/problems.rb @@ -1,9 +1,58 @@ -require_relative './stack.rb' +require_relative "./stack.rb" def balanced(string) - raise NotImplementedError, "Not implemented yet" + return true if string == "" + + # set up + stack = Stack.new + char_map = { + "}" => "{", + "]" => "[", + ")" => "(", + } + open_chars = ["{", "[", "("] + + # loop through each char + # push to stack if it's opening char. if not, look up corresponding value in hash + # compare hash value to popped stack node. if they are not equal or hash value is nil, return false + # finally, check if the stack is empty. if it is, it means everything matches up + + string.each_char do |c| + if open_chars.include?(c) + stack.push(c) + else + pop_c = stack.pop + return false unless pop_c && pop_c == char_map[c] + end + end + return stack.empty? end def evaluate_postfix(postfix_expression) - raise NotImplementedError, "Not implemented yet" -end \ No newline at end of file + num_stack = Stack.new + postfix_expression.each_char do |c| + case c + # when it's an operand, pop both operands out, perform operation, push back to stack + when "+" + num2 = num_stack.pop + num1 = num_stack.pop + num_stack.push(num1 + num2) + when "-" + num2 = num_stack.pop + num1 = num_stack.pop + num_stack.push(num1 - num2) + when "*" + num2 = num_stack.pop + num1 = num_stack.pop + num_stack.push(num1 * num2) + when "/" + num2 = num_stack.pop + num1 = num_stack.pop + num_stack.push(num1 / num2) + else + # push to stack + num_stack.push(c.to_i) + end + end + return num_stack.pop +end diff --git a/lib/queue.rb b/lib/queue.rb index 828217c6..89e33e13 100644 --- a/lib/queue.rb +++ b/lib/queue.rb @@ -1,31 +1,83 @@ +require_relative "./linked_list.rb" + +# Using a circular buffer implement a Queue with the following methods: + +# enqueue(value) - Adds the value to the back of the queue. +# dequeue - removes and returns a value from the front of the queue +# empty? returns true if the queue is empty and false otherwise + class Queue + Q_SIZE = 5 def initialize - # @store = ... - raise NotImplementedError, "Not yet implemented" + @store = Array.new(Q_SIZE) + @head = -1 + @tail = -1 end def enqueue(element) - raise NotImplementedError, "Not yet implemented" + # q is full if rear = length - 1 or head - 1 + if (@tail + 1 == @head) || (@tail + 1 == Q_SIZE) + raise ArugmentError, "queue is full" + # if head and tail are -1, then make it head + # move head and tail to 1 + elsif @head == -1 && @tail == -1 + @store[0] = element + @head = 0 + @tail = 0 + # head starts at not 0, and tail is last element + elsif @head > 0 && (@tail + 1 == Q_SIZE) + @tail = 0 + @store[@tail] = element + else + @tail += 1 + @store[@tail] = element + end end def dequeue - raise NotImplementedError, "Not yet implemented" + raise ArugmentError, "q empty" unless !empty? + # if head is at end of array + data = @store[@head] + @store[@head] = nil + if @head == Q_SIZE - 1 + # data = @store[@head] + # @store[@head] = nil + @head = 0 + # if only one element + elsif @head == @tail + # data = @store[@head] + # @store[@head] = nil + @head = @tail = -1 + else + # data = @store[@head] + # @store[@head] = nil + @head += 1 + end + return data end def front - raise NotImplementedError, "Not yet implemented" + return @head == -1 ? nil : store[@head] end def size - raise NotImplementedError, "Not yet implemented" + if empty? + return 0 + elsif @head == 0 + return @tail + 1 + elsif @tail < @head + return (Q_SIZE - head + @tail + 1) + end end def empty? - raise NotImplementedError, "Not yet implemented" + return @head == -1 && @tail == -1 end def to_s - return @store.to_s + clone_store = @store.clone + clone_store.delete(nil) + return clone_store.to_s end end diff --git a/lib/stack.rb b/lib/stack.rb index cfc6ef0f..70c7d1aa 100644 --- a/lib/stack.rb +++ b/lib/stack.rb @@ -1,19 +1,20 @@ +require_relative "./linked_list.rb" + 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" + return @store.empty? end def to_s diff --git a/test/problems_test.rb b/test/problems_test.rb index f851f1d2..39b28d6e 100644 --- a/test/problems_test.rb +++ b/test/problems_test.rb @@ -1,43 +1,43 @@ -require 'minitest/autorun' -require 'minitest/reporters' -require_relative '../lib/problems' +require "minitest/autorun" +require "minitest/reporters" +require_relative "../lib/problems" Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new describe "Test wave 3 problems" do describe "balanced" do it "Given balanced strings it should return true" do - skip - expect(balanced('(({}))')).must_equal true + # skip + expect(balanced("(({}))")).must_equal true end it "regards an empty string as balanced" do - skip - expect(balanced('')).must_equal true + # 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 + # 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 + # 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 + # skip + expect(balanced("[]()")).must_equal true end end describe "postfix" do it "can add a 2 numbers together" do - skip + # skip expect(evaluate_postfix("34+")).must_equal 7 expect(evaluate_postfix("34*")).must_equal 12 expect(evaluate_postfix("34-")).must_equal -1 @@ -45,7 +45,7 @@ end it "can add a evaluate a more complicated expression" do - skip + # 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 +54,4 @@ expect(evaluate_postfix("62/5+")).must_equal 8 end end -end \ No newline at end of file +end diff --git a/test/queue_test.rb b/test/queue_test.rb index 9b616a30..ba3aa901 100644 --- a/test/queue_test.rb +++ b/test/queue_test.rb @@ -1,6 +1,6 @@ -require 'minitest/autorun' -require 'minitest/reporters' -require_relative '../lib/queue' +require "minitest/autorun" +require "minitest/reporters" +require_relative "../lib/queue" Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new @@ -11,14 +11,14 @@ end it "adds something to an empty Queue" do - skip + # skip q = Queue.new q.enqueue(10) q.to_s.must_equal "[10]" end it "adds multiple somethings to a Queue" do - skip + # skip q = Queue.new q.enqueue(10) q.enqueue(20) @@ -27,13 +27,13 @@ end it "starts the size of a Queue at 0" do - skip + # skip q = Queue.new q.empty?.must_equal true end it "removes something from the Queue" do - skip + # skip q = Queue.new q.enqueue(5) removed = q.dequeue @@ -42,7 +42,7 @@ end it "removes the right something (LIFO)" do - skip + # skip q = Queue.new q.enqueue(5) q.enqueue(3) @@ -53,19 +53,20 @@ end it "properly adjusts the size with enqueueing and dequeueing" do - skip + # skip q = Queue.new q.empty?.must_equal true q.enqueue(-1) q.enqueue(-60) q.empty?.must_equal false + q.size.must_equal 2 q.dequeue q.dequeue q.empty?.must_equal true end it "returns the front element in the Queue" do - skip + # skip q = Queue.new q.enqueue(40) q.enqueue(22) @@ -73,4 +74,4 @@ q.dequeue expect(q.dequeue).must_equal 22 end -end \ No newline at end of file +end diff --git a/test/stack_test.rb b/test/stack_test.rb index df5046c8..a29943f3 100644 --- a/test/stack_test.rb +++ b/test/stack_test.rb @@ -1,6 +1,6 @@ -require 'minitest/autorun' -require 'minitest/reporters' -require_relative '../lib/stack' +require "minitest/autorun" +require "minitest/reporters" +require_relative "../lib/stack" Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new describe "Test Stack Implementation" do @@ -10,14 +10,14 @@ end it "pushes something onto a empty Stack" do - skip + # skip s = Stack.new s.push(10) s.to_s.must_equal "[10]" end it "pushes multiple somethings onto a Stack" do - skip + # skip s = Stack.new s.push(10) s.push(20) @@ -26,13 +26,13 @@ end it "starts the stack empty" do - skip + # skip s = Stack.new s.empty?.must_equal true end it "removes something from the stack" do - skip + # skip s = Stack.new s.push(5) removed = s.pop @@ -41,7 +41,7 @@ end it "removes the right something (LIFO)" do - skip + # skip s = Stack.new s.push(5) s.push(3) @@ -50,4 +50,4 @@ removed.must_equal 7 s.to_s.must_equal "[5, 3]" end -end \ No newline at end of file +end