A stack is a linear data structure in computer science that follows the Last-In-First-Out (LIFO) principle. In a stack, the last element added is the first one to be removed. Think of it as a collection of objects stacked on top of each other, where you can only add or remove items from the top.
Key operations and characteristics of a stack:
Push: Adding an element to the top of the stack. This operation is sometimes referred to as "pushing onto the stack."
Pop: Removing and retrieving the element from the top of the stack. This operation is sometimes referred to as "popping from the stack."
Peek (Top): Retrieving the element from the top of the stack without removing it.
Empty Check: Checking if the stack is empty.
Common use cases and applications of stacks:
Function Call Management: Stacks are used to manage function calls and their local variables in programming languages. Each time a function is called, its local variables and return address are pushed onto the stack. When the function completes, its information is popped from the stack.
Expression Evaluation: Stacks are used to evaluate arithmetic expressions. Operators and operands are pushed onto the stack, and when an operator is encountered, the necessary operands are popped, and the result is pushed back onto the stack.
Undo and Redo: Many applications use stacks to implement undo and redo functionality. Each change is pushed onto the stack, allowing for easy reversal of actions.
Backtracking and Recursion: Stacks can help in backtracking algorithms and recursion by storing states or data as you explore different paths.
Browser History: Browsers use stacks to keep track of visited websites, allowing users to go back to the previous page.
Implementation of a stack can be done using arrays or linked lists. Arrays are simpler to implement, but they have a fixed size. Linked lists allow dynamic resizing but require more memory due to the additional overhead of pointers.
Here's a simple example of a stack's behavior with a sequence of push and pop operations:
Stack: empty
Push 5:
Stack: 5
Push 10:
Stack: 10 -> 5
Push 7:
Stack: 7 -> 10 -> 5
Pop:
Popped: 7
Stack: 10 -> 5
Remember that a stack follows the LIFO principle, so the last element that was pushed onto the stack (in this case, 7) is the first one to be popped.
Here's an example of implementing a stack in Python using a list. While Python's list
is versatile and can be used as a stack, keep in mind that it has some performance limitations for larger stacks due to resizing when elements are added or removed.
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
return len(self.items) == 0
def push(self, item):
self.items.append(item)
def pop(self):
if not self.is_empty():
return self.items.pop()
else:
raise Exception("Stack is empty")
def peek(self):
if not self.is_empty():
return self.items[-1]
else:
raise Exception("Stack is empty")
def size(self):
return len(self.items)
# Example usage
stack = Stack()
print("Is the stack empty?", stack.is_empty()) # True
stack.push(5)
stack.push(10)
stack.push(7)
print("Peek:", stack.peek()) # 7
print("Size:", stack.size()) # 3
popped_item = stack.pop()
print("Popped item:", popped_item) # 7
print("Size after pop:", stack.size()) # 2
print("Is the stack empty?", stack.is_empty()) # False
In this example, we've defined a Stack
class with methods for the standard stack operations: push
, pop
, peek
, is_empty
, and size
. We're using a Python list to store the stack elements. The methods perform the necessary operations to maintain the LIFO behavior of the stack.
Keep in mind that Python's list
can be used directly as a stack (using append()
for push
and pop()
for pop
), but creating a separate class with explicit methods can help encapsulate the stack behavior and provide additional control over the operations.