Class 12Computer Science · Programming with PythonFull chapter

Stack

The whole chapter in one place — read it, then test yourself. Clear notes, a reference sheet, a practice quiz, and worked NCERT solutions & PYQs.

What a Stack Is: LIFO

Quick answer A stack allows insertion and deletion at one end only — the top — so the item pushed last is the item popped first, which is the entire meaning of LIFO.

A stack is a linear data structure in which you may insert and delete at one end only. That single open end is called the top. Everything below it is sealed in.

Because the item you put in last is the one sitting on top, it is also the item you can take out first. That is the whole definition: a stack is LIFO — Last In, First Out.

You already use stacks every day without calling them that:

  • A pile of steel plates at a canteen counter. You lift the top plate, and a washed plate goes back on top.
  • The Back button in a browser. It takes you to the page you visited most recently, not the first one you opened.
  • Ctrl+Z in any editor. Undo reverses your latest action first, then the one before it.
  • Answer sheets an invigilator collects. The last sheet handed in lies on top of the bundle.

Contrast this with the queue at a Metro ticket counter, where the first person to arrive is served first. That is a different structure, and it is not in your syllabus. Everything in this chapter is LIFO.

The vocabulary the board expects:

TermMeaning
TopThe only position where an item can be added or removed
PushInsert a new item on the top
PopRemove the top item and return it
Peek (or Top operation)Read the top item without removing it
UnderflowTrying to pop or peek when the stack is empty
OverflowTrying to push when the stack has reached its maximum size

Worked example. Six operations on a stack of student names. Watch which name comes out first.

# A stack of answer sheets on an invigilator's desk
stack = []                 # an empty stack

stack.append("Aarav")      # push
stack.append("Diya")       # push
stack.append("Kabir")      # push
print("Stack now :", stack)
print("Top item  :", stack[-1])

print("Popped    :", stack.pop())
print("Popped    :", stack.pop())
print("Stack now :", stack)

stack.append("Ishaan")
print("Stack now :", stack)
print("Top item  :", stack[-1])

Real output:

Stack now : ['Aarav', 'Diya', 'Kabir']
Top item  : Kabir
Popped    : Kabir
Popped    : Diya
Stack now : ['Aarav']
Stack now : ['Aarav', 'Ishaan']
Top item  : Ishaan

Trace it operation by operation. In the table the list is written bottom to top, exactly as Python prints it:

OperationStack (bottom to top)Top
push Aarav['Aarav']Aarav
push Diya['Aarav', 'Diya']Diya
push Kabir['Aarav', 'Diya', 'Kabir']Kabir
pop returns Kabir['Aarav', 'Diya']Diya
pop returns Diya['Aarav']Aarav
push Ishaan['Aarav', 'Ishaan']Ishaan

The proof of LIFO is in the third line of the output. Kabir went in last and came out first. Aarav went in first and was still sitting at the bottom, untouched, at the end. Diya could not be reached until Kabir was removed — in a stack you cannot pull an item out from the middle.

Create an empty stack stack = [] Python has no built-in stack type. The syllabus builds one on a plain list.
Push (insert on top) stack.append(item) Adds at the end of the list. Returns None, so never write stack = stack.append(x).
Pop (remove from top) stack.pop() Removes AND returns the last item. Raises IndexError if the stack is empty.
Peek (read the top) stack[-1] Same as stack[len(stack)-1]. Does not remove anything. Raises IndexError if empty.
Number of items len(stack) The top index is len(stack) - 1, which is -1 when the stack is empty.
Is the stack empty? len(stack) == 0 stack == [] and not stack mean exactly the same thing. All three give True for an empty stack.
Remember
  • A stack permits insertion and deletion at one end only; that end is called the top.
  • LIFO means Last In, First Out — the most recently pushed item is the first one popped.
  • The four operations are push (insert), pop (remove and return), peek (read the top), and the empty test.
  • Underflow is popping or peeking an empty stack; overflow is pushing into a full stack.
  • Real examples: browser Back, Ctrl+Z undo, a pile of plates, a bundle of collected answer sheets.

Implementing a Stack Using a List

Quick answer A Python list becomes a stack when you agree that the last element is the top, using append() to push and pop() to pop — a choice made for speed, not convention.

Python has no built-in stack type. The syllabus tells you to build one on a list, and a list already gives you every operation you need.

The first decision is which end of the list is the top. The answer is the end — the highest index — and there is a hard reason for it, not just a convention.

append() and pop() at the end do not move any other element. insert(0, x) and pop(0) must shift every remaining element one position across. Here is that cost measured with the timeit module: 200000 push-plus-pop pairs on a list of 100000 items.

import timeit

t_end   = timeit.timeit("s.append(1); s.pop()",
                        setup="s=list(range(100000))", number=200000)
t_front = timeit.timeit("s.insert(0,1); s.pop(0)",
                        setup="s=list(range(100000))", number=200000)
print("append/pop at end   :", round(t_end, 4), "sec")
print("insert/pop at front :", round(t_front, 4), "sec")

Output from one run on an ordinary laptop:

append/pop at end   : 0.0198 sec
insert/pop at front : 21.1736 sec

Over a thousand times slower for exactly the same number of operations. Run it on your own machine and the two numbers will be different — timings depend on the computer and on whatever else it happens to be doing — but the size of the gap is the point, and the gap does not go away. So the rule is settled: the last element of the list is the top of the stack. This benchmark is background reading; the board never asks you to time anything.

That gives this mapping, which is worth memorising as a block:

Stack operationPython on a list stk
Push itemstk.append(item)
Popstk.pop()
Peek / topstk[-1]
Number of itemslen(stk)
Index of the toplen(stk) - 1
Empty testlen(stk) == 0

