Skip to main content

Implementing Monotonic Stack in Python

A monotonic stack is an extension of the Stack data structure in which the elements of the stack are in either increasing or decreasing order. The stack in which the items are in increasing order is called the monotonically increasing stack, the top element of such a stack will be the largest of all the elements below. On the other hand, if the items are arranged in a decreasing order then the stack is called a monotonically decreasing stack, on which the top element will be the smallest of all the elements below. Monotonic stacks are very popular in competitive programming.


Disclaimer: All code presented in this article has been run with Python 3.12 version, the same may not run expected on a different version of Python. Please modify the code accordingly based on the Python version(s).

The core of a monotonic stack is the LIFO stack, except for the fact that during every push() operation, the elements are popped from the stack till the desired ordering is achieved. A stack data structure is initially added as a Class with the following methods defined,
push - insert an element at the top of the stack
pop - remove the element at the top of the stack
is_empty - check whether a given stack has no entries
peek - return the element at the top of the stack without removing it

Apart from the core methods, there are two more methods that were added for debugging purposes,
__str__ - overloading the string format method to return the list of elements in stack
verify - a unit testing method to verify the stack elements at any given point of time

The Stack is coded as follows,

class Stack:
    def __init__(self):
        self.stack = []
        self.top = -1

    def peek(self):
        if not self.is_empty():
            return self.stack[self.top]
        return None

    def is_empty(self):
        return self.top == -1

    def push(self, num):
        self.stack.append(num)
        self.top += 1

    def pop(self):
        if not self.is_empty():
            self.top -= 1
            return self.stack.pop()
        return None

    def __str__(self):
        return "Stack: " + " -> ".join(str(i) for i in self.stack)

    def verify(self, expected):
        if self.stack != expected:
            raise Exception(f"Expected {expected}, got {self.stack}")


For push and pop methods, Python's existing list modification methods append and pop are used respectively. The top variable of the object keeps track of the index where the top of the stack is located. The is_empty method uses the top of the stack index to determine whether the stack is empty.

With the basic stack implementation, a monotonic stack can be implemented just by overriding the push method. There are two more classes added, one for the increasing and other for decreasing order. For an increasing stack, the top element is the highest, so if the current top is higher than the element that is being pushed, then a pop is invoked and the new top is compared and so on. The process repeats as long as the top element is less than the element being pushed or the stack becomes empty. For a decreasing stack, the reverse happens, the top element is smaller compared to the current element being pushed, then a series of pop operation happens, till all the elements in the stack are larger than the current element. These classes only implement the push method, but extends the base Stack class. These classes are coded as follows,

class IncreasingStack(Stack):
    def push(self, num):
        while not self.is_empty() and self.peek() >= num:
            self.pop()
        super().push(num)


class DecreasingStack(Stack):
    def push(self, num):
        while not self.is_empty() and self.peek() <= num:
            self.pop()
        super().push(num)


There are a set of tests written to validate the behavior of the newly implemented stacks. Each test pushes one element to a given stack and verifies the order of the stack to ensure the logic works fine. The test code is as follows, no output is shown but the assert trips in case of any failure.

def run_one_test(s, n, expected):
    s.push(n)
    s.verify(expected)


def run_tests(s, tests):
    for test in tests:
        run_one_test(s, test[0], test[1])


def run_dec_tests():
    dec_tests = [
        (1, [1]),
        (2, [2]),
        (4, [4]),
        (3, [4, 3]),
        (6, [6]),
        (5, [6, 5]),
    ]
    s = DecreasingStack()
    run_tests(s, dec_tests)


def run_inc_tests():
    inc_tests = [
        (1, [1]),
        (2, [1, 2]),
        (4, [1, 2, 4]),
        (3, [1, 2, 3]),
        (6, [1, 2, 3, 6]),
        (5, [1, 2, 3, 5]),
    ]
    s = IncreasingStack()
    run_tests(s, inc_tests)


run_inc_tests()
run_dec_tests()

Thank you for reading the article, please leave your suggestions or comments if any.

Comments

Popular posts from this blog

BMI Calculator using Python and Tkinter

Body Mass Index can be calculated using a simple formula kg/m 2 , where the weight is represented in kilograms (kg) and the height is represented in metres (m). The article presents code to create a simple GUI to calculate BMI based on user input values. The GUI is implemented using tkinter and tkinter.ttk libraries in Python language.

Using hilite.me API in Python

hilite.me is a light weight website made by Alexander Kojevnikov , used to format source code from various programming languages into formatted HTML snippets which can be added to blogs, articles, and other web pages as needed. I personally use the API to format the source code snippets in this blog, and it has greatly reduced the time taken to complete an article, much thanks to the creator. In this article, an automated way of using the hilite.me API through Python and a simple HTML document is created with formatted code snippets.

Tic-tac-toe game using Python and tkinter

Tic-tac-toe is a popular two player game where each player tries to occupy an empty slot in a 3x3 board until one of them wins or the entire board is filled without any winner. The winner has to occupy three continuous cells in any direction, including the two diagonals. In this article, a version of the tic-tac-toe game is coded using Python and tkinter library.