How to avoid starvation in a priority queue - python

I am trying to create a queue that avoids having elements in the queue for too long. I am using a linked list. The way I want to implement it is that if a greater priority is added, the ones that are pushed backed have 0.4 added to them. I also need to implement a sort for the linked list but that makes some sense already. I do not really understand how I am supposed to add this 0.4 to the priority's that have been displaced.
class Node:
def __init__(self, data=None):
self.data = data
self.next = None
class LinkedList:
def __init__(self,):
self.head = Node()
def append(self, data):
new_node = Node(data)
current = self.head
while current.next is not None:
current = current.next
current.next = new_node
def __str__(self):
data = []
current = self.head
while current is not None:
data.append(current.data)
current = current.next
return "[%s]" %(', '.join(str(i) for i in data))
def __repr__(self):
return self.__str__()
def length(self):
current = self.head
total = 0
while current.next is not None:
total += 1
current = current.next
return total
def display(self):
elements = []
current_node = self.head
while current_node.next is not None:
current_node = current_node.next
elements.append(current_node.data)
print("Elements ", elements)
def get(self, index):
if index >= self.length():
print("Error: Index is out of range!")
return None
current_index = 0
current_node = self.head
while True:
current_node = current_node.next
if current_index == index:
return current_node.data
current_index += 1
def remove(self):
current = self.head
if current is not None:
self.head = current.next
else:
print("Queue is empty!")
def main():
queue = LinkedList()
queue.append(5)
queue.append(2)
queue.display()
main()

I'd suggest using python's heapq rather than a linked list. Because you're doing a custom comparison, you'll probably want to implement something like what's described in heapq with custom compare predicate.
In addition to the priority, add a field that contains the time when the item was added to the heap. Then, your actual priority is a function of the assigned priority and the length of time the item has been in the heap. For example, the priority could be something like:
elapsed_time = current_time - added_time;
real_priority = assigned_priority * elapsed_time;
You might want to make the multiplier some fraction of the elapsed time.
Another possibility is if you want to make sure that something doesn't stay in the queue longer than some set time. For example, if you want to make sure things don't stay for more than 5 minutes, you could add dequeue_time field. Your comparison then becomes something like:
// assume items a and b are being compared
if (a.dequeue_time < current_time) {
if (b.dequeue_time < a.dequeue_time) {
// b sorts before a
}
else if (b.dequeue_time > a.dequeue_time) {
// return a sorts before b
}
else { // dequeue times are equal
return result of comparing a.priority to b.priority
}
}
else {
// here, return result of comparing a.priority and b.priority
}
heapq is going to be much more efficient than your linked list, especially as the number of items in your queue increases. Plus, it's already written and known working. All you have to do is provide the comparison function.

Related

When a method has no return statement, how do I print out it's return value?