Worked example — the standard function set. Write your stack once in this shape and it will answer almost every stack question in the paper. Note that display() walks backwards so that the top prints first, and that it removes nothing.

def isEmpty(stk):
    if stk == []:
        return True
    else:
        return False

def push(stk, item):
    stk.append(item)
    top = len(stk) - 1
    return top

def pop(stk):
    if isEmpty(stk):
        return "Underflow"
    else:
        return stk.pop()

def peek(stk):
    if isEmpty(stk):
        return "Underflow"
    else:
        top = len(stk) - 1
        return stk[top]

def display(stk):
    if isEmpty(stk):
        print("Stack is empty")
    else:
        top = len(stk) - 1
        print(stk[top], "<-- top")
        for i in range(top - 1, -1, -1):
            print(stk[i])

# ---- driver code ----
Stack = []
print("top index after push:", push(Stack, "Roll-1"))
print("top index after push:", push(Stack, "Roll-2"))
print("top index after push:", push(Stack, "Roll-3"))
display(Stack)
print("peek :", peek(Stack))
print("pop  :", pop(Stack))
display(Stack)
print("List behind the stack:", Stack)

Real output:

top index after push: 0
top index after push: 1
top index after push: 2
Roll-3 <-- top
Roll-2
Roll-1
peek : Roll-3
pop  : Roll-3
Roll-2 <-- top
Roll-1
List behind the stack: ['Roll-1', 'Roll-2']

Two things to notice. peek and pop both returned Roll-3, but only pop shortened the stack — that is the whole difference between them. And the last line prints the ordinary list underneath: a stack is not a new kind of object, it is a list you have agreed to use in a disciplined way.

Two traps that cost marks. First, pop() with no argument removes the last item, but pop(0) removes the first — that is not stack behaviour at all.

data = [10, 20, 30, 40]

print(data.pop())      # no argument -> removes the LAST item (stack behaviour)
print(data)

print(data.pop(0))     # argument 0 -> removes the FIRST item (NOT a stack)
print(data)
40
[10, 20, 30]
10
[20, 30]

Second, append() changes the list in place and returns None. Assigning its result destroys your stack, and the error only shows up much later:

t = []
t = t.append(9)
print("t after t = t.append(9) :", t)
t after t = t.append(9) : None
Push at the end (correct) stk.append(x) Constant time: nothing else in the list has to move. This is why the end is the top.
Push at the front (wrong end) stk.insert(0, x) Works, but shifts every element. In a measured run it was over a thousand times slower than append for the same job.
Pop the top stk.pop() No argument means the last item. stk.pop(-1) is identical.
Pop a chosen index stk.pop(i) stk.pop(0) removes the FIRST item, which is queue behaviour, not a stack. A classic exam trap.
Top index variable top = len(stk) - 1 CBSE model answers usually keep this variable. For an empty stack it works out to -1, so stk[len(stk)-1] becomes stk[-1] and raises IndexError: list index out of range — always test for empty before using it.
Display top to bottom for i in range(len(stk)-1, -1, -1): Prints without removing. For a 3-item stack this gives indexes [2, 1, 0].
Remember
  • A stack is implemented on an ordinary Python list; the last element is treated as the top.
  • The end of the list is chosen because append and pop there move no other element, while insert(0,x) and pop(0) shift every remaining element; in a measured run the front-end version took over a thousand times longer for identical work.
  • push is stk.append(item), pop is stk.pop(), peek is stk[-1], size is len(stk), top index is len(stk) - 1.
  • display() must loop with range(len(stk)-1, -1, -1) so the top prints first, and must not remove anything.
  • append() returns None, so stk = stk.append(x) silently replaces your stack with None.

Underflow, Overflow and Safe Operations

Quick answer Popping or peeking an empty list raises IndexError with two different messages, so a correct stack always tests for empty before it touches the top.

Underflow is the case the board actually tests, and it is the one your code must handle. It happens when you pop or peek a stack that has nothing left in it.

Here is what an unguarded stack does. The first pop succeeds and empties the stack; the second one crashes and the program dies on the spot.

stack = [5]
print(stack.pop())     # works, the stack is now empty
print(stack.pop())     # UNDERFLOW - this line crashes
print('never reached')

Real output — note that the fourth line never ran and the program ended with a non-zero exit code:

5
Traceback (most recent call last):
  File "", line 3, in 
    print(stack.pop())     # UNDERFLOW - this line crashes
          ~~~~~~~~~^^
IndexError: pop from empty list

Three things in that traceback are worth reading properly. Python echoes the whole of line 3, trailing comment and all, and draws ~~~~~~~~~^^ under the exact call that failed. The file is shown as only because this transcript was captured by feeding the lines straight to Python; run the same code from a saved .py file and that spot holds the path of your file instead. The last line is the one to memorise — the rest of the traceback changes with where and how you ran it.

Peeking an empty stack fails too, but with a different message. Examiners ask for the exact wording, so learn both:

stack = []
print(stack[-1])
Traceback (most recent call last):
  File "", line 2, in 
    print(stack[-1])
          ~~~~~^^^^
IndexError: list index out of range

Both are IndexError, but pop from empty list comes from the .pop() method and list index out of range comes from subscripting with [-1]. Writing stk[len(stk)-1] instead of stk[-1] changes nothing: on an empty stack that is stk[-1] too, and it raises the same list index out of range.

The fix is always the same: check for empty first. A correct implementation never lets the crash happen. It tests the stack and returns something sensible instead.

def pop(stk):
    if len(stk) == 0:                 # guard FIRST
        return None                   # sentinel: "nothing came back"
    return stk.pop()

