Julie
Julie
Somobay
Section/Year: BSIT 2A
Subject: Data Structure Algorithm
Instructor: Carl Justine G. Pinedo
Part 1: Stack
1. Explain the concept of a Stack and its applications. Give two examples
where a stack would be useful.
Answer:
A stack is a linear data structure that follows the Last-In, First-Out (LIFO)
principle. Elements are added (pushed) and removed (popped) from the
same end, called the top. This is like a stack of plates; you can only access
the top plate.
Two examples where stacks are useful:
- Function Call Stack: When a function calls another function, the current
function’s state (local variables, return address) is pushed onto the stack.
When the called function returns, its state is popped, and execution resumes
from where it left off. This allows for nested function calls.
- Undo/Redo Functionality: Many applications use stacks to implement
undo/redo. Each action is pushed onto the stack. Undoing an action pops the
last action from the stack.
2. Describe the LIFO (Last In, First Out) principle and how it applies to stack
operations. LIFO means the last element added to the stack is the first one
removed. In stack operations:
- push() adds an element to the top.
- pop() removes the element from the top.
- peek() views the top element without removing it.
All operations primarily affect the top element, adhering to the LIFO order.
Def pop(self):
If not self.isEmpty():
Return self.items.pop() # Remove and return the last item
Else:
Return None #Handle empty stack case
Def peek(self):
If not self.isEmpty():
Return self.items[-1] # Return the last item without removing
Else:
Return None #Handle empty stack case
Def isEmpty(self):
Return len(self.items) == 0
Def __str__(self):
Return str(self.items) #String representation of the stack
Def pop(self):
If self.isEmpty():
Raise Exception(“Stack is empty. Cannot pop item.”)
Return self.stack.pop()
Def peek(self):
If self.isEmpty():
Raise Exception(“Stack is empty. Cannot peek.”)
Return self.stack[-1]
Def isEmpty(self):
Return len(self.stack) == 0
Def isFull(self):
Return len(self.stack) == self.max_size
Def size(self):
Return len(self.stack)
Def __str__(self):
If self.isEmpty():
Return “Stack is empty.”
Return “Stack contents: [“ + “, “.join(map(str, self.stack)) + “]”
# Example usage:
If __name__ == “__main__”:
Stack = LimitedStack(3)
Stack.push(1)
Stack.push(2)
Print(stack) # Output: Stack contents: [1, 2]
Stack.push(3)
Print(stack) # Output: Stack contents: [1, 2, 3]
Try:
Stack.push(4) # This should raise an exception
Except Exception as e:
Print€ # Output: Stack is full. Cannot push new item.
Print(stack.pop()) # Output: 3
Print(stack) # Output: Stack contents: [1, 2]
Print(stack.isFull()) # Output: False
def pop(self):
if self.isEmpty():
raise Exception("Stack is empty. Cannot pop item.")
return self.stack.pop()
def peek(self):
if self.isEmpty():
raise Exception("Stack is empty. Cannot peek.")
return self.stack[-1]
def isEmpty(self):
return len(self.stack) == 0
def isFull(self):
return len(self.stack) == self.max_size
def size(self):
return len(self.stack)
# Example usage:
if __name__ == "__main__":
stack = LimitedStack(3)
print(stack.isFull()) # Output: False
stack.push(1)
stack.push(2)
stack.push(3)
try:
stack.push(4) # This should raise an exception
except Exception as e:
print(e) # Output: Stack is full. Cannot push new item.
print(stack.pop()) # Output: 3
print(stack.isFull()) # Output: FalsePart
Part 2 : Queue
Theory Questions:
6. What is Queue? How does it the differ from a Stack?
In a queue, data is added to one end and removed from the other. The
first item added to the queue is the first to be accessed. Queues are
open at both ends, with one end used for inserting data and the other
for removing it. In a stack, data is added to the top and removed from
the bottom. The last item added to the stack is the first to be accessed.
Stacks are limited access data structures, meaning elements can only
be added and removed from the top. Stacks and queues are
fundamental data structures that are often used in computer
programming to organize and manipulate data efficiently.
7. Explain the FIFO ( First In, First Out) principle and how it applies to
queue operations.
The FIFO principle states that the first element added to a queue will
be the first one to be removed. This is analogous to a real-world queue,
such as a line at a grocery store. The first person in line will be the first
one to be served.
In queue operations, the FIFO principle is implemented through the
following methods:
- enqueue(): Adds an element to the rear of the queue.
- dequeue(): Removes and returns the element at the front of the
queue.
The FIFO principle ensures that elements are processed in the order
they were added to the queue. This is essential for applications where
order of processing is critical, such as handling requests in a server or
managing tasks in a multi-threaded environment.
Class Queue:
Def __init__(self):
Self.queue = []
Def dequeue(self):
If self.isEmpty():
Return None
Else:
Return self.queue.pop(0)
Def peek(self):
If self.isEmpty():
Return None
Else:
Return self.queue[0]
Def isEmpty(self):
Return len(self.queue) == 0
Class Queue:
Def __init__(self):
Self.queue = []
Def dequeue(self):
If self.isEmpty():
Return None
Else:
Return self.queue.pop(0)
Def peek(self):
If self.isEmpty():
Return None
Else:
Return self.queue[0]
Def isEmpty(self):
Return len(self.queue) == 0
Def __str__(self):
Return str(self.queue)
Def dequeue(self):
If self.isEmpty():
Return None
Else:
Return self.queue.pop(0)
Def peek(self):
If self.isEmpty():
Return None
Else:
Return self.queue[0]
Def isEmpty(self):
Return len(self.queue) == 0
Def __str__(self):
Return str(self.queue)
10. Implement a Circular Queue with a fixed size. Include methods for
checking if the queue is full and handling wrap-around indexing.
Class CircularQueue:
Def __init__(self, size):
Self.size = size
Self.queue = [None] * size
Self.front = 0
Self.rear = 0
Def isEmpty(self):
Return self.front == self.rear
Def isFull(self):
Return (self.rear + 1) % self.size == self.front
Def __str__(self):
Return str(self.queue)
Part 3. Recursion
Theory Question:
11. Define recursion. Explain how it differs from iteration.
Answer:
Recursion is a programming technique where a function calls itself
within its own definition. This creates a chain of function calls, each
working on a smaller version of the original problem. Iteration, on the
other hand, uses loops to repeatedly execute a block of code until a
certain condition is met.
Answer:
A base case in recursion is a condition that stops the recursive calls. It
defines the simplest version of the problem that can be solved directly
without further recursion. Base cases are crucial for preventing infinite
recursion and ensuring that the recursive function eventually
terminates.
Importance:
• Termination: Without a base case, a recursive function would call
itself indefinitely, leading to a stack overflow.
• Correctness: The base case ensures that the recursion reaches a
point where it can return a valid result, preventing incorrect or
undefined behavior.
Practical Coding Tasks:
13. Write recursive functions for the following:
Def factorial(n):
# Base case: factorial of 0 or 1 is 1
If n == 0 or n == 1:
Return 1
# Recursive case: n! = n * (n-1)!
Return n * factorial(n – 1)
# Example usage:
Print(f”Factorial of 5: {factorial(5)}”) # Output: 120
Answer:
Fibonacci Function;
Computes the nth Fibonacci number using recursion.
Base cases: returns 0 for ( n = 0 ) and 1 for ( n = 1 ).
Recursive case: ( \text{fibonacci}(n – 1) + \text{fibonacci}(n – 2) ).
def fibonacci(n):
# Base case: the first two Fibonacci numbers are 0 and 1
if n == 0:
return 0
elif n == 1:
return 1
# Recursive case: nth Fibonacci number is the sum of (n-1)th
and (n-2)th
return fibonacci(n - 1) + fibonacci(n - 2)
# Example usage:
print(f"Fibonacci number at position 5: {fibonacci(5)}") # Output:
5
# Example usage:
print(f"Reversed string of 'hello': {reverse_string('hello')}") #
Output: 'olleh'
Answer:
# Example usage:
If __name__ == “__main__”:
N = 3 # Number of disks
Tower_of_hanoi(n, ‘A’, ‘C’, ‘B’) # A is the source, C is the target, B is
the auxiliary