EDITED.
I am learning about Linked Lists. For each process applied by a Method, it is printed out to the console. So, adding, removing, searching (i.e, displaying the result of a search), are all streamed to stdout, but I cannot seem to do this for the insertion Method even though the insert Method is executed.
Some Methods have a return statement, while others rely on the __repr__() for conversion to string, to then be streamed to the console. The insertion Method (not mine, but a course worked example) takes two arguments and does not have a return statement. The most consistent error message I get when attempting to print is TypeError: %d format: a real number is required, not NoneType, or TypeError: not enough arguments for format string, where I have replaced %d with %s.
What I do not understand is, why I am unable to display test data for the insert Method, while I can do so for all others.
The code,
#!/usr/bin/env python3
class Node:
data = None
next_node = None
def __init__(self, data):
self.data = data
def __repr__(self):
return "<Node data: {}>".format(self.data)
# Linked List
class LinkedList:
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None # corrected
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.next_node
return count
# Adding a node
def add(self, data):
new_node = Node(data)
new_node.next_node = self.head
self.head = new_node
# Searching the List
def search(self, key):
current = self.head
while current:
if current.data == key:
return current
else:
current = current.next_node
return None
# Inserting into the List
def insert(self, data, index):
if index == 0:
self.add(data)
if index > 0:
new_data = Node(data)
position = index
current = self.head
while position > 1:
current = current.next_node
position -= 1
past_node = current
future_node = current.next_node
past_node.next_node = new_data
new_data = current.next_node
# Removing a node from the List
def remove(self, key):
current = self.head
previous = None
found = False
while current and not found:
if current.data == key and current == self.head:
found = True
self.head = current.next_node
elif current.data == key:
found = True
previous.next_node = current.next_node
return current
def __repr__(self):
nodes = []
current = self.head
while current:
if current is self.head:
nodes.append("[Head: {}]".format(current.data))
elif current.next_node is None:
nodes.append("[Tail {}]".format(current.data))
else:
nodes.append("[{}]".format(current.data))
current = current.next_node
return '-> '.join(nodes)
Test output;
l = LinkedList()
l.add(1)
l.add(2)
l.add(3)
l.add(5)
l.add(6)
l.add(7)
length = l.size()
print("Size of list: {}".format(length)) # Size of list: 6
print(l) # [Head: 7]-> [6]-> [5]-> [3]-> [2]-> [Tail: 1]
seek = l.search(7)
print("Found: {}".format(seek)) # Found: <Node data: 7>
between = l.insert(4, 3)
if between is not None:
print(f"Inserted {between} at index {between}")
else:
print("A problem with code") # A problem with code
gone = l.remove(1)
print("Removed: {}".format(gone)) # Removed: <Node data: 1>
# Note the insertion of '4' at index 3
print(l) # [Head: 7]-> [6]-> [5]-> [4]-> [3]-> [Tail: 2]
THIS CODE WORKS!
Other variants of the print format have been tried f"{}", .format() and even an attempt at conversion to string str() was made, with no luck. Could someone explain exactly what the problem is (though, I believe it to be a NoneType issue) and how to resolve it?
I hope my question is clearer. Thank you.
There are several issues with the code you presented, including the following:
The Node class should not define data and next_node as class attributes. They should be instance attributes. Luckily, the constructor creates an instance attribute data (hiding the class attribute), but for next_node this is not done, which makes your linked list unusable.
In line with the previous comment, you should have self.next_node = None in your constructor.
[You corrected this in an edit to your question: The method name is_empty suggests that it will return a boolean indicating whether the list is empty or not. But instead it makes the list empty. That seems wrong.]
[You corrected this in an edit to your question: insert can call a method add which is not defined.]
In insert, when index is 0, the code will still continue after the first if and reference a variable new_data that has not been defined (since the second if condition was not true). You should avoid that any of the other code is executed when index is 0. You can do this with a return.
In insert, in the while loop there is no verification whether current is None. If that happens, current = current.next_node will raise an error.
new_data = current.next_node is useless and leaves the next_node attribute of next_node uninitialised.
Not an issue, but in remove, the found name is not very useful. Just break out of the loop when the node has been found and removed. Also, avoid having the current.data == key condition executed twice for the same node.
In remove, in the loop, you never change current nor previous, and so the loop hangs.
[You corrected this in an edit to your question: In the main code, the list is empty at the moment that l.insert is called, so it is strange to pass 3 as value for the index parameter, as that index is out of range. As mentioned in a previous bullet, this will trigger an error. If you want to add a node at index 3, you'll first have to add nodes at indexes 0, 1, and 2.]
The insert method does not return anything, so capturing its return value is not going to give you anything else than None. If you really want to get some feedback from it, then do like you did for the remove method: have it return the relevant node. In that case you should also let add have a return value.
Here is some working code with the above issues addressed and more:
class Node:
def __init__(self, data):
self.data = data
self.next_node = None # next_node neads to be an instance attribute, not a class attribute
def __repr__(self):
return "<Node data: {}>".format(self.data)
class LinkedList:
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None # Don't MAKE it empty!
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.next_node
return count
def add(self, data):
new_node = Node(data)
new_node.next_node = self.head
self.head = new_node
return new_node # Return the new node
def search(self, key):
current = self.head
while current:
if current.data == key:
return current
else:
current = current.next_node
return None
def insert(self, data, index):
if index == 0:
# Don't continue after this call to self.add
return self.add(data) # Return the new node
current = self.head
while index > 1 and current: # Protect against out of range index
current = current.next_node
index -= 1
if current: # Protect against out of range index
new_data = Node(data)
# Make sure the new node gets a next_node assignment
new_data.next_node = current.next_node
current.next_node = new_data
return new_data # Return the new node
def remove(self, key):
current = self.head
previous = None
while current:
if current.data == key: # Check this only once per node
if current == self.head:
self.head = current.next_node
else:
previous.next_node = current.next_node
break # No need for variable - just exit
previous = current # Update previous
current = current.next_node # Move to next node
return current
def __repr__(self):
nodes = []
current = self.head
while current:
if current is self.head:
nodes.append("[Head: {}]".format(current.data))
elif current.next_node is None:
nodes.append("[Tail {}]".format(current.data))
else:
nodes.append("[{}]".format(current.data))
current = current.next_node
return '-> '.join(nodes)
l = LinkedList()
l.add(1)
l.add(2)
l.add(3)
l.add(5)
l.add(6)
l.add(7)
length = l.size()
print("Size of list: {}".format(length)) # Size of list: 6
print(l) # [Head: 7]-> [6]-> [5]-> [3]-> [2]-> [Tail: 1]
seek = l.search(7)
print("Found: {}".format(seek)) # Found: <Node data: 7>
node = l.insert(4, 3)
print("Inserted {}".format(node)) # Inserted: <Node data: 4>
gone = l.remove(1)
print("Removed: {}".format(gone)) # Removed: <Node data: 1>
# Note the insertion of '4' at index 3
print(l) # [Head: 7]-> [6]-> [5]-> [4]-> [3]-> [Tail: 2]
insert does not "have a problem" outputting data - just like standard Python lists, it is an in-place operation. You are modifying the list on which it is applied.
insert() does not need to return anything, as all the information you need is provided by you when calling it - you need to pass a list, you need to pass data to insert and you need to pass an index at which the element is to be placed - there is no new information to be gained from returning anything.
Related question:
Why don't list operations return the resulting list?
I think you're confusing 2 things here. The value after the return statement is what the function call is replaced with when the function is called. So for example:
def square(x):
return x*x
square(4)
here the square(4) would be replaced with 4*4. And if you don't explicitly use a return statement than a None is returned after the last command in the function/method.
Whereas repr() is a way to specifiy the string representation of that object. So for example:
class A:
pass
a = A()
print(a)
might create a cryptic output of <main.A at 0x7fbc841c9490>. So if you want it to be more descriptive you could add a repr() method:
class Point:
def __init__(self, x,y):
self.x = x
self.y = y
def __repr__(self):
return f"Coordinates of the point are x: {self.x}, y: {self.y}"
p = Point(2,4)
And instead of the cryptic default message you'd get:
Coordinates of the point are x: 2, y: 4
So the representation is how the obj is converted to a string whereas the return value is what the function call is replaced with.
print is TypeError: %d format: a real number is required, not
NoneType, or TypeError: not enough arguments for format string, where
I have replaced %d with %s.
So this creates errors because %d and %s expect numbers and strings when the return type of a method without return is None.

Linked Lists: How to remove odd numbers?

I am currently taking an online computer science introductory course and have just learned the concept of a linked list. Though I understand the concept of linked lists, I still am unsure as how to deal with linked lists.
As such, I seek out help in solving the following problem, which will be of significant help for my understanding of linked lists:
Write a function (not in LinkedList class definition) that given a linked list, will change that linked list to filter out odd numbers. Immediately after the function returns, the linked list will only have even numbers.
I am unsure as to how to access the nodes in the list and check whether they are odd or even and remove or keep them accordingly.
I apologize if this seems like a trivial question, but I would appreciate any help that might help me learn.
The code for the linked list and node classes (as provided by the online course):
class Node:
def __init__(self, data=None, next_node=None):
self.data = data
self.next = next_node
def __str__(self):
return str(self.data)
class LinkedList:
def __init__(self):
self.length = 0
self.head = None
def print_list(self):
node = self.head
while node is not None:
print(node, end=' ')
node = node.next
print('')
def add_at_head(self, node):
node.next = self.head
self.head = node
self.length += 1
def remove_node_after(self, node):
if node.next is not None:
temp = node.next
node.next = node.next.next
temp.next = None
self.length -= 1
def remove_first_node(self):
if self.head is None:
return
temp = self.head
self.head = self.head.next
temp.next = None
self.length -= 1
def print_backward(self):
def print_nodes_backward(node):
if node.next is not None:
print_nodes_backward(node.next)
if node is not None:
print(node, end=' ')
if self.head is not None:
print_nodes_backward(self.head)
print('')
Let's say you have a bare-bones simple linked list that looks like this:
class LinkedList:
class ListNode:
def __init__(self, data):
self.data = data
self.next = None
def __init__(self):
self.head = None
def add(self, data):
if self.head is None:
self.head = LinkedList.ListNode(data)
else:
current_node = self.head
while current_node.next is not None:
current_node = current_node.next
current_node.next = LinkedList.ListNode(data)
def __str__(self):
ret = "["
current_node = self.head
while current_node is not None:
ret = ret + str(current_node.data)
if current_node.next is not None:
ret = ret + ", "
current_node = current_node.next
ret = ret + "]"
return ret
In other words, the LinkedList contains a single head, which is a ListNode. Every element in the Linked List is contained in a ListNode, and each ListNode points towards the next element in the list.
As you can see, for adding an element to the list, we either create a node at the head if the list is empty (i.e. self.head is None), or we traverse to the end of the list by continuously jumping to the .next element for each ListNode, starting from the head. We also use this paradigm for printing a string representation of our list.
So, to remove any node from the linked list, we can simply change the node that references it, so that the node we want to remove gets skipped. At which point it will disappear.
To remove all list nodes containing odd-numbered data, we might do something like this:
def remove_odds(self):
# special case: head node
# remove odd head elements by simply setting head to the next element after
while (self.head is not None) and (self.head.data % 2 == 1):
self.head = self.head.next
# regular case: the rest of the nodes
current_node = self.head
while (current_node is not None) and (current_node.next is not None):
# if the next node's data is odd, then
if current_node.next.data % 2 == 1:
# skip that node by pointing this node's .next to the next node's .next
current_node.next = current_node.next.next
# otherwise, move forwards in the list
else:
current_node = current_node.next
Proof of concept:
>>> lst = LinkedList()
>>> lst.add(2)
>>> lst.add(5)
>>> lst.add(6)
>>> lst.add(3)
>>> lst.add(7)
>>> lst.add(8)
>>> lst.add(10)
>>> lst.add(1)
>>> lst.add(4)
>>> print(lst)
[2, 5, 6, 3, 7, 8, 10, 1, 4]
>>> lst.remove_odds()
>>> print(lst)
[2, 6, 8, 10, 4]
Copied from comment: The idea is to iterate through the list head-to-tail while remembering the previous node; when you find a garbage node, apply remove_node_after to the remembered node, or move the head to the current node if we haven't had time to remember anything yet.
The code would be something like this (untested):
class LinkedList:
# ...
def delete_if(self, pred):
prev = None
curr = self.head
while curr:
if pred(curr.data):
if prev:
self.remove_node_after(prev)
else:
self.head = curr
prev = curr
curr = curr.next
llist.delete_if(lambda x: x % 2 == 1) # delete if odd
# Mahmoud AL-Mokdad
# this course on Udemy By SEfactoru right๐Ÿ˜
# use my code ๐Ÿ˜‰
def filter_even(ll):
first_node = ll.head
while (first_node is not None) and (first_node.data % 2 != 0):
ll.remove_first_node()
first_node = ll.head
node = first_node
while node is not None and node.next is not None:
if node.next.data % 2 != 0:
ll.remove_node_after(node)
else:
node = node.next

Find the frequency of numbers using linkedlist

Find the frequency of numbers using linkedlist.
Getting SIGTSTP - time limit exceed error while running the below code. Can anyone help me where am I getting it wrong?
class Element(object):
def __init__(self,value):
self.value = value
self.next = None
class LinkedList(object):
def __init__(self, head = None):
self.head = head
def append(self, new):
current = self.head
if self.head:
while current.next:
current = current.next
current.next = new
else:
self.head = new
def traverse(self):
current = self.head
while current != None:
print(current.value)
current = current.next
arr = list(map(int, input().split()))
ll = LinkedList()
for i in arr:
e = Element(i)
ll.append(e)
ll.traverse()
def frequency(a):
current = a.head
while current != None:
count = 1
while current.next != None:
if current.value == current.next.value:
current+=1
if current.next.next != None:
current.next = current.next.next
else:
current.next = None
print(str(current.value)+" : " + str(count))
current = current.next
frequency(ll)
Everything looks fine except frequency. You will need to keep two references, one to the current element and the other will traverse the remainder of the list, starting from current. Does that give you something to go on?
Note too that your current implementation will modify the underlying linked list, while you can indeed do "skipping" with the pointers to prevent listing the same element multiple times, it is imo preferable to avoid modifying the underlying structure in this way.

Understanding a Linked List implementation in Python

I have found an implementation of a Linked List in Python, online, but it doesn't have any explanation or comments.
I understand the underlying concepts of a Linked List, but there is one key part of the code I don't understand:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def get_data(self):
return self.data
def get_next(self):
return self.next
def set_data(self, data):
self.data = data
def set_next(self, next):
self.next = next
class LinkedList:
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None
def add(self, item):
temp = Node(item)
temp.set_next(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.get_next()
return count
def search(self, item):
current = self.head
while current != None:
if current.get_data() == item:
return True
else:
current = current.get_next()
return False
def remove(self, item):
current = self.head
previous = None
found = False
while not found:
if current.get_data() == item:
found = True
else:
previous = current
current = current.get_next()
if previous == None:
self.head = current.get_next()
else:
previous.set_next(current.get_next())
I don't understand how the size, search and remove methods in the LinkedList class are able to call functions from the Node class via the current variable, after setting it to self.head, which seems to be contained within the scope of the LinkedList class.
Is it because the add method sets self.head = temp, where temp is a Node object?
If possible, could someone explain how this works?
You stated that:
I don't understand how the size, search and remove methods in the LinkedList class are able to call functions from the Node class via the current variable, after setting it to self.head, which seems to be contained within the scope of the LinkedList class.
You can see that in the code, initializing a LinkedList performs this line of code:
self.head = None
Since the head is set to none, the size, search, and remove methods will not run through the whole code. Rather, it will stop when the self.head == None, which is pretty much in the beginning.
For example, let's take a look at the size method.
def size(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.get_next()
return count
In this function, current is set to self.head which is null unless you have added any nodes by calling the add() method. More on that later.
count is set equal to 0. Then a while loop begins which only runs if the current is not None. But since the current is set to self.head which is None, the while loop will not run and the function will return count which is 0. This is a correct implementation because there are currently no nodes in the linkedlist.
Now onto how you can add nodes.
The add method:
def add(self, item):
temp = Node(item)
temp.set_next(self.head)
self.head = temp
Here, the add method takes in itself and an item. The item is an object of some sort whether it be a string, integer, float, etc. Now a variable temp is created and set to a new node which is finally using something from the Node class. Then, temp's next node is set to head and the head is set to temp. What this does is that the linked list continuously updates the head.
Like this:
(head)
NODE1
ADD ONE MORE NODE
(head)
NODE2 NODE1
And so on...
Happy Coding!

Algorithm to delete a node from the middle

I'm reading Cracking the Coding Interview and doing practice problems and I'm stuck on this one:
"Implement an algorithm to delete a node in the middle (i.e., any node but the first and the last node, not necessarily the exact middle) or a singly linked list, given only access to that node.
EXAMPLE
Input: the node from the linked list a->b->c->d->e->f
Result: nothing is returned, but the new linked list looks like a->b->d->e->f"
Here's my code :
class Node:
def __init__(self, data = None, nextnode = None):
self.data = data
self.nextnode = nextnode
def __str__(self):
return str(self.data)
class LinkedList():
def __init__(self, head = None):
self.head = head
def insert(self, data):
new_node = Node(data)
new_node.nextnode = self.head
self.head = new_node
def remove(self, data):
current = self.head
absent = True
if current == None: print('List is empty')
if current.data == data:
self.head = current.nextnode
absent = False
while current.nextnode:
if current.nextnode.data == data:
absent = False
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
else: current.nextnode = None
else: current = current.nextnode
if absent: print('Element not in list')
def size(self):
current = self.head
size = 0
while current:
current = current.nextnode
size += 1
return size
def find(self, data):
current = self.head
if current == None: print('List is empty')
search = True
while current and search:
if current.data == data:
print(current)
search = False
current = current.nextnode
if search: print('Not found')
def print_list(self):
current = self.head
while current:
print(current, end = ' ')
current = current.nextnode
print('')
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node1.nextnode = node2
node2.nextnode = node3
node3.nextnode = node4
list1 = LinkedList(node1)
list1.insert(2 ****EDITED node2 to 2 here****)
print_list(list1)
def delmid(ll, n):
current = ll.head
if current == n:
print('Can\'t delete first node')
return
while current.nextnode:
if current.nextnode == n:
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
return
else:
print('Can\'t delete last node')
return
delmid(list1, node2)
print_list(list1)
I can't figure out why it doesn't seem to think that ll.head and node2 are the same ... It does work if I get rid of the line list1.insert(node2) ...
I don't understand ...
EDIT: after reading the first sentence of the solution in the book, apparently i did it wrong anyways .... "given only access to that node" means you don't know the head of the list ... back to the drawing board ...
Because your insert method is wrong:
def insert(self, data):
new_node = Node(data)
new_node.nextnode = self.head
self.head = new_node
Your method does not insert node2 itself as a node: it creates a new node with node2 as payload (data). That is something different.
You can define a method:
def insert_node(self, node):
node.nextnode = self.head
self.head = new_node
Nevertheless this will create a loop since now node1 will be pointing to node2 and node2tonode1`. So the resulting linked list will be a rounded list with two elements, like:
node1 --> node2
^---------/
EDIT: since you solved that one. There is also a problem with your delmid method.
The main problem is that in your while loop you need to walk through the linked list, and you do not do that: current always remains the same, so:
def delmid(ll, n):
current = ll.head
if current == n:
print('Can\'t delete first node')
return
while current.nextnode:
if current.nextnode == n:
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
return
else:
print('Can\'t delete last node')
return
current = current.nextnode
Should fix that.
Insert operation
You misunderstood your own insert-implementation.
list1.insert(node2) inserts a new node with node2 as content:
def insert(self, data):
new_node = Node(data) # <== wrap node2 into another node instance
new_node.nextnode = self.head
self.head = new_node
Comparison of Nodes
The ==-operator internally works by calling the method __eq__(self, other). In your case you didn't provide and implementation for this method, so the default is used to compare by all variables, which includes nextnode. Thus two nodes can only be equal, if they are precisely the same. To get this corrected, use a custom comparison method in Node:
def __eq__(self, other):
return type(other) is Node && other.data == self.data
This __eq__-method would work by first checking that other definitely is of type Node and afterwards compare by the data stored in each instance.
Delmid
Going a bit further than the actual question:
while current.nextnode:
if current.nextnode == n:
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
return
else:
print('Can\'t delete last node')
return
This loop will run infinitely, unless the list has at most a size of 1. To fix this step through the list by current = current.nextnode.
Improving Delmid
The actual purpose of this task was to get you used to another way of manipulating linked lists: swapping.
Instead of searching the entire list for the predecessor of n, you could just check that n is neither the first nor the last node, swap out the value with the value of it's consecutive node and delete the consecutive node:
def delmid(ll, n):
if ll.head == n:
print('Can\'t delete first node')
return
if n.nextnode is None:
print('Can\'t delete last node')
return
# swap values
tmp = n.data
n.data = n.nextnode.data
n.nextnode.data = tmp
# remove node
n.nextnode = n.nextnode.nextnode

Categories