def peek(stk):
    if len(stk) == 0:
        return None
    return stk[-1]

stack = [5]
print("peek :", peek(stack))
print("pop  :", pop(stack))
print("pop  :", pop(stack))           # underflow - handled, no crash
print("peek :", peek(stack))
print("Program finished normally")

Real output:

peek : 5
pop  : 5
pop  : None
peek : None
Program finished normally

The program now runs all the way to the end. Returning None is right for code that other code will call. In a board answer it is usually better to return or print the word Underflow or Stack Empty, because the question normally asks for a message. Either way, the guard comes before the operation, never after.

All three empty-tests mean the same thing. Use whichever reads best in your answer:

stack = []
print("Truth value of empty stack :", bool(stack))
stack.append(7)
print("Truth value of [7]         :", bool(stack))

# the three empty-checks that all mean the same thing
s = []
print(s == [], len(s) == 0, not s)
Truth value of empty stack : False
Truth value of [7]         : True
True True True

Because an empty list is falsy, while len(stk) > 0: and while stk: are interchangeable.

Overflow is the mirror case, and in Python it is largely theoretical. A list grows as long as memory allows, so a list-based stack does not fill up by itself. Overflow exists only if you impose a maximum size:

MAX = 3                      # capacity we choose to impose

def push(stk, item):
    if len(stk) == MAX:
        print("Overflow! Stack is full, cannot push", item)
    else:
        stk.append(item)
        print("Pushed", item, "->", stk)

s = []
push(s, "UPI-1")
push(s, "UPI-2")
push(s, "UPI-3")
push(s, "UPI-4")
print("Final stack:", s)
Pushed UPI-1 -> ['UPI-1']
Pushed UPI-2 -> ['UPI-1', 'UPI-2']
Pushed UPI-3 -> ['UPI-1', 'UPI-2', 'UPI-3']
Overflow! Stack is full, cannot push UPI-4
Final stack: ['UPI-1', 'UPI-2', 'UPI-3']

So the honest exam answer is: underflow can happen on any Python stack and must always be guarded; overflow happens only when the program sets a size limit (or the machine genuinely runs out of memory).

Underflow error from pop IndexError: pop from empty list Raised by stack.pop() when len(stack) is 0. Learn the exact wording.
Underflow error from peek IndexError: list index out of range Raised by stack[-1] on an empty stack. A different message from pop — the board asks for the right one.
Guarded pop if len(stk) == 0: return None Place before stk.pop(). CBSE answers often return or print 'Underflow' / 'Stack Empty' instead of None.
Guarded peek if len(stk) == 0: return 'Underflow' The same guard protects the stk[-1] lookup.
Overflow guard if len(stk) == MAX: print('Overflow') Only meaningful when you define MAX yourself; a Python list never fills up on its own.
Pop-until-empty loop while len(stk) > 0: print(stk.pop()) Safe by construction — the condition itself is the guard. while stk: is equivalent.
Remember
  • Popping an empty list raises IndexError: pop from empty list; peeking with [-1] raises IndexError: list index out of range.
  • A correct stack tests for empty BEFORE popping or peeking, and returns a sentinel such as None or the message 'Underflow'.
  • len(stk) == 0, stk == [] and not stk are three ways to write the same empty test.
  • Python lists grow dynamically, so overflow can only occur if the programmer imposes a MAX size.
  • The guard must come first — checking after the operation is too late, the exception has already been raised.

Board-Style Stack Programs

Quick answer Almost every board question has the same shape — push only the entries of a list or dictionary that satisfy a condition, then pop them all and display — so learn the shape once.

Stack questions in the CBSE paper are remarkably consistent. You are handed a list or a dictionary, you push only the entries that satisfy a condition, and then you pop everything and display it. Learn that shape and the marks come easily.

Worked example 1 — a dictionary of prices in rupees. Push the names of items costing more than 500, then pop them all.

# Prices in rupees at a school stationery counter
items = {"Pen": 20, "Geometry Box": 250, "Scientific Calculator": 1200,
         "School Bag": 899, "Notebook": 60, "Drawing Board": 540}

def push_costly(stk, d):
    for name in d:
        if d[name] > 500:
            stk.append(name)

def pop_all(stk):
    if len(stk) == 0:
        print("Stack empty - nothing to pop")
        return
    while len(stk) > 0:
        print("Popped:", stk.pop())
    print("Stack empty")

stack = []
push_costly(stack, items)
print("Stack after pushing :", stack)
pop_all(stack)

Real output:

Stack after pushing : ['Scientific Calculator', 'School Bag', 'Drawing Board']
Popped: Drawing Board
Popped: School Bag
Popped: Scientific Calculator
Stack empty

Two points worth marks. Looping for name in d gives you the keys, so d[name] is needed for the value. And because Python keeps a dictionary in insertion order, the stack fills in that order — Scientific Calculator went in first, so it comes out last. Also note that more than 500 is a strict test: an item priced exactly 500 would not be pushed.

Worked example 2 — a list of numbers, with the empty case handled. This is the version that separates full marks from partial marks, because it survives the case where nothing qualifies.

def PUSH(Arr):
    stack = []
    for num in Arr:
        if num % 5 == 0:
            stack.append(num)
    if len(stack) > 0:
        print("Stack:", stack)
    else:
        print("Empty Stack")
    return stack

def POP(stack):
    if len(stack) == 0:
        print("Underflow")
        return None
    return stack.pop()

s = PUSH([12, 25, 7, 40, 55, 9, 100])
print("Popped:", POP(s))
print("Popped:", POP(s))
print("Now   :", s)

