Data structures and algorithms

Exercise solutions: Sequences

Here are suggested solutions to some of the exercises about stacks, queues and lists.

Core exercises

A1. Give an algorithm that removes all comments from a program.

The idea is to go through the program and use a counter to track how deeply nested the current position is. This counter behaves like a stack where we don’t care about the elements, only the size (instead of push/pop, we increment/decrement).

Try to implement it first, and then you can click here to reveal a solution in pseudocode.
comments = 0
for each character in program:
    if character == "{":
        comments += 1
    else if character == "}":
        if comments == 0:
            throw error "comment end without start"
        comments -= 1
    else:
        if comments == 0:
            add character to new program
if comments > 0:
    throw error "comment start without end"

Assuming we use a dynamic array to store the new program, the time complexity of this algorithm is linear in the length of the original program (i.e., O(n)).

For the hard part, we really have to use a stack. There are different kinds of nested comments, { ... } (type A) and [ ... ] (type B). When we encounter {, we push “A” onto the stack. Similarly, when we encounter [, we push “B”. When we encounter }, we pop from the stack and check that we get “A”. Similarly, when we encounter ], we pop from the stack and check that we get “B”. After going over the whole program, we check that the stack is empty (otherwise we have an unclosed comment).

A2. Write a program that reads a postfix expression and evaluates it.

Here’s an explanation of postfix expressions together with a high-level description of the algorithm. The main idea can be summarised as follows:

Solution:

Try to implement it first, and then you can click here to reveal a solution in pseudocode.
stack = new Stack()
for each token in the input stream:
    if token == ".":
        print stack.pop()
    else if token == "+":
        stack.push(stack.pop() + stack.pop())
    else if token == "+":
        stack.push(stack.pop() + stack.pop())
    else if token == "-":
        stack.push(stack.pop() + stack.pop())
    else if token == "*":
        stack.push(stack.pop() + stack.pop())
    else:
        stack.push(token parsed as an integer)

A3. Sequence of queue operations

Regardless of the order in which you give the enqueue/dequeue operations, the result will always be the same: the elements are returned in the same order as they were enqueued.

So if you enqueue the numbers 0 to 9, then the only sequence that can occur is 0, 1, …, 9.

A4. An array of unlimited size

This is a variant of a dynamic array.

Try to implement it first, and then you can click here to reveal a suggestion in pseudocode.
class UnlimitedSizeArray:
    array = new Array of size 1

    get(index):
        return array[index]

    # Here is one way to implement `set`.
    set(index, value):
        # Repeatedly double the array until it's big enough
        while index >= array.size():
            resize(array.size()*2)
        array[index] = value

    # Here is another way. Both ways work!
    set(index, value):
        # If the array is too small, increase its size.
        # But, to avoid having to copy the array too often,
        # at least double the size of the array.
        if index >= array.size():
            resize(max(array.size()*2, index+1))
        array[index] = value

    # A private method to resize the array.
    resize(newSize):
        oldArray = array
        array = new Array of size newSize
        for i in 0 ... array.size()-1:
            array[i] = oldArray[i]

Bonus exercises

B1. Sequence of stack operations

This is much trickier than A3!

Suppose the sequence looks like this: […, a, …, b, …, c, …], where there might or might not be numbers between a, b and c. Now, if a > b, a > c and b < c, then the sequence is impossible!

Informal proof: When a is popped, we know that both b and c must be on the stack (because they are pushed in numerical order). But b was pushed earlier than c (because b < c), and therefore b cannot be popped before c is popped.

B2. Exercises from Sedgewick & Wayne

Heading “Exercises” on the book website: https://algs4.cs.princeton.edu/13stacks/

Heading “Web Exercises” on the book website: https://algs4.cs.princeton.edu/13stacks/

Linked list exercises from the book

Click here to show some pseudocode.
previous = null
current = list.first
while current is not null:
    if current.item is not equal to key:
        previous = current
    else if previous is null:
        list.first = current.next   # we remove the first element in the list
    else:
        previous.next = current.next   # we remove an inner element
    current = current.next
Click here to show some pseudocode.
max = 0
current : Node = list.first
while current is not null:
    if current.item > max:
        max = current.item
    current = current.next

Creative problems from the book