Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linked-list - Warmup Challenge with LRU challenge #37

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions classwork/04/DoublyLinkedList.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import functools


class Node:
def __init__(self, data):
self.data = data
self.next = None
self.previous = None


# This decorator states that the element can't run if empty
def is_empty(func):
@functools.wraps(func)
def wrapper(self, *args):
if not self.is_empty():
return func(self, *args)
print("You cannot do the operation when the DS is empty")
return
return wrapper


# Possible add functionality ?
def increase_count(func):
@functools.wraps(func)
def wrapper(self, *args):
value = func(self, *args)
self.count += 1
return value
return wrapper


def decrease_count(func):
@functools.wraps(func)
def wrapper(self, *args):
value = func(self, *args)
self.count -= 1
return value
return wrapper


def check_index(func):
@functools.wraps(func)
def wrapper(self, index, *args):
if index < 0 or index >= self.size():
raise IndexError("The index being used is NA")
return
else:
return func(self, index, *args)
return wrapper


class DoublyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.count = 0

@increase_count
def push(self, data):
current_node = Node(data)
if self.is_empty():
self.head = current_node
else:
if self.tail is None:
self.tail = current_node
self.head.next = self.tail
self.tail.previous = self.head
else:
self.tail.next = current_node
current_node.previous = self.tail
self.tail = current_node

@increase_count
def push_left(self, data):
current_node = Node(data)
if self.is_empty():
self.head = current_node
elif self.size() == 1:
self.head.previous = current_node
current_node.next = self.head
self.head = current_node
self.tail = current_node.next
else:
self.head.previous = current_node
current_node.next = self.head
self.head = current_node

@decrease_count
@check_index
def remove(self, index):
if index == 0:
self.head = self.head.next
self.head.previous = None
elif index == self.size() - 1:
self.tail = self.tail.previous
self.tail.next = None
else:
current_node = self.find_node(index, True)
previous_node = current_node.previous
next_node = current_node.next
previous_node.next = next_node

@is_empty
@decrease_count
def pop(self):
if self.size() == 1:
current_data = self.head.data
self.head = None
elif self.size() == 2:
current_data = self.tail.data
self.tail = None
else:
current_data = self.tail.data
self.tail = self.tail.previous
self.tail.next = None
return current_data

def size(self):
return self.count

def is_empty(self):
return self.size() == 0

@check_index
def insert(self, index, data):
# I will not be adding the @increase_count decoration due to this
if index == 0:
self.push_left(data)
else:
next_node = self.find_node(index, True)
previous_node = next_node.previous
current_node = Node(data)
previous_node.next = current_node
current_node.next = next_node
next_node.previous = current_node
current_node.previous = previous_node
self.count += 1

@check_index
def find_node(self, index, node = False):
current_node = self.head
i = 0
while i < index:
current_node = current_node.next
i += 1
return current_node if node else current_node

def print_list(self):
current_node = self.head
i = 0
while current_node is not None:
if i == self.size() - 1:
print(current_node.data)
else:
print(current_node.data, ' -- ', end = '')
current_node = current_node.next
i += 1


if __name__ == '__main__':
linked_list = DoublyLinkedList()
linked_list.push(3)
linked_list.push(10)
linked_list.push(5)
linked_list.push(1)
linked_list.push(6)

print("Printing the entire initiated list")
linked_list.print_list()
print("Inserting the data 25 at index 2 then printing")
linked_list.insert(2, 25)
linked_list.print_list()
print("Inserting 1 at the index 0 then printing")
linked_list.push_left(1)
linked_list.print_list()
print(linked_list.size())
print("Popping then printing")
print(linked_list.pop())
linked_list.print_list()
59 changes: 59 additions & 0 deletions classwork/04/LRU.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from DoublyLinkedList import DoublyLinkedList as LinkedList


class LRU(LinkedList):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!!

def __init__(self, max_length = 5):
super().__init__()
self.max_length = max_length
self.hash_set = set()

def push(self, data):
if self.size() < self.max_length:
super().push_left(data)
self.hash_set.add(data)
else:
# We remove the last element
last_data = self.pop()
self.hash_set.remove(last_data)
self.push(data)