t = PUSH([1, 2, 3, 4])
print("Popped:", POP(t))

Real output:

Stack: [25, 40, 55, 100]
Popped: 100
Popped: 55
Now   : [25, 40]
Empty Stack
Underflow
Popped: None

The last three lines are the ones that earn the mark. No number in [1, 2, 3, 4] is divisible by 5, so the stack stays empty, PUSH prints Empty Stack instead of an empty pair of brackets, and POP reports Underflow instead of crashing.

Worked example 3 — a stack of records. An item pushed onto a stack need not be a single number. It can be a whole list.

# A stack holding whole records (each record is a list)
stack = []

def push_book(stk, bno, title, price):
    stk.append([bno, title, price])

def pop_book(stk):
    if not stk:
        return "Underflow"
    return stk.pop()

push_book(stack, 101, "Sumita Arora CS", 545)
push_book(stack, 102, "NCERT CS Part 2", 180)
push_book(stack, 103, "Together With CS", 420)

print("Stack :", stack)
print("Top   :", stack[-1])
print("Pop   :", pop_book(stack))
print("Pop   :", pop_book(stack))
print("Pop   :", pop_book(stack))
print("Pop   :", pop_book(stack))

Real output:

Stack : [[101, 'Sumita Arora CS', 545], [102, 'NCERT CS Part 2', 180], [103, 'Together With CS', 420]]
Top   : [103, 'Together With CS', 420]
Pop   : [103, 'Together With CS', 420]
Pop   : [102, 'NCERT CS Part 2', 180]
Pop   : [101, 'Sumita Arora CS', 545]
Pop   : Underflow

append() put each three-element list in as one stack item, which is what you want. Had you written extend() the record would have been flattened into three separate items and the stack would be nonsense. The fourth pop hits the guard and returns Underflow rather than raising IndexError.

Push only items passing a test if x % 5 == 0: stk.append(x) Placed inside a for loop over the list. This is the single most common stack question shape.
Loop over a dictionary for k in d: k is the KEY; use d[k] for the value. Insertion order is preserved, so the stack order is predictable.
Strictly greater than if d[k] > 70: 70 itself is NOT pushed. 'More than 70' excludes 70; 'at least 70' would need >=.
Case-insensitive match if d[k].upper() == 'TATA': Handles 'TATA', 'tata' and 'Tata' in one test — board questions often say 'in any case'.
Pop everything and print while len(stk) > 0: print(stk.pop()) Prints top to bottom and empties the stack. Follow it with print('Stack Empty').
Push a whole record stk.append([bno, name, price]) append inserts the list as ONE item. extend would flatten it into three — do not confuse them.
Remember
  • The board pattern is: loop over the data, push only what satisfies the condition, then pop everything and display.
  • Looping over a dictionary yields keys, so use d[key] to test the value; Python preserves insertion order, so the stack order is predictable.
  • Read the inequality carefully — 'more than 70' excludes 70 itself, and a wrong comparison operator loses the mark.
  • Always handle the case where nothing qualifies: print 'Empty Stack' rather than an empty list, and guard POP against underflow.
  • append() pushes a whole list as a single item; extend() would flatten it and break the record.

Applications: Reversal and Balanced Brackets

Quick answer Reversing a string and checking whether an expression's brackets are balanced are the two classic applications, and both work only because a stack is LIFO.

Two applications appear again and again, and both work only because the structure is LIFO. If you understand why, you can reconstruct the code in the exam hall without memorising it. Nothing here needs anything beyond push, pop and a list.

Application 1 — reversing a string. Push every character onto the stack, then pop them all back. The first character pushed is the last one popped, so the order flips for free. You do nothing to reverse it; the data structure does it.

def reverse_using_stack(text):
    stack = []
    for ch in text:          # PUSH every character
        stack.append(ch)
    result = ""
    while len(stack) > 0:    # POP them back - comes out reversed
        result = result + stack.pop()
    return result

print(reverse_using_stack("PRIODEMY"))
print(reverse_using_stack("MALAYALAM"))
print(reverse_using_stack("Delhi"))

word = "NAMAN"
if reverse_using_stack(word) == word:
    print(word, "is a palindrome")
else:
    print(word, "is not a palindrome")

Real output:

YMEDOIRP
MALAYALAM
ihleD
NAMAN is a palindrome

MALAYALAM comes back unchanged, which is precisely what palindrome means — so the same function gives you a two-line palindrome checker, as the last two lines show.

Application 2 — checking balanced parentheses. This is the classic. Push every opening bracket. When a closing bracket arrives, pop the stack and check that the two are partners. The stack is the right tool because brackets nest: the bracket you must close next is always the most recent one you opened, which is LIFO exactly.

There are three distinct ways an expression can fail:

  1. A closing bracket arrives when the stack is empty — nothing was open to close.
  2. The bracket popped is the wrong partner, as in {[(])}.
  3. The string ends with items still on the stack — something was opened and never closed.
def is_balanced(expr):
    stack = []
    pairs = {')': '(', ']': '[', '}': '{'}
    for ch in expr:
        if ch in "([{":
            stack.append(ch)                  # opening -> push
        elif ch in ")]}":
            if len(stack) == 0:               # closing with nothing open
                return False
            if stack.pop() != pairs[ch]:      # wrong partner
                return False
    return len(stack) == 0                    # leftovers means unclosed

tests = ["(a+b)*(c-d)", "[(a+b)*c]", "(a+b))", "((a+b)",
         "{[()]}", "{[(])}", "a+b", "((()))"]
for t in tests:
    print(t.ljust(12), "->", is_balanced(t))

Real output:

