0% found this document useful (0 votes)
25 views13 pages

Julie

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views13 pages

Julie

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 13

Name: Julie Mae R.

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.

Practical Coding Task:


3. Implement a Stack using a list with the following methods:
This requires Python code. Here’s a skeletal implementation. You’ll need to
fill in the details:
Class Stack:
Def __init__(self):
Self.items = []

Def push(self, item):


Self.items.append(item) # Add item to the end of the list

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

4. Add a method __str__() that displays the stack contents in a readable


format.
Class LimitedStack:
Def __init__(self, max_size):
Self.max_size = max_size
Self.stack = []

Def push(self, item):


If self.isFull():
Raise Exception(“Stack is full. Cannot push new item.”)
Self.stack.append(item)

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)

Print(stack) # Output: Stack is empty.

Stack.push(1)
Stack.push(2)
Print(stack) # Output: Stack contents: [1, 2]

Stack.push(3)
Print(stack) # Output: Stack contents: [1, 2, 3]

Print(stack.isFull()) # Output: True

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

5. return str(self.items)Implement a Stack with a size limit. Add a method


is full () that returns True if the stack is at its maximum size.
class LimitedStack:
def __init__(self, max_size):
self.max_size = max_size
self.stack = []

def push(self, item):


if self.isFull():
raise Exception("Stack is full. Cannot push new item.")
self.stack.append(item)

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)

print(stack.isFull()) # Output: True

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.

8. Create a Queue using a list with the following methods:


1. enqueue(): Adds an item to the end.
2. dequeue(): Removes an item from the front.
3. peek(): Returns the front item without removing it.
4. isEmpty(): Checks if the queue is empty.

Class Queue:
Def __init__(self):
Self.queue = []

Def enqueue(self, item):


Self.queue.append(item)

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

9.Implement a __str__() method to display the current queue.

Class Queue:
Def __init__(self):
Self.queue = []

Def enqueue(self, item):


Self.queue.append(item)

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)

9. Implement a __str__() method to display the current queue.


Class Queue:
Def __init__(self):
Self.queue = []

Def enqueue(self, item):


Self.queue.append(item)

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 enqueue(self, item):


If self.isFull():
Print(“Queue is full!”)
Return
Self.queue[self.rear] = item
Self.rear = (self.rear + 1) % self.size
Def dequeue(self):
If self.isEmpty():
Print(“Queue is empty!”)
Return None
Item = self.queue[self.front]
Self.front = (self.front + 1) % self.size
Return item
Def peek(self):
If self.isEmpty():
Print(“Queue is empty!”)
Return None
Return self.queue[self.front]

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.

12. Describe a base case in recursion and why it is important.

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:

1. Factorial Calculation: A function that returns the factorial of a given


number nnn .
Answer:
Factorial Function;
Computes the factorial of a number ( n ) using recursion.
Base case: returns 1 for ( n = 0 ) or ( n = 1 ).
Recursive case: ( n \times \text{factorial}(n – 1) ).

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

2. Fibonacci Sequence: A function that returns the nth(th) number in the


Fibonacci sequence.

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

3. String Reversal: A function that reverses a string using recursion.


Answer:
String Reversal Function;
Reverses a string using recursion. Base case: returns the string if it’s
empty or has one character. Recursive case: concatenates the last
character with the reversed substring (excluding the last character).
def reverse_string(s):
# Base case: if the string is empty or has one character,
return it
if len(s) <= 1:
return s
# Recursive case: reverse the substring and append the first
character at the end
return s[-1] + reverse_string(s[:-1])

# Example usage:
print(f"Reversed string of 'hello': {reverse_string('hello')}") #
Output: 'olleh'

Challenge Task (Bonus):


14. Implement the Tower of Hanoi problem with a recursive function. Display
each move needed to solve the problem for nnn disks.

Answer:

Def tower_of_hanoi(n, source, target, auxiliary):


# Base case: If there’s only one disk, move it directly from source to
target
If n == 1:
Print(f”Move disk 1 from {source} to {target}”)
Return
# Recursive case:
# Move n-1 disks from source to auxiliary, using target as auxiliary
Tower_of_hanoi(n – 1, source, auxiliary, target)

# Move the nth disk from source to target


Print(f”Move disk {n} from {source} to {target}”)

# Move the n-1 disks from auxiliary to target, using source as


auxiliary
Tower_of_hanoi(n – 1, auxiliary, target, source)

# 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

You might also like