def get(self, data):
if data in self.hash_set:
# We find it then take it to the start
# Also return the data
current_node = self.head
i = 0
while current_node.data != data:
current_node = current_node.next
i += 1
# This will remove the data from the list
self.remove(i)
# This will push the data to the start of the list
self.push(data)

else:
print("Page Exception: Page not found. Adding to start")
self.push(data)

if __name__ == '__main__':
short_LRU = LRU(5)
short_LRU.push(3)
short_LRU.push(10)
short_LRU.push(5)
short_LRU.push(1)
short_LRU.push(6)
print("Printing the entire initiated list")
short_LRU.print_list()
print(short_LRU.count)
print(short_LRU.max_length)
print("Adding a new page into the list then printing")
short_LRU.push(25)
short_LRU.print_list()
print(short_LRU.count)
print(short_LRU.max_length)
print("Searching for the removed 3 then adding it to the front of the list")
short_LRU.get(3)
short_LRU.print_list()
print("Searching for 1 then adding it to the front of the list")
short_LRU.get(1)
short_LRU.print_list()
148 changes: 148 additions & 0 deletions classwork/04/LinkedList.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
class Node:
def __init__(self, data):
self._data = data
self.next = None
# self.prev = None




class Linkedlist:

def __init__(self):
self._head = None
self._tail = None
self._count = 0

# Done
def push(self, data):
if self.isEmpty():
self._head = Node(data)
else:
if self._tail is None:
self._tail = Node(data)
self._head.next = self._tail
else:
self._tail.next = Node(data)
self._tail = self._tail.next
self._count += 1

# Done
def remove(self, index):
if index < 0 or index >= self.size():
raise IndexError
return
if index == 0:
# Removing from the head
self._head = self._head.next
self._count -= 1
return
previous_node = self.findNode(index - 1, True)
if index == self.size() - 1:
# Removing from the last index
self._tail = previous_node
else:
# Removing from the middle
previous_node.next = previous_node.next.next
self._count -= 1

# Done
def pop(self):
if self.isEmpty():
# Popping an empty list
return
if self.size() == 1:
# Popping a list with size = 1, From the head
current_data = self._head.data
self._head = None
elif self.size() == 2:
# Popping a list with size = 2, From the tail
current_data = self._tail.data
self._tail = None
else:
# Popping a list with size > 2
previous_node = self.findNode(self.size() - 2, True)
current_data = self._tail.data
self._tail = previous_node
self._tail.next = None
self._count -= 1
return current_data

# Done
def size(self):
return self._count

# Done
def isEmpty(self):
return self.size() == 0

# Done
def insert(self, index, data):
if index == 0:
# Inserting at the head
current_node = Node(data)
current_node.next = self._head
self._head = current_node
self._count += 1
return
# Find the node before the index
previous_node = self.findNode(index - 1, True)
# Find the node at the index
next_node = previous_node.next
current_node = Node(data)
# Set the node at the index
previous_node.next = current_node
# Push the previous next node
current_node.next = next_node
self._count += 1

# Done
def findNode(self, index, node = False):
current_node = self._head
if index >= self.size() or index < 0:
raise IndexError
return
i = 0
while i < index:
current_node = current_node.next
i += 1
return current_node if node else current_node

def printList(self):
current_node = self._head
i = 0
while current_node is not None:
if i == self.size() - 1:
# To avoid the last ->
print(current_node._data)
else:
print(current_node._data , end = ' -> ')
current_node = current_node.next
i += 1

def sum(self):
current_node = self._head
total = 0
while current_node is not None:
total += current_node._data
current_node = current_node.next
return total

linked_list = Linkedlist()
linked_list.push(3)
linked_list.push(10)
linked_list.push(5)
linked_list.push(1)
linked_list.push(6)
print("Printing the entire initiated list")
linked_list.printList()
print("The sum is =", linked_list.sum())
print("Inserting the data 25 at index 2 then printing")
linked_list.insert(2, 25)
linked_list.printList()
print("The sum is =", linked_list.sum())
print("Removing the data at index 2 then printing")
linked_list.remove(2)
linked_list.printList()
print("The sum is =", linked_list.sum())