(a+b)*(c-d)  -> True
[(a+b)*c]    -> True
(a+b))       -> False
((a+b)       -> False
{[()]}       -> True
{[(])}       -> False
a+b          -> True
((()))       -> True

Read the three failures against the three cases. (a+b)) fails on case 1 — the second closing bracket meets an empty stack. ((a+b) fails on case 3 — one opener is still on the stack when the loop ends. {[(])} fails on case 2 — the ] pops a (, and ( is not the partner of ]. And a+b has no brackets at all and is correctly reported balanced: an empty stack at the end is a pass, not a failure.

The order of the two checks inside the elif matters. The empty test must come first. If it did not, an input like )( would make stack.pop() raise IndexError instead of returning False — the underflow guard from the previous section, doing real work.

Application 3 — the browser Back button. The same structure, in a product you use daily. Every page you open is pushed; Back is a pop.

# Browser "Back" button: every page you open is pushed
history = []

def visit(page):
    history.append(page)
    print("Opened :", page)

def back():
    if len(history) <= 1:
        print("Back   : no previous page")
        return None
    history.pop()                 # throw away the current page
    print("Back   :", history[-1])
    return history[-1]

visit("priodemy.com")
visit("priodemy.com/courses")
visit("priodemy.com/courses/python")
back()
back()
back()
print("History:", history)

Real output:

Opened : priodemy.com
Opened : priodemy.com/courses
Opened : priodemy.com/courses/python
Back   : priodemy.com/courses
Back   : priodemy.com
Back   : no previous page
History: ['priodemy.com']

The third back() is the underflow case wearing a different hat: only the first page is left, there is nothing behind it, so the function refuses instead of crashing. This is why every browser greys out the Back button on the first page of a tab.

Push every character for ch in text: stk.append(ch) Step one of reversal. Works for any iterable, including a list.
Rebuild while popping while stk: result = result + stk.pop() while stk is True as long as the stack has items, because an empty list is falsy.
Palindrome test if reverse_using_stack(w) == w: Reuses the reversal function. NAMAN and MALAYALAM pass; Delhi does not.
Bracket pair map pairs = {')': '(', ']': '[', '}': '{'} Maps each closing bracket to its opening partner, so one dictionary lookup replaces three if-conditions.
Guard before matching if len(stk) == 0: return False Must come BEFORE stk.pop() inside the closing-bracket branch, or input like )( raises IndexError.
Final balance test return len(stk) == 0 Leftover openers mean something was never closed. An expression with no brackets at all is balanced.
Remember
  • Reversal works because the first character pushed is the last one popped — the stack does the reversing, not your code.
  • Comparing a string with its stack-reversal is a complete palindrome test.
  • Bracket matching suits a stack because the bracket that must close next is always the most recently opened one.
  • An expression is unbalanced in three ways: a closer with an empty stack, a mismatched partner, or leftover openers at the end.
  • In the bracket checker the empty-stack test must come before the pop, otherwise an input like )( raises IndexError instead of returning False.

The formula sheet

Every formula in this chapter, in one place — screenshot it before your exam.

stack = []
Create an empty stack
stack.append(item)
Push (insert on top)
stack.pop()
Pop (remove from top)
stack[-1]
Peek (read the top)
len(stack)
Number of items
len(stack) == 0
Is the stack empty?
stk.append(x)
Push at the end (correct)
stk.insert(0, x)
Push at the front (wrong end)
stk.pop()
Pop the top
stk.pop(i)
Pop a chosen index
top = len(stk) - 1
Top index variable
for i in range(len(stk)-1, -1, -1):
Display top to bottom
IndexError: pop from empty list
Underflow error from pop
IndexError: list index out of range
Underflow error from peek
if len(stk) == 0: return None
Guarded pop
if len(stk) == 0: return 'Underflow'
Guarded peek
if len(stk) == MAX: print('Overflow')
Overflow guard
while len(stk) > 0: print(stk.pop())
Pop-until-empty loop
if x % 5 == 0: stk.append(x)
Push only items passing a test
for k in d:
Loop over a dictionary
if d[k] > 70:
Strictly greater than
if d[k].upper() == 'TATA':
Case-insensitive match
while len(stk) > 0: print(stk.pop())
Pop everything and print
stk.append([bno, name, price])
Push a whole record
for ch in text: stk.append(ch)
Push every character
while stk: result = result + stk.pop()
Rebuild while popping
if reverse_using_stack(w) == w:
Palindrome test
pairs = {')': '(', ']': '[', '}': '{'}
Bracket pair map
if len(stk) == 0: return False
Guard before matching
return len(stk) == 0
Final balance test

Test yourself

Tap an answer to check it instantly — you'll see why it's right, and what to revise if it isn't.

0 correct · 0/12 answered
Q1

What is the output? s = [] s.append(1) s.append(2) s.append(3) s.pop() s.append(4) print(s)

Q2

What is the output? s = ['a', 'b', 'c'] print(s.pop(), s.pop(), s)

Q3

What is the output? stack = [] for i in range(1, 6): if i % 2 == 0: stack.append(i) else: if stack: stack.pop() print(stack)

Q4

What is the output? def f(x): st = [] for ch in x: st.append(ch) r = '' while st: r = r + st.pop() return r print(f("STACK"))

Q5

What is the output? data = [3, 5, 7] data.append([9, 11]) print(len(data), data.pop())

Q6

What is the output? S = [] for w in "COMPUTER": if w in "AEIOU": S.append(w) while S: print(S.pop(), end=" ")

Q7

What is the output? st = [10, 20, 30, 40, 50] print(st.pop(1), st[-1], len(st))

Q8

A stack is implemented on a list named s, and s is currently empty. Which of these raises an IndexError?

Q9

Which statement correctly performs a peek (reads the top item without removing it) on a stack named stk?

Q10

In a stack implemented on a Python list, why is the END of the list (the highest index) chosen as the top rather than index 0?

Q11

Under what circumstance can overflow occur in a stack implemented on a Python list?

Q12

What does the expression stk.append(item) evaluate to?

NCERT solutions & previous-year questions

Step-by-step model answers — tap a question to reveal the full solution.

NCERT questions 6

1 What is a stack? Why is it called a LIFO data structure? Give two real-life examples.Concept of a stack

A stack is a linear data structure in which insertion and deletion of elements are permitted at one end only. That end is called the top of the stack. No element in the middle or at the bottom can be reached until the elements above it have been removed.

Because insertion and removal share the same end, the element that was inserted most recently is the one lying at the top, and therefore the one that will be removed first. This behaviour is described as LIFO — Last In, First Out.

Two real-life examples:

  1. A pile of steel plates at a canteen counter. A washed plate is placed on top and the next customer lifts the top plate — the plate placed last is taken first.
  2. The Undo (Ctrl+Z) feature of a text editor. It reverses your most recent action first, then the one before that.

Demonstration in Python:

stack = []
stack.append(10)      # 10 goes in first
stack.append(20)
stack.append(30)      # 30 goes in last
print("Stack :", stack)
print("Out 1 :", stack.pop())   # 30 comes out first  -> LIFO
print("Out 2 :", stack.pop())
print("Out 3 :", stack.pop())   # 10 comes out last

Output:

Stack : [10, 20, 30]
Out 1 : 30
Out 2 : 20
Out 3 : 10

The insertion order was 10, 20, 30 and the removal order was 30, 20, 10 — the exact reverse. That reversal is the observable signature of LIFO.

2 Write a program to reverse a string using a stack.Application of stack

Push every character of the string onto a stack. Since the first character pushed is the last one popped, popping the whole stack yields the characters in reverse order. No reversing logic is needed — the LIFO property supplies it.

s = input("Enter a string: ")
stack = []
for ch in s:
    stack.append(ch)          # push every character
rev = ""
while len(stack) > 0:
    rev = rev + stack.pop()   # pop them back
print("Reversed string is:", rev)

Sample run, with Bharat supplied at the prompt:

Enter a string: Reversed string is: tarahB

(The word does not appear after the prompt in this captured transcript because the input was piped in rather than typed. When you type it yourself the terminal echoes it, so you will see Enter a string: Bharat on the first line and the result on the next.)

How it works. After the for loop the stack holds ['B','h','a','r','a','t'] with 't' on top. The while loop pops 't', then 'a', then 'r', and so on, concatenating each to rev. The loop condition len(stack) > 0 doubles as the underflow guard, so pop() is never called on an empty stack.

The same function gives a palindrome test for free — if the reversed string equals the original, the word is a palindrome.

3 What do you understand by underflow and overflow of a stack? Which of the two can actually occur in a stack implemented on a Python list, and why?Error conditions

Underflow is the condition that arises when a pop or peek operation is attempted on a stack that is already empty. There is no top element to return, so the operation cannot be performed.

Overflow is the condition that arises when a push is attempted on a stack that has already reached its maximum permitted size. There is no room for the new element.

In Python, underflow can genuinely occur. It raises an exception:

stack = [5]
print(stack.pop())     # works, the stack is now empty
print(stack.pop())     # UNDERFLOW - this line crashes
print('never reached')
5
Traceback (most recent call last):
  File "", line 3, in 
    print(stack.pop())     # UNDERFLOW - this line crashes
          ~~~~~~~~~^^
IndexError: pop from empty list

The message on the last line is the part that must be quoted exactly. Python echoes the whole of the offending line, comment included, and marks the failing call with ~~~~~~~~~^^; the file name shown depends on how you ran the code, and is here only because the lines were fed straight to Python instead of being saved in a file.

Peeking an empty stack with stack[-1] also fails, but with the different message IndexError: list index out of range.

Overflow, however, cannot occur on its own, because a Python list grows dynamically as elements are appended — it has no fixed capacity. Overflow exists only if the programmer deliberately imposes a limit:

MAX = 3

def push(stk, item):
    if len(stk) == MAX:
        print("Overflow! Stack is full, cannot push", item)
    else:
        stk.append(item)
        print("Pushed", item, "->", stk)

s = []
push(s, "UPI-1")
push(s, "UPI-2")
push(s, "UPI-3")
push(s, "UPI-4")
Pushed UPI-1 -> ['UPI-1']
Pushed UPI-2 -> ['UPI-1', 'UPI-2']
Pushed UPI-3 -> ['UPI-1', 'UPI-2', 'UPI-3']
Overflow! Stack is full, cannot push UPI-4

Conclusion: underflow must always be guarded against in a Python stack; overflow is relevant only for a bounded stack, or in the extreme case where the machine runs out of memory.

4 Write a menu-driven program to implement a stack of integers with push, pop and display operations.Implementation using list

The program keeps the stack in a list and repeats a menu until the user chooses to exit. Both pop and display check for an empty stack before touching it.

stack = []

def push(stk):
    n = int(input("Enter number to push: "))
    stk.append(n)
    print(n, "pushed")

def pop(stk):
    if len(stk) == 0:
        print("Underflow! Stack is empty")
    else:
        print(stk.pop(), "popped")

def display(stk):
    if len(stk) == 0:
        print("Stack is empty")
    else:
        print("Top to bottom:", end=" ")
        for i in range(len(stk) - 1, -1, -1):
            print(stk[i], end=" ")
        print()

while True:
    print("1.Push  2.Pop  3.Display  4.Exit")
    ch = int(input("Enter choice: "))
    if ch == 1:
        push(stack)
    elif ch == 2:
        pop(stack)
    elif ch == 3:
        display(stack)
    elif ch == 4:
        print("Bye")
        break
    else:
        print("Invalid choice")

Sample run with the keystrokes 1, 15, 1, 40, 3, 2, 2, 2, 4 entered in that order:

1.Push  2.Pop  3.Display  4.Exit
Enter choice: Enter number to push: 15 pushed
1.Push  2.Pop  3.Display  4.Exit
Enter choice: Enter number to push: 40 pushed
1.Push  2.Pop  3.Display  4.Exit
Enter choice: Top to bottom: 40 15 
1.Push  2.Pop  3.Display  4.Exit
Enter choice: 40 popped
1.Push  2.Pop  3.Display  4.Exit
Enter choice: 15 popped
1.Push  2.Pop  3.Display  4.Exit
Enter choice: Underflow! Stack is empty
1.Push  2.Pop  3.Display  4.Exit
Enter choice: Bye

(The digits are not echoed after each prompt in this captured transcript because the input was piped in rather than typed. On your own screen each number appears after the colon as you type it.)

Note the two design points. display uses range(len(stk)-1, -1, -1) so the top prints first and nothing is removed. The third pop finds the stack empty and prints Underflow instead of crashing with IndexError.

5 A stack of characters contains A, C, D, F, K with K at the top. Show the contents of the stack and the top element after each of the following operations, performed in order: PUSH('M'), POP, POP, PUSH('R').Tracing stack operations

Work through the operations one at a time. Writing the stack bottom to top (the way Python prints a list) makes the top the rightmost element.

OperationStack (bottom to top)Top
InitialA, C, D, F, KK
PUSH('M')A, C, D, F, K, MM
POP (returns M)A, C, D, F, KK
POP (returns K)A, C, D, FF
PUSH('R')A, C, D, F, RR

Verification in Python:

stack = ['A', 'C', 'D', 'F', 'K']       # K is on top
print("Start   :", stack, " top =", stack[-1])

stack.append('M')
print("PUSH(M) :", stack, " top =", stack[-1])

print("POP ->", stack.pop(), " :", stack, " top =", stack[-1])
print("POP ->", stack.pop(), " :", stack, " top =", stack[-1])

stack.append('R')
print("PUSH(R) :", stack, " top =", stack[-1])

Output:

Start   : ['A', 'C', 'D', 'F', 'K']  top = K
PUSH(M) : ['A', 'C', 'D', 'F', 'K', 'M']  top = M
POP -> M  : ['A', 'C', 'D', 'F', 'K']  top = K
POP -> K  : ['A', 'C', 'D', 'F']  top = F
PUSH(R) : ['A', 'C', 'D', 'F', 'R']  top = R

Final answer: the stack contains A, C, D, F, R with R at the top. The two elements deleted, in order, were M and K. Note that the second POP removed K and not M — M had already been taken by the first POP, which is the LIFO rule at work.

6 Write a function that accepts a list of numbers and pushes only the odd numbers onto a stack. Then display the contents of the stack from top to bottom without deleting any element.Implementation using list

Two separate functions are needed. push_odd() filters and pushes; show() only reads, so it must walk the list backwards with a range rather than calling pop().

def push_odd(stk, nums):
    for n in nums:
        if n % 2 != 0:
            stk.append(n)

def show(stk):
    if len(stk) == 0:
        print("Stack is empty")
        return
    for i in range(len(stk) - 1, -1, -1):
        print(stk[i])
    print("(stack untouched:", stk, ")")

s = []
push_odd(s, [12, 7, 40, 33, 8, 91, 2])
show(s)

Output:

91
33
7
(stack untouched: [7, 33, 91] )

Explanation. From [12, 7, 40, 33, 8, 91, 2] the odd numbers 7, 33 and 91 are pushed in that order, so 91 ends up on top. show() starts at index len(stk)-1 (which is 2) and counts down to 0, printing 91, 33 and 7 — top to bottom.

The last line proves the requirement was met: the stack still holds [7, 33, 91] after display. Had show() been written with a while loop calling pop(), the output would have looked identical but the stack would have been emptied, which the question forbids. The empty-stack check at the top handles the case where the input list contains no odd numbers at all.

Previous-year board questions 4

Q1 Write a function in Python, PUSH(Arr), where Arr is a list of numbers. From this list, push all numbers divisible by 5 into a stack implemented by using a list. Display the stack if it has at least one element, otherwise display an appropriate error message. (2 marks) 2020

The two marks are split: one for the filtered push, one for correctly handling the case where no number qualifies.

def PUSH(Arr):
    stack = []
    for n in Arr:
        if n % 5 == 0:
            stack.append(n)
    if len(stack) > 0:
        print("Stack:", stack)
    else:
        print("Empty Stack")
    return stack

PUSH([15, 22, 30, 7, 45, 8])
PUSH([2, 3, 4])

Output:

Stack: [15, 30, 45]
Empty Stack

Marking points. The condition is n % 5 == 0, which is true for 15, 30 and 45. They are appended in the order they appear in the list, so 45 ends up on top. The second call proves the error branch works: none of 2, 3, 4 is divisible by 5, so the function prints Empty Stack rather than an empty pair of brackets. Candidates who omit the else branch lose the second mark even though the first call prints correctly.

Q2 Vedika has created a dictionary containing names and marks as key-value pairs of 6 students. Write a program, with separate user-defined functions, to perform the following operations: (i) Push the keys (name of the student) of the dictionary into a stack, where the corresponding value (marks) is greater than 70. (ii) Pop and display the content of the stack. For example, if the sample content of the dictionary is {"Aarav":82, "Diya":65, "Kabir":91, "Ishaan":70, "Meera":78, "Rohan":55}, the output should be: Meera Kabir Aarav 2022

Two functions are required, as the question says explicitly. PushEl() filters and pushes; PopEl() empties the stack and prints, then reports Stack Empty.

Marks = {"Aarav": 82, "Diya": 65, "Kabir": 91,
         "Ishaan": 70, "Meera": 78, "Rohan": 55}
stack = []

def PushEl(d):
    for name in d:
        if d[name] > 70:
            stack.append(name)

def PopEl():
    while len(stack) > 0:
        print(stack.pop())
    print("Stack Empty")

PushEl(Marks)
print("Stack:", stack)
PopEl()

Output:

Stack: ['Aarav', 'Kabir', 'Meera']
Meera
Kabir
Aarav
Stack Empty

The trap in this question is Ishaan. His marks are exactly 70, and the question says greater than 70, so he must not be pushed. Writing >= instead of > produces four names and costs a mark.

Why the output order is Meera, Kabir, Aarav. Looping for name in d visits the keys in insertion order, so Aarav is pushed first and Meera last. Meera therefore sits on top and is popped first — the reverse of the push order, which is exactly the LIFO behaviour the examiner is checking. The final print("Stack Empty") after the loop is expected in the model answer. The names and marks used here are a worked substitute for the ones printed on the original paper; the logic being tested is identical.

Q3 Write a function in Python, Push(Vehicle), where Vehicle is a dictionary containing details of vehicles in the form Car_Name : Maker. The function should push the names of only those cars into a stack which are manufactured by "TATA" (in any case — uppercase, lowercase or mixed). Also display the popped elements. For example, if the dictionary is {"Nexon":"TATA", "Swift":"Maruti", "Harrier":"tata", "Creta":"Hyundai", "Punch":"Tata", "Venue":"Hyundai"} the stack should contain Nexon, Harrier and Punch. (3 marks) 2023

The examiner is testing one specific idea here: the maker name appears in three different cases, so a plain == comparison would miss two of the three cars. Normalise the case with .upper() before comparing.

def Push(Vehicle):
    st = []
    for car in Vehicle:
        if Vehicle[car].upper() == "TATA":
            st.append(car)
    return st

Cars = {"Nexon": "TATA", "Swift": "Maruti", "Harrier": "tata",
        "Creta": "Hyundai", "Punch": "Tata", "Venue": "Hyundai"}
s = Push(Cars)
print("Stack:", s)
while s:
    print("Popped:", s.pop())
print("Stack Empty")

Output:

Stack: ['Nexon', 'Harrier', 'Punch']
Popped: Punch
Popped: Harrier
Popped: Nexon
Stack Empty

Marking points. Looping over the dictionary gives the car name as the key, so Vehicle[car] is the maker — swapping these is the commonest error and pushes the makers instead of the cars. .upper() converts "TATA", "tata" and "Tata" all to "TATA", so all three qualify; .lower() == "tata" would be equally acceptable. Finally, the pop order is Punch, Harrier, Nexon — the reverse of the push order, because the stack is LIFO. while s: is safe here since an empty list is falsy, so no underflow can occur.

Q4 Write the definition of a user-defined function push_even(N) which accepts a list of integers in the parameter N and pushes all those integers which are even from the list N into a stack named EvenNumbers. Write a function pop_even() to pop the topmost number from the stack and return it. If the stack is already empty, the function should return "None". Write a function Disp_even() to display all the elements of the stack without deleting them; if the stack is empty, display "None". (3 marks) 2024

This is the full three-function set, and each function carries roughly one mark. Note that both pop_even() and Disp_even() must handle the empty stack, which is where marks are usually lost.

EvenNumbers = []

def push_even(N):
    for n in N:
        if n % 2 == 0:
            EvenNumbers.append(n)

def pop_even():
    if len(EvenNumbers) == 0:
        return "None"
    return EvenNumbers.pop()

def Disp_even():
    if len(EvenNumbers) == 0:
        print("None")
    else:
        for i in range(len(EvenNumbers) - 1, -1, -1):
            print(EvenNumbers[i])

push_even([10, 15, 22, 33, 40, 7])
Disp_even()
print("popped:", pop_even())
print("popped:", pop_even())
print("popped:", pop_even())
print("popped:", pop_even())
Disp_even()

Output:

40
22
10
popped: 40
popped: 22
popped: 10
popped: None
None

Reading the output. From [10, 15, 22, 33, 40, 7] the even numbers 10, 22 and 40 are pushed in that order, so 40 is the top. Disp_even() prints 40, 22, 10 — top to bottom — using range(len-1, -1, -1), and crucially it does not remove anything, which is why the three pops that follow still succeed.

The fourth pop_even() is the mark-earning line: the stack is now empty, so the guard fires and returns "None" instead of letting EvenNumbers.pop() raise IndexError: pop from empty list. The final Disp_even() hits the same empty condition and prints None.

One detail to be clear about. Because the question asks for "None" in quotes, the function returns the four-character string "None", not Python's None value. The two print identically, which is why the output line reads popped: None either way; print(repr(pop_even())) would show 'None' for the string and None for the value. Returning the real None, or printing the word Underflow, is equally acceptable unless the question pins down the wording.

Part of Priodemy for School

Interactive CBSE lessons, Class 8–12 — free with every school on Priodemy EduSuite. Explore more chapters and labs on the Priodemy for School hub.

Ask AI