From 6ee0ed30b4fab49387b00407e0c3e6be1039f960 Mon Sep 17 00:00:00 2001 From: Mohd Raza Date: Wed, 16 Sep 2020 22:07:17 -0500 Subject: [PATCH 1/6] data-1 lecture --- singly_linked_list/singly_linked_list.py | 87 ++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/singly_linked_list/singly_linked_list.py b/singly_linked_list/singly_linked_list.py index e69de29bb2..c3de582e61 100644 --- a/singly_linked_list/singly_linked_list.py +++ b/singly_linked_list/singly_linked_list.py @@ -0,0 +1,87 @@ +# TODO a class that represents that individual elements in our LL + +class Node: + def __init__(self, value=None, next_node=None): + self.value = value + self.next_node = next_node + + def get_value(self): + return self.value + + def get_next_node(self): + return self.next_node + + def set_next_node(self, new_next): + self.next_node = new_next + + +class LinkedList: + def __init__(self, first_node=None): + # what attributes do we need? + self.head = first_node + self.tail = first_node + + def add_to_head(self, value): + # create a new Node + new_node = Node(value) + + if self.head is None: + # update head & tail attributes + self.head = new_node + self.tail = new_node + else: + # set next_node of my new Node to the head + new_node.set_next_node(self.head) + # update the head attribute + self.head = new_node + + def add_to_tail(self, value): + # create a new Node + new_node = Node(value) + + # 1. LL is empty + if self.head is None: + # update head & tail attributes + self.head = new_node + self.tail = new_node + # 2. LL is NOT empty + else: + # update next_node of our tail + self.tail.set_next_node(new_node) + # update self, tail + self.tail = new_node + + def remove_head(self): + # empty list + if self.head is None: + return None + # else, return VALUE of the old head + else: + ret_value = self.head.get_value() + # list iwth 1 element + if self.head == self.tail: + self.head = None + self.tail = None + # list with +2 elements + else: + self.head = self.head.get_next_node() + return ret_value + + def remove_tail(self): + # empty list + # list iwth 1 element + # list with +2 elements + pass + + def contains(self, value): + # loop through LL until next pointer is None + cur_node = self.head + while cur_node is not None: + # if we find 'value' + if cur_node.get_value() == value: + return True + return False + + def get_max(self): + # TODO time permitting + pass From 52cf3c37df74490dd5b9f929c137b4ae252f4cc3 Mon Sep 17 00:00:00 2001 From: Mohd Raza Date: Wed, 16 Sep 2020 22:13:29 -0500 Subject: [PATCH 2/6] starting project --- stack/stack.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stack/stack.py b/stack/stack.py index 6e6d660ac7..20f3f87964 100644 --- a/stack/stack.py +++ b/stack/stack.py @@ -10,6 +10,9 @@ 3. What is the difference between using an array vs. a linked list when implementing a Stack? """ +from './../singly_linked_list/singly_linked_list' import Node + + class Stack: def __init__(self): self.size = 0 From 14542d33465119ea53b2ea51279f264132cb8833 Mon Sep 17 00:00:00 2001 From: Mohd Raza Date: Sun, 20 Sep 2020 19:07:35 -0500 Subject: [PATCH 3/6] pushing --- doubly_linked_list/doubly_linked_list.py | 86 ++++++++++++++++++++---- singly_linked_list/singly_linked_list.py | 60 +++++++++++++++-- stack/stack.py | 27 ++++++-- 3 files changed, 150 insertions(+), 23 deletions(-) diff --git a/doubly_linked_list/doubly_linked_list.py b/doubly_linked_list/doubly_linked_list.py index 6f91b43a9b..6cfe85da49 100644 --- a/doubly_linked_list/doubly_linked_list.py +++ b/doubly_linked_list/doubly_linked_list.py @@ -2,16 +2,27 @@ Each ListNode holds a reference to its previous node as well as its next node in the List. """ + + class ListNode: - def __init__(self, value, prev=None, next=None): - self.prev = prev + def __init__(self, value, prev_node=None, next_node=None): + self.prev = prev_node self.value = value - self.next = next - + self.next = next_node + + def delete(self): + if self.prev: + self.next.prev = self.prev + if self.next: + self.prev.nex = self.next + + """ Our doubly-linked list class. It holds references to the list's head and tail nodes. """ + + class DoublyLinkedList: def __init__(self, node=None): self.head = node @@ -20,50 +31,77 @@ def __init__(self, node=None): def __len__(self): return self.length - + """ Wraps the given value in a ListNode and inserts it as the new head of the list. Don't forget to handle the old head node's previous pointer accordingly. """ + def add_to_head(self, value): - pass - + """ + 1. create new_node + 2. add to empty + 3. add to nonempty + """ + new_node = ListNode(value) + if self.head is None: + self.head = new_node + self.tail = new_node + else: + new_node.next = self.head + self.head.prev = new_node + self.head = new_node + + self.length += 1 + """ Removes the List's current head node, making the current head's next node the new head of the List. Returns the value of the removed Node. """ + def remove_from_head(self): pass - + """ Wraps the given value in a ListNode and inserts it as the new tail of the list. Don't forget to handle the old tail node's next pointer accordingly. """ + def add_to_tail(self, value): pass - + """ Removes the List's current tail node, making the current tail's previous node the new tail of the List. Returns the value of the removed Node. """ + def remove_from_tail(self): pass - + """ Removes the input node from its current spot in the List and inserts it as the new head node of the List. """ + def move_to_front(self, node): - pass - + """ + 1. delete() + 2. add_to_head() + """ + if node is self.head: + return + self.delete(node) + self.add_to_head(node.value) + """ Removes the input node from its current spot in the List and inserts it as the new tail node of the List. """ + def move_to_end(self, node): pass @@ -71,12 +109,32 @@ def move_to_end(self, node): Deletes the input node from the List, preserving the order of the other elements of the List. """ + def delete(self, node): - pass + """ + 1. dont need to return value + 2. do need to update heade, tail + """ + if self.head is None: + return None + elif self.head is self.tail: + self.head = None + self.tail = None + elif node is self.head: # list has +2 nodes + self.head = node.next + node.delete() # updating previous and next pointers + elif node is self.tail: + self.tail = node.prev + node.delete() + else: + node.delete() + + self.length -= 1 """ Finds and returns the maximum value of all the nodes in the List. """ + def get_max(self): - pass \ No newline at end of file + pass diff --git a/singly_linked_list/singly_linked_list.py b/singly_linked_list/singly_linked_list.py index c3de582e61..80f186447c 100644 --- a/singly_linked_list/singly_linked_list.py +++ b/singly_linked_list/singly_linked_list.py @@ -25,6 +25,18 @@ def add_to_head(self, value): # create a new Node new_node = Node(value) + if self.head is not None: + print(f"head: {self.head.value} ", ) + else: + print(f"head: {self.head} ", ) + + if self.tail is not None: + print(f"tail: {self.tail.value} ") + else: + print(f"tail: {self.tail} ") + + print(f"newNode: value: {new_node.value} next: {new_node.next_node} ") + if self.head is None: # update head & tail attributes self.head = new_node @@ -68,10 +80,25 @@ def remove_head(self): return ret_value def remove_tail(self): - # empty list - # list iwth 1 element + # empty lst? + if self.head is None: + return None + + # list with 1 element + ret_value = self.tail.get_value() + if self.head == self.tail: + self.head = None + self.tail = None + # list with +2 elements - pass + else: + cur_node = self.head + while cur_node.get_next_node() is not self.tail: + cur_node = cur_node.get_next_node() + cur_node.set_next_node(None) + self.tail = cur_node + + return ret_value def contains(self, value): # loop through LL until next pointer is None @@ -79,9 +106,34 @@ def contains(self, value): while cur_node is not None: # if we find 'value' if cur_node.get_value() == value: + print(f"The node contains value {value} ") return True - return False + cur_node = cur_node.next_node + # return False def get_max(self): # TODO time permitting pass + + def printList(self): + if self.head is None: + return "list is empty" + currentNode = self.head + ret_value = [] + while True: + if currentNode is None: + break + ret_value.append(currentNode.value) + currentNode = currentNode.next_node + return ret_value + + +# Tests the List +# myList = LinkedList() +# myList.add_to_head(2) +# myList.add_to_head(3) +# myList.add_to_head(4) +# myList.add_to_head(5) + + +# print(f"myList: {myList.printList()} ") diff --git a/stack/stack.py b/stack/stack.py index 20f3f87964..7dce6824e7 100644 --- a/stack/stack.py +++ b/stack/stack.py @@ -10,19 +10,36 @@ 3. What is the difference between using an array vs. a linked list when implementing a Stack? """ -from './../singly_linked_list/singly_linked_list' import Node class Stack: def __init__(self): self.size = 0 - # self.storage = ? + self.storage = [] def __len__(self): - pass + return len(self.storage) def push(self, value): - pass + self.storage.append(value) + self.size += 1 def pop(self): - pass + if len(self.storage) == 0: + return None + self.size -= 1 + return self.storage.pop() + + +# myStack = Stack() +# # # myStack.storage = [1, 2, 3, 4, 5] + + + +# myStack.push('raza') + +# print(f"my storage: {myStack.storage} ") +# print(myStack.pop()) +# print(f"my storage: {myStack.storage} ") + + From 40eafe7a5bd0141b82ddc2be7cc2a615d70e42b9 Mon Sep 17 00:00:00 2001 From: Mohd Raza Date: Sun, 20 Sep 2020 19:10:28 -0500 Subject: [PATCH 4/6] change --- queue/queue.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/queue/queue.py b/queue/queue.py index 0d2599ded7..9d4ac8c296 100644 --- a/queue/queue.py +++ b/queue/queue.py @@ -13,11 +13,13 @@ Stretch: What if you could only use instances of your Stack class to implement the Queue? What would that look like? How many Stacks would you need? Try it! """ + + class Queue: def __init__(self): self.size = 0 # self.storage = ? - + def __len__(self): pass @@ -26,3 +28,5 @@ def enqueue(self, value): def dequeue(self): pass + +# changing this file From dbe258b8ee4b4c479548da124ea731723a52e43b Mon Sep 17 00:00:00 2001 From: Mohd Raza Date: Mon, 21 Sep 2020 20:34:56 -0500 Subject: [PATCH 5/6] doubly linked node --- doubly_linked_list/doubly_linked_list.py | 112 +++++++++++++++--- queue/queue.py | 47 +++++++- queue/singly_linked_list.py | 139 +++++++++++++++++++++++ stack/singly_linked_list.py | 139 +++++++++++++++++++++++ stack/stack.py | 56 ++++++--- 5 files changed, 456 insertions(+), 37 deletions(-) create mode 100644 queue/singly_linked_list.py create mode 100644 stack/singly_linked_list.py diff --git a/doubly_linked_list/doubly_linked_list.py b/doubly_linked_list/doubly_linked_list.py index 6cfe85da49..fe4feb304d 100644 --- a/doubly_linked_list/doubly_linked_list.py +++ b/doubly_linked_list/doubly_linked_list.py @@ -14,11 +14,10 @@ def delete(self): if self.prev: self.next.prev = self.prev if self.next: - self.prev.nex = self.next - - + self.prev.next = self.next + """ -Our doubly-linked list class. It holds references to +Our doubly-linked list class. It holds references to the list's head and tail nodes. """ @@ -33,8 +32,8 @@ def __len__(self): return self.length """ - Wraps the given value in a ListNode and inserts it - as the new head of the list. Don't forget to handle + Wraps the given value in a ListNode and inserts it + as the new head of the list. Don't forget to handle the old head node's previous pointer accordingly. """ @@ -62,25 +61,64 @@ def add_to_head(self, value): """ def remove_from_head(self): - pass + cur_node = self.head + + # print(f"node is: {cur_node.value} ") + # print(f"node prev: {cur_node.next.next.value} ") + + if self.head is None: + return None + elif self.head is self.tail: + self.head = None + self.tail = None + else: + self.head = cur_node.next + cur_node.next.prev = cur_node.prev + + self.length -= 1 + return cur_node.value """ - Wraps the given value in a ListNode and inserts it - as the new tail of the list. Don't forget to handle + Wraps the given value in a ListNode and inserts it + as the new tail of the list. Don't forget to handle the old tail node's next pointer accordingly. """ def add_to_tail(self, value): - pass + new_node = ListNode(value) + + if self.head is None: + self.head = new_node + self.tail = new_node + else: + new_node.prev = self.tail + self.tail.next = new_node + self.tail = new_node + self.length += 1 """ - Removes the List's current tail node, making the + Removes the List's current tail node, making the current tail's previous node the new tail of the List. Returns the value of the removed Node. """ def remove_from_tail(self): - pass + cur_node = self.tail + + # print(f"node is: {cur_node.value} ") + # print(f"node prev: {cur_node.prev.value} ") + + if self.tail is None: + return None + elif self.head is self.tail: + self.head = None + self.tail = None + else: + self.tail = cur_node.prev + cur_node.prev.next = cur_node.next + + self.length -= 1 + return cur_node.value """ Removes the input node from its current spot in the @@ -103,7 +141,10 @@ def move_to_front(self, node): """ def move_to_end(self, node): - pass + if node is self.tail: + return + self.delete(node) + self.add_to_tail(node.value) """ Deletes the input node from the List, preserving the @@ -137,4 +178,47 @@ def delete(self, node): """ def get_max(self): - pass + curNode = self.head + + valueList = [] + + while True: + if curNode is None: + break + valueList.append(curNode.value) + curNode = curNode.next + return max(valueList) + + + def printList(self): + if self.head is None: + return "list is empty" + currentNode = self.head + ret_value = [] + while True: + if currentNode is None: + break + ret_value.append(currentNode.value) + currentNode = currentNode.next + return ret_value + + +# # Test +myList = DoublyLinkedList() +myList.add_to_head(1) +myList.add_to_head(2) +myList.add_to_head(3) +myList.add_to_head(4) +myList.add_to_head(5) +myList.add_to_head(6) + +myList.add_to_tail(20) + + +print(f"myList: {myList.printList()} ") + +myList.remove_from_tail() + +print(f"myList: {myList.printList()} ") + +print(f"max value: {myList.get_max()} ") diff --git a/queue/queue.py b/queue/queue.py index 9d4ac8c296..334a26e902 100644 --- a/queue/queue.py +++ b/queue/queue.py @@ -15,18 +15,53 @@ """ -class Queue: +from singly_linked_list import LinkedList + +# class Queue: +# def __init__(self): +# self.size = 0 +# self.storage = [] + +# def __len__(self): +# return len(self.storage) + +# def enqueue(self, value): +# self.storage.append(value) +# self.size += 1 + +# def dequeue(self): +# if len(self.storage) == 0: +# return None +# self.size -= 1 +# return self.storage.pop(0) + +class Queue(LinkedList): def __init__(self): + super().__init__() self.size = 0 - # self.storage = ? def __len__(self): - pass + return self.size def enqueue(self, value): - pass + self.add_to_tail(value) + self.size += 1 def dequeue(self): - pass + if self.size == 0: + return None + self.size -= 1 + return self.remove_tail() +# test + +myQueue = Queue() +myQueue.enqueue(2) +myQueue.enqueue(3) +myQueue.enqueue(4) +myQueue.enqueue(5) +myQueue.enqueue(6) +myQueue.enqueue(7) + +print(myQueue.dequeue()) -# changing this file +print(f"myQueue: {myQueue.printList()} ") diff --git a/queue/singly_linked_list.py b/queue/singly_linked_list.py new file mode 100644 index 0000000000..80f186447c --- /dev/null +++ b/queue/singly_linked_list.py @@ -0,0 +1,139 @@ +# TODO a class that represents that individual elements in our LL + +class Node: + def __init__(self, value=None, next_node=None): + self.value = value + self.next_node = next_node + + def get_value(self): + return self.value + + def get_next_node(self): + return self.next_node + + def set_next_node(self, new_next): + self.next_node = new_next + + +class LinkedList: + def __init__(self, first_node=None): + # what attributes do we need? + self.head = first_node + self.tail = first_node + + def add_to_head(self, value): + # create a new Node + new_node = Node(value) + + if self.head is not None: + print(f"head: {self.head.value} ", ) + else: + print(f"head: {self.head} ", ) + + if self.tail is not None: + print(f"tail: {self.tail.value} ") + else: + print(f"tail: {self.tail} ") + + print(f"newNode: value: {new_node.value} next: {new_node.next_node} ") + + if self.head is None: + # update head & tail attributes + self.head = new_node + self.tail = new_node + else: + # set next_node of my new Node to the head + new_node.set_next_node(self.head) + # update the head attribute + self.head = new_node + + def add_to_tail(self, value): + # create a new Node + new_node = Node(value) + + # 1. LL is empty + if self.head is None: + # update head & tail attributes + self.head = new_node + self.tail = new_node + # 2. LL is NOT empty + else: + # update next_node of our tail + self.tail.set_next_node(new_node) + # update self, tail + self.tail = new_node + + def remove_head(self): + # empty list + if self.head is None: + return None + # else, return VALUE of the old head + else: + ret_value = self.head.get_value() + # list iwth 1 element + if self.head == self.tail: + self.head = None + self.tail = None + # list with +2 elements + else: + self.head = self.head.get_next_node() + return ret_value + + def remove_tail(self): + # empty lst? + if self.head is None: + return None + + # list with 1 element + ret_value = self.tail.get_value() + if self.head == self.tail: + self.head = None + self.tail = None + + # list with +2 elements + else: + cur_node = self.head + while cur_node.get_next_node() is not self.tail: + cur_node = cur_node.get_next_node() + cur_node.set_next_node(None) + self.tail = cur_node + + return ret_value + + def contains(self, value): + # loop through LL until next pointer is None + cur_node = self.head + while cur_node is not None: + # if we find 'value' + if cur_node.get_value() == value: + print(f"The node contains value {value} ") + return True + cur_node = cur_node.next_node + # return False + + def get_max(self): + # TODO time permitting + pass + + def printList(self): + if self.head is None: + return "list is empty" + currentNode = self.head + ret_value = [] + while True: + if currentNode is None: + break + ret_value.append(currentNode.value) + currentNode = currentNode.next_node + return ret_value + + +# Tests the List +# myList = LinkedList() +# myList.add_to_head(2) +# myList.add_to_head(3) +# myList.add_to_head(4) +# myList.add_to_head(5) + + +# print(f"myList: {myList.printList()} ") diff --git a/stack/singly_linked_list.py b/stack/singly_linked_list.py new file mode 100644 index 0000000000..80f186447c --- /dev/null +++ b/stack/singly_linked_list.py @@ -0,0 +1,139 @@ +# TODO a class that represents that individual elements in our LL + +class Node: + def __init__(self, value=None, next_node=None): + self.value = value + self.next_node = next_node + + def get_value(self): + return self.value + + def get_next_node(self): + return self.next_node + + def set_next_node(self, new_next): + self.next_node = new_next + + +class LinkedList: + def __init__(self, first_node=None): + # what attributes do we need? + self.head = first_node + self.tail = first_node + + def add_to_head(self, value): + # create a new Node + new_node = Node(value) + + if self.head is not None: + print(f"head: {self.head.value} ", ) + else: + print(f"head: {self.head} ", ) + + if self.tail is not None: + print(f"tail: {self.tail.value} ") + else: + print(f"tail: {self.tail} ") + + print(f"newNode: value: {new_node.value} next: {new_node.next_node} ") + + if self.head is None: + # update head & tail attributes + self.head = new_node + self.tail = new_node + else: + # set next_node of my new Node to the head + new_node.set_next_node(self.head) + # update the head attribute + self.head = new_node + + def add_to_tail(self, value): + # create a new Node + new_node = Node(value) + + # 1. LL is empty + if self.head is None: + # update head & tail attributes + self.head = new_node + self.tail = new_node + # 2. LL is NOT empty + else: + # update next_node of our tail + self.tail.set_next_node(new_node) + # update self, tail + self.tail = new_node + + def remove_head(self): + # empty list + if self.head is None: + return None + # else, return VALUE of the old head + else: + ret_value = self.head.get_value() + # list iwth 1 element + if self.head == self.tail: + self.head = None + self.tail = None + # list with +2 elements + else: + self.head = self.head.get_next_node() + return ret_value + + def remove_tail(self): + # empty lst? + if self.head is None: + return None + + # list with 1 element + ret_value = self.tail.get_value() + if self.head == self.tail: + self.head = None + self.tail = None + + # list with +2 elements + else: + cur_node = self.head + while cur_node.get_next_node() is not self.tail: + cur_node = cur_node.get_next_node() + cur_node.set_next_node(None) + self.tail = cur_node + + return ret_value + + def contains(self, value): + # loop through LL until next pointer is None + cur_node = self.head + while cur_node is not None: + # if we find 'value' + if cur_node.get_value() == value: + print(f"The node contains value {value} ") + return True + cur_node = cur_node.next_node + # return False + + def get_max(self): + # TODO time permitting + pass + + def printList(self): + if self.head is None: + return "list is empty" + currentNode = self.head + ret_value = [] + while True: + if currentNode is None: + break + ret_value.append(currentNode.value) + currentNode = currentNode.next_node + return ret_value + + +# Tests the List +# myList = LinkedList() +# myList.add_to_head(2) +# myList.add_to_head(3) +# myList.add_to_head(4) +# myList.add_to_head(5) + + +# print(f"myList: {myList.printList()} ") diff --git a/stack/stack.py b/stack/stack.py index 7dce6824e7..76ef898606 100644 --- a/stack/stack.py +++ b/stack/stack.py @@ -1,45 +1,67 @@ """ A stack is a data structure whose primary purpose is to store and -return elements in Last In First Out order. +return elements in Last In First Out order. 1. Implement the Stack class using an array as the underlying storage structure. Make sure the Stack tests pass. 2. Re-implement the Stack class, this time using the linked list implementation as the underlying storage structure. Make sure the Stack tests pass. -3. What is the difference between using an array vs. a linked list when +3. What is the difference between using an array vs. a linked list when implementing a Stack? """ +# import sys +# path = sys.path.append('..') +from singly_linked_list import LinkedList -class Stack: +# class Stack: +# def __init__(self): +# self.size = 0 +# self.storage = [] + +# def __len__(self): +# return len(self.storage) + +# def push(self, value): +# self.storage.append(value) +# self.size += 1 + +# def pop(self): +# if len(self.storage) == 0: +# return None +# self.size -= 1 +# return self.storage.pop() + + +class Stack(LinkedList): def __init__(self): + super().__init__() self.size = 0 - self.storage = [] def __len__(self): - return len(self.storage) - + return self.size + def push(self, value): - self.storage.append(value) + self.add_to_head(value) self.size += 1 def pop(self): - if len(self.storage) == 0: + if self.size == 0: return None self.size -= 1 - return self.storage.pop() - - -# myStack = Stack() -# # # myStack.storage = [1, 2, 3, 4, 5] + return self.remove_head() +myStack = Stack() +# # myStack.storage = [1, 2, 3, 4, 5] -# myStack.push('raza') -# print(f"my storage: {myStack.storage} ") -# print(myStack.pop()) -# print(f"my storage: {myStack.storage} ") +myStack.push('raza') +myStack.push('fatima') +myStack.push('zainab') +print(f"my storage: {myStack.printList()} ") +print(myStack.pop()) +print(f"my storage: {myStack.printList()} ") From 7bdb6cbc02ddf79766e9eb304dde0aa49ce93090 Mon Sep 17 00:00:00 2001 From: Mohd Raza Date: Tue, 29 Sep 2020 20:18:52 -0500 Subject: [PATCH 6/6] finished --- binary_search_tree/binary_search_tree.py | 284 +++++++++++++++++++++-- doubly_linked_list/doubly_linked_list.py | 38 +-- recursion.py | 54 +++++ singly_linked_list/singly_linked_list.py | 12 +- 4 files changed, 347 insertions(+), 41 deletions(-) create mode 100644 recursion.py diff --git a/binary_search_tree/binary_search_tree.py b/binary_search_tree/binary_search_tree.py index d80d9f6282..23e9d64da9 100644 --- a/binary_search_tree/binary_search_tree.py +++ b/binary_search_tree/binary_search_tree.py @@ -1,7 +1,7 @@ """ -Binary search trees are a data structure that enforce an ordering over -the data they store. That ordering in turn makes it a lot more efficient -at searching for a particular piece of data in the tree. +Binary search trees are a data structure that enforce an ordering over +the data they store. That ordering in turn makes it a lot more efficient +at searching for a particular piece of data in the tree. This part of the project comprises two days: 1. Implement the methods `insert`, `contains`, `get_max`, and `for_each` @@ -9,6 +9,46 @@ 2. Implement the `in_order_print`, `bft_print`, and `dft_print` methods on the BSTNode class. """ + + +class Queue: + def __init__(self): + self.size = 0 + self.storage = [] + + def __len__(self): + return len(self.storage) + + def enqueue(self, value): + self.storage.append(value) + self.size += 1 + + def dequeue(self, index=0): + if len(self.storage) == 0: + return None + self.size -= 1 + return self.storage.pop(index) + + +class Stack: + def __init__(self): + self.size = 0 + self.storage = [] + + def __len__(self): + return len(self.storage) + + def push(self, value): + self.storage.append(value) + self.size += 1 + + def pop(self): + if len(self.storage) == 0: + return None + self.size -= 1 + return self.storage.pop() + + class BSTNode: def __init__(self, value): self.value = value @@ -17,54 +57,233 @@ def __init__(self, value): # Insert the given value into the tree def insert(self, value): - pass + + cur_node = self + new_node = BSTNode(value) + + # print('new.value: ' + str(new_node.value)) + # print('self.value: ' + str(cur_node.value)) + + while True: + if new_node.value >= cur_node.value: + if cur_node.right is None: + # print('values:right: ' + str(cur_node.value)) + cur_node.right = new_node + break + cur_node = cur_node.right + elif new_node.value < cur_node.value: + if cur_node.left is None: + # print('values:left: ' + str(cur_node.value)) + cur_node.left = new_node + break + cur_node = cur_node.left + else: + print('value already in tree') + break + + # RECURSIVE + # if value is < root, go left + # if left child is None: + # add here.... left child = BSTNode(value) + # else + # self.left.insert(value) # recursive call + + # if value >= root, go right (dupes go to the right) + # if right child is None + # add here + # else + # self.right.insert(value) # recursive call + + # ITERATIVE + # while not at bottom level of tree + # if value is < root, go left + # if left child is None: + # add here + # exit loop + + # if value >= root, go right (dupes go to the right) + # if right child is None + # add here + # exit loop # Return True if the tree contains the value # False if it does not + def contains(self, target): - pass + # check if self.value is target + # if yes, return true + # if no, + # go left + # go right + cur_node = self + + while True: + if target == cur_node.value: + return True + elif target > cur_node.value: + if cur_node.right is None: + return False + cur_node = cur_node.right + elif target < cur_node.value: + if cur_node.left is None: + return False + cur_node = cur_node.left + else: + return False # Return the maximum value found in the tree def get_max(self): - pass + # go right until you cannot anymore + # return value at far right + cur_node = self + while cur_node.right is not None: + cur_node = cur_node.right + + return cur_node.value # Call the function `fn` on the value of each node def for_each(self, fn): - pass + fn(self.value) + + # base case - no chidlren + if self.left is None and self.right is None: + return + + # recursive case - 1 or more children + # go left, call fn(value) for each node + if self.left: + self.left.for_each(fn) + # go right, call fn(value) for each node + if self.right: + self.right.for_each(fn) # Part 2 ----------------------- # Print all the values in order from low to high # Hint: Use a recursive, depth first traversal + def in_order_print(self): - pass + # Recursive - place your print statement + # that explore left & right subtrees + + if self.left is None and self.right is None: + print(self.value) + return + + # recursive case - 1 or more children + # go left, call fn(value) for each node + if self.left: + self.left.in_order_print() + print(self.value) + # go right, call fn(value) for each node + if self.right: + self.right.in_order_print() + + # Interative - think abou the order in which we are adding ndoes to the stack # Print the value of every node, starting with the given node, # in an iterative breadth first traversal def bft_print(self): - pass + # breadth-first + # visit all nodes at the same level first + # level-order traversal + # Create a Queue to keep track of nodes + # insert self onto beginning of Queue + # while something still in Queue + # add left and right if exist to the queue + # print and remove first node + + q = Queue() + q.enqueue(self) + print(f"queue lenght: {q.__len__()} ") + # for i in q.storage: + # print(f"i is: {i.right.value} ") + + while(q.size != 0): + for i, node in enumerate(q.storage): + print(node.value) + # print(q.storage[0].value) + if node.left is not None: + q.enqueue(node.left) + # print(f"queue lenght:left:before {q.__len__()} ") + # q.dequeue() + # print(f"queue lenght:left:after {q.__len__()} ") + if node.right is not None: + q.enqueue(node.right) + # print(f"queue lenght:right:before {q.__len__()} ") + # q.dequeue() + # print(f"queue lenght:right:after {q.__len__()} ") + # if node.left is None and node.right is None: + # print(f"queue lenght:none:before {q.__len__()} ") + q.dequeue(i) + + + # Print the value of every node, starting with the given node, + # in an iterative depth first traversal - # Print the value of every node, starting with the given node, - # in an iterative depth first traversal def dft_print(self): - pass + # depth-first + # visit all the children first, then go to the next children + # create a stack to keep track of nodes we are procssing + # push root into stack + + # while something still in the stack (not done processign all nodes) + # use existing 'for_each()' as a reference for traversal logic + # push when we START, pop when a node is DONE + # and don't forget to call 'print()' + s = Stack() + s.push(self) + print(f"length is: {s.__len__()} ") + while(s.__len__() != 0): + for node in s.storage: + if node.left is None: + if node.right is not None: + s.push(node.right) + if node.left is not None: + s.push(node.left) + if node.right is not None: + s.push(node.right) + print(node.value) + s.pop() + return # Stretch Goals ------------------------- # Note: Research may be required # Print Pre-order recursive DFT def pre_order_dft(self): - pass + print(self.value) + if self.left: + self.left.pre_order_dft() + if self.right: + self.right.pre_order_dft() # Print Post-order recursive DFT def post_order_dft(self): - pass + + if self.left: + self.left.post_order_dft() + if self.right: + self.right.post_order_dft() + print(self.value) + """ This code is necessary for testing the `print` methods """ -bst = BSTNode(1) +# bst = BSTNode(1) + +# bst.insert(8) +# bst.insert(5) +# bst.insert(7) + +# bst.insert(6) +# bst.insert(3) +# bst.insert(4) +# bst.insert(2) + +bst = BSTNode(1) bst.insert(8) bst.insert(5) bst.insert(7) @@ -73,13 +292,34 @@ def post_order_dft(self): bst.insert(4) bst.insert(2) +# bst.bft_print() +# bst.dft_print() + +# print("elegant methods") +# print("pre order") +# bst.pre_order_dft() +print("in order") +bst.in_order_print() +# print("post order") +# bst.post_order_dft() + +# print(bst.contains(-10)) + +print("bft order:") bst.bft_print() + +print("dft order:") bst.dft_print() -print("elegant methods") -print("pre order") -bst.pre_order_dft() -print("in order") -bst.in_order_dft() -print("post order") -bst.post_order_dft() + +# def bft_print(self): +# q = Queue() +# q.enqueue(self) +# while(q.size != 0): +# for i, node in enumerate(q.storage): +# print(node.value) +# if node.left is not None: +# q.enqueue(node.left) +# if node.right is not None: +# q.enqueue(node.right) +# q.dequeue(i) \ No newline at end of file diff --git a/doubly_linked_list/doubly_linked_list.py b/doubly_linked_list/doubly_linked_list.py index fe4feb304d..65c743c8f2 100644 --- a/doubly_linked_list/doubly_linked_list.py +++ b/doubly_linked_list/doubly_linked_list.py @@ -12,10 +12,13 @@ def __init__(self, value, prev_node=None, next_node=None): def delete(self): if self.prev: - self.next.prev = self.prev - if self.next: + # self.next.prev = self.prev self.prev.next = self.next - + if self.next: + # self.prev.next = self.next + self.next.prev = self.prev + + """ Our doubly-linked list class. It holds references to the list's head and tail nodes. @@ -178,17 +181,25 @@ def delete(self, node): """ def get_max(self): - curNode = self.head + # curNode = self.head - valueList = [] - - while True: - if curNode is None: - break - valueList.append(curNode.value) - curNode = curNode.next - return max(valueList) + # valueList = [] + # while True: + # if curNode is None: + # break + # valueList.append(curNode.value) + # curNode = curNode.next + # return max(valueList) + if self.head is None: + return None + cur_node = self.head + max_value = self.head.value + while cur_node: + if cur_node.value > max_value: + max_value = cur_node.value + cur_node = cur_node.next + return max_value def printList(self): if self.head is None: @@ -202,12 +213,11 @@ def printList(self): currentNode = currentNode.next return ret_value - # # Test myList = DoublyLinkedList() myList.add_to_head(1) myList.add_to_head(2) -myList.add_to_head(3) +myList.add_to_head(33) myList.add_to_head(4) myList.add_to_head(5) myList.add_to_head(6) diff --git a/recursion.py b/recursion.py new file mode 100644 index 0000000000..a8a9109119 --- /dev/null +++ b/recursion.py @@ -0,0 +1,54 @@ +# Recursive function - function that calls itself +# Have 2 cases +# i) Base (stopping) +# 2) Recursive (when the function calls itself) +# Given base and n that are both 1 or more, compute recursively +# (no loops) the value of base to the n power, so powerN(3, 2) +# is 9 (3 squared). +# base^n +# from https://codingbat.com/prob/p158888 +def powerN(base, n): + # base case + if n == 0: + return 1 + elif n == 1: + return base + # recursive + # ex. 3^2 = 3*3 + # ex. 3^3 = 3*3*3 + # ex. 3^4 = 3*3*3*3 = 3 * 3^3 + else: + return base * powerN(base, n-1) + + + + +print(powerN(3, 1)) # → 3 +print(powerN(3, 2)) # → 9 +print(powerN(3, 3)) # → 27 +# Given a string, compute recursively (no loops) the number of +# lowercase 'x' chars in the string. +# from https://codingbat.com/prob/p170371 + + +def countX(str): + # base case + if len(str) == 0: + return 0 + + # recursive case + else: + # we found an x + if str[0] == 'x': + return 1 + countX(str[1:]) + # we did not find an x + else: + return countX(str[1:]) + + + +print(countX("xxhixx")) # → 4 +print(countX("xhixhix")) # → 3 +print(countX("hi")) # → 0 + + diff --git a/singly_linked_list/singly_linked_list.py b/singly_linked_list/singly_linked_list.py index 80f186447c..ccb3ddf84a 100644 --- a/singly_linked_list/singly_linked_list.py +++ b/singly_linked_list/singly_linked_list.py @@ -129,11 +129,13 @@ def printList(self): # Tests the List -# myList = LinkedList() -# myList.add_to_head(2) -# myList.add_to_head(3) -# myList.add_to_head(4) -# myList.add_to_head(5) +myList = LinkedList() +myList.add_to_head(2) +myList.add_to_head(3) +myList.add_to_head(4) +myList.add_to_head(5) # print(f"myList: {myList.printList()} ") +for item in myList.printList(): + print(item)