Search code examples
pythonabstract-data-type

Removing 2nd item from a queue, using another queue as an ADT


class Queue:

    def __init__(self):
        self._contents = []

    def enqueue(self, obj):
        self._contents.append(obj)

    def dequeue(self):
        return self._contents.pop(0)

    def is_empty(self):
        return self._contents == []

class remove_2nd(Queue):

    def dequeue(self):

        first_item = Queue.dequeue(self)
        # Condition if the queue length isn't greater than two
        if self.is_empty():
            return first_item
        else:
            # Second item to return
            second_item = Queue.dequeue(self)
            # Add back the first item to the queue (stuck here)

The remove_2nd class is basically a queue except if the length of the queue is greater than two, then you remove the 2nd item every dequeue. If it isn't then you do the same as a normal queue. I am only allowed to use the methods in the queue to finish remove_2nd.

My algorithm:

If queue is bigger than two:

Lets say my queue is 1 2 3 4

I would first remove the first item so it becomes

2 3 4

I would then remove the 2nd item and that will be the returned value, so then it will be

3 4

I would then add back the first item as wanted

1 3 4

The problem is, I don't know how to add it back. Enqueue puts it at the end, so basically it would be 3 4 1. I was thinking of reversing the 3 4, but I don't know how to do that either. Any help?

Just want to point out, I'm not allowed to call on _contents or allowed to create my own private variable for the remove_2nd class. This should strictly be done using the queue adt


Solution

  • To get the queue back in the right order after removing the first two elements, you'll need to remove all the other elements as well. Once the queue is empty, you can add back the first element and all the other elements one by one.

    How exactly you keep track of the values you're removing until you can add them again is a somewhat tricky question that depends on the rules of your assignment. If you can use Python's normal types (as local variables, not as new attributes for your class), you can put them in a list or a deque from the collections module. But you can also just use another Queue instance (an instance of the base type, not your subclass).

    Try something like this in your else clause:

    second_item = Queue.dequeue(self)   # note, this could be written super().dequeue()
    
    temp = Queue()
    while not self.is_empty():
        temp.enqueue(Queue.dequeue(self))
    
    self.enqueue(first_item)
    while not temp.is_empty()
        self.enqueue(temp.dequeue())
    
    return second_item
    

    As I commented in the code, Queue.dequeue(self) can be written more "pythonically" using the super builtin. The exact details of the call depend on which version of Python you're using (Python 3's super is much fancier than Python 2's version).

    In Python 2, you have to explicitly pass self and your current class, so the call would be super(self, dequeue_2nd).dequeue(). In Python 3, you simply use super().dequeue() and it "magically" takes care of everything (in reality, the compiler figures out the class at compile time, and adds some extra code to let it find self at run time).

    For your simple code with only basic inheritance, there's no difference between using super or explicitly looking up the base class by name. But in more complicated situations, using super is very important. If you ever use multiple inheritance, calling overridden methods with super is often the only way to get things to work sanely.