Learning Objectives
- Practice using the stack and the queue data structures
Assignment Description
The stack and the queue data structures are used in a wide range of applications throughout the world. A number of programming languages are built on operand stacks and call stacks as a means of organizing inputs and determining the order of operations of function calls. Likewise, queues can do many things like helping schedule tasks for a computer or managing user priority based on a FIFO (first in, first out) principle. You will find both useful in traversing a graph later in the semester. These data structures are very versatile and useful throughout the software world. A visualization of them can be found here.
Submission Instructions
Using the Prairielearn workspace, test and save your solution to the following exercises. (You are welcome to download and work on the files locally but they must be re-uploaded or copied over to the workspace for submission).
You will only need to modify the following files:
lab_quack.ipynb
Provided queue and stacks
Python provides many ways to set up these data structures in real life, list
, deque, task queue which all have different tradeoffs and use cases. However, we want to focus on understanding the use of such structures, so for this lab we have provided class definitions for the queue
and stack
data structures for you to use which the test cases will assume you’re using. Here is what all you have available:
Stack:
__init__()
- sets up an empty stackpush(val)
- adds an item to the top of the stackpop()
- removes the item from the top of the stack and returns it__len__()
- returns the length of all the items in the stack__str__()
- prints the stack as though it was a list (‘top’ is the last element)top()
- returns the item from the top of the stackempty()
- returns if the stack is empty
Queue:
__init__()
- sets up an empty queueenqueue(val)
- adds an item to the front of the queuedequeue()
- removes an item from the end of the queue__len__()
- returns the size of the queue__str__()
- prints the queue as though it was a list (‘front’ is the first element)front()
- returns the item in front of the queueempty()
- returns if the queue is empty
Warning: To get credit you must use these given structure and you must not edit utils.py (It is not a submitted file so your changes wont effect the autograder!)
Part 1: Balancing Brackets: the isBalanced
Function
bool isBalanced(queue<char> input)
# INPUT:
# q, an input queue containing a sequence of characters
# OUTPUT:
# a bool value indicating whether the input sequence is 'balanced'
# A balanced string contains an equal amount of '[' and ']' brackets
For this exercise, you must write a function called isBalanced
that takes one argument, a queue
, and returns a bool (true or false) based on whether the string represented by the queue has balanced brackets. The queue may contain any characters, although you should only consider square bracket characters, ‘[’ and ‘]’, when considering whether a string is balanced. To be balanced, a string must not have any unmatched, extra, or hanging brackets. For example, the string [hello][]
is balanced, [[][[]a]]
is balanced, []]
is unbalanced, ][
is unbalanced, and ))))[beepboop]
is balanced. It’s possible to solve this problem without using a stack, but in the spirit of this lab, you should use one in your solution!
NOTE: Order matters! ][
is unbalanced because although it contains an equal number of opening and closing brackets, the open bracket must come first. Stated another way, a closing bracket with nothing to close is unbalanced as is an open bracket which is not closed.
Part 2: spyCode
Functions
None spyEncode(queue<int> q)
# INPUT:
# q, an input queue containing a sequence of values (type doesnt matter)
# OUTPUT:
# None
# The function should directly modify the input queue to contain a re-arranged
# version of the same input sequence. Successful implementations will require
# reading and replacing the values in the queue in the appropriate order.
None spyDecode(queue<int> q)
# INPUT:
# q, an input queue containing a sequence of values (type doesnt matter)
# OUTPUT:
# None
# The function should directly modify the input queue to contain a re-arranged
# version of the same input sequence. Successful implementations will require
# reading and replacing the values in the queue in the appropriate order.
Your task is to write a pair of functions called spyEncode
and spyDecode
that each take in one argument: a queue
and either scrambles the elements in the queue or reverses the scramble. Note that you are adjusting the values in the input queue, not making (or returning) a new queue!
You may use whatever local variables you need. The encode function should reverse the order of SOME of the elements in the queue, and maintain the order of others, according to the following pattern:
- The first three elements stay on the front of the queue.
- After the first three elements, each of the following occurs to the next set of three elements in sequence.
- The next three elements are rotated. So the first element becomes second, the second element becomes the third, and the third element becomes the first.
- Then the next three elements are reversed. So the first element becomes third, the second stays the same, and the third becomes the first.
- Then the next three elements are placed on the queue in their original order.
- The next three elements are rotated (the pattern repeats)
NOTE: You are not allowed to directly edit the underlying data structure to re-arrange elements. Your solution must use the built-in functions (enqueue and dequeue for queues and push and pop for stacks) to perform the re-arrangements.
Hint: You’ll want to make a local stack
variable.
For example, given the following queue,
front back
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
we get the following result:
front back
0 1 2 5 3 4 8 7 6 9 10 11 14 12 13
You can assume that the that you’ll always be given blocks of three numbers.
Hint: Think about what will be identical in both encode and decode, and what will be different.
If you want a challenge, consider what adjustments will need to be made to both encode and decode to allow input queues of any size. There’s three levels to this difficulty:
- What happens if you need to rotate or reverse a block but there’s not three numbers left?
- What happens if I want to reserve or rotate blocks much larger than three?
- What happens if I want to account for both?