0% found this document useful (0 votes)
10 views

FDS Unit 5 Notes

Uploaded by

harischaus
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

FDS Unit 5 Notes

Uploaded by

harischaus
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

1|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

Unit –V
Stack
1. Stack

1.1 Basic Concept

A Stack is a linear data structure that follows a particular order in which the operations are
performed. The order may be LIFO (Last In First Out) or FILO (First In Last
Out). LIFO implies that the element that is inserted last, comes out first and FILO implies
that the element that is inserted first, comes out last.

It behaves like a stack of plates, where the last plate added is the first one to be removed. Think
of it this way:

 Pushing an element onto the stack is like adding a new plate on top.

 Popping an element removes the top plate from the stack.

1.2 Stack Abstract Data Type (ADT)

 In Stack ADT Implementation instead of data being stored in each node, the pointer to
data is stored.

 The program allocates memory for the data and address is passed to the stack ADT.

 The head node and the data nodes are encapsulated in the ADT. The calling function
can only see the pointer to the stack.

 The stack head structure also contains a pointer to top and count of number of entries
currently in stack.
2|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

 push() – Insert an element at one end of the stack called top.

 pop() – Remove and return the element at the top of the stack, if it is not empty.

 peek() – Return the element at the top of the stack without removing it, if the stack is
not empty.

 size() – Return the number of elements in the stack.

 isEmpty() – Return true if the stack is empty, otherwise return false.

 isFull() – Return true if the stack is full, otherwise return false.

1.3 Representation of Stack Data Structure

Stack follows LIFO (Last In First Out) Principle so the element which is pushed last is popped
first.

1.4 Stack Operations

1. push(): Adds an element to the top of the stack.

Check if the stack is full (if implemented with a fixed size). If not, place the element at the top.

Example: Initial Stack: [10, 20, 30],

push(40) → Stack becomes [10, 20, 30, 40].


3|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

2. pop(): Removes the top element from the stack.

Check if the stack is empty. If not, remove and return the top element.

Example: Initial Stack: [10, 20, 30, 40]

pop() → Returns 40, Stack becomes [10, 20, 30].

3. peek() / top(): Retrieves the top element without removing it.

Check if the stack is empty. If not, return the top element.

Example: Stack: [10, 20, 30, 40]

peek() → Returns 40.


4|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

4. isEmpty(): Checks whether the stack is empty.

Returns true if the stack has no elements, otherwise false.

5. isFull(): Checks whether the stack has reached its maximum capacity (for fixed-size
stacks).

Returns true if the number of elements equals the stack's capacity.

C++ pseudocode for basic operations of stack:

// Define the stack


const int MAX_SIZE = 100; // Maximum size of the stack
int stack[MAX_SIZE]; // Array to hold stack elements
int top = -1; // Initialize the stack pointer

// Function to check if the stack is empty


bool isEmpty() {
return top == -1;
}
5|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

// Function to check if the stack is full


bool isFull() {
return top == MAX_SIZE - 1;
}

// Function to push an element onto the stack


void push(int element) {
if (isFull()) {
print("Stack Overflow! Cannot push element.");
} else {
top = top + 1;
stack[top] = element;
}
}

// Function to pop an element from the stack


int pop() {
if (isEmpty()) {
print("Stack Underflow! Cannot pop element.");
return -1; // Indicate failure
} else {
int poppedElement = stack[top];
top = top - 1;
return poppedElement;
}
}

// Function to get the top element of the stack


int peek() {
if (isEmpty()) {
print("Stack is empty! No top element.");
return -1; // Indicate failure
6|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

} else {
return stack[top];
}
}

// Example usage
void main() {
push(5); // Push 5 onto the stack
push(10); // Push 10 onto the stack
print(peek()); // Output: 10
print(pop()); // Output: 10
print(pop()); // Output: 5
print(isEmpty()); // Output: true
}
1.5 Stack Implementation Using an Array

To implement a stack using an array, initialize an array and treat its end as the stack’s top. The
basic operations for a stack include push, pop, and peek, with appropriate handling for empty
and full stack conditions.

Step-by-Step Approach:

1. Initialize an Array: An array is used to represent the stack. The end of the array represents
the top of the stack.

2. push Operation:

o Adds an item to the stack.

o Overflow Condition: Before adding an element, check if the stack is full (i.e., top ==
capacity - 1). If the stack is full, it leads to a stack overflow, and no further elements
can be added.

o If space is available, increment the top pointer and insert the element at the new top
position.

o Time Complexity: O(1)


7|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

3. pop Operation:

o Removes an item from the stack.

o Underflow Condition: Before popping, check if the stack is empty (i.e., top == -1).
If the stack is empty, it leads to a stack underflow.

o If the stack is not empty, store the element at top, decrement the top pointer, and return
the popped value.

o Time Complexity: O(1)

4. peek or top Operation:

o Returns the top element of the stack without removing it.

o If the stack is empty, return a message like "Stack is empty".

o If the stack is not empty, return the element at index top.

o Time Complexity: O(1)

5. isEmpty Operation:

o Checks if the stack is empty.

o If top == -1, the stack is empty, so return true; otherwise, return false.

o Time Complexity: O(1)

6. isFull Operation:

o Checks if the stack is full.

o If top == capacity - 1, the stack is full, so return true; otherwise, return false.

o Time Complexity: O(1)

Complexity Analysis:

 Time Complexity:

o push: O(1)

o pop: O(1)

o peek: O(1)
8|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

o is_empty: O(1)

o is_full: O(1)

 Auxiliary Space: O(n), where n is the number of items in the stack.

Advantages of Array Implementation:

 Simple and easy to implement.

 Memory is efficiently utilized since there are no pointers involved.

Disadvantages of Array Implementation:

 The size of the stack is fixed at compile time, meaning it cannot grow or shrink
dynamically during runtime. However, dynamic arrays like vectors in C++ or
ArrayLists in Java can overcome this limitation, allowing the stack to grow or shrink
as needed.

 Predefined stack size may lead to inefficient memory usage if the size is not fully
utilized.

Multiple Stacks:

Multiple stacks can be implemented using a single array. By dividing the array into sections,
each section can represent a separate stack. Each stack has its own top pointer to manage its
elements independently. This approach is useful when managing multiple stacks in programs
without allocating separate memory for each stack, making the memory usage more efficient.

Key Points of Multiple Stacks:

 Each stack operates independently within its designated section of the array.

 Stack operations (push, pop, peek) are handled separately for each stack using its own
top pointer.

 This method is memory efficient when multiple stacks are needed, as it minimizes
memory overhead by using a single array.

Example: C++ code to implement stack using array with overflow and underflow conditions.

Refer the example “basic operations of stack” above.

1.6 Expression Evaluation and Conversion


9|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

Stacks are widely used in expression evaluation and conversion because they follow the
LIFO (Last In, First Out) principle, which matches the nested structure of expressions and
operations.

Expression Types:

 Infix Expression: Operators are placed between operands.

Example: A + B * C

 Postfix Expression (Reverse Polish Notation): Operators follow operands.

Example: A B C * +

 Prefix Expression (Polish Notation): Operators precede operands.

Example: + A * B C

2. Expression Conversion:

a) Infix to Postfix Conversion:

 Use a stack to temporarily hold operators.

 Steps:

1. Scan the infix expression from left to right.

2. If an operand is encountered, add it directly to the output.

3. If an operator is encountered:

 Pop operators from the stack (with higher or equal precedence) and
append them to the output.

 Push the current operator onto the stack.

4. If a left parenthesis ( is encountered, push it onto the stack.

5. If a right parenthesis ) is encountered, pop and output operators until a left


parenthesis is encountered.

6. After scanning, pop remaining operators and append them to the output.

 Example:
10 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

o Input: A + B * C

o Output: A B C * +

b) Infix to Prefix Conversion:

 Reverse the infix expression, convert it to postfix, and then reverse the result.

3. Expression Evaluation:

Stacks are used to evaluate postfix and prefix expressions because they eliminate the need for
parentheses.

a) Postfix Evaluation:

 Scan the expression from left to right.

 Use a stack to store operands.

 Steps:

1. If an operand is encountered, push it onto the stack.

2. If an operator is encountered:

 Pop the top two elements from the stack.

 Apply the operator to them.

 Push the result back onto the stack.

3. The final result is at the top of the stack.

 Example:

o Input: 5 6 2 + *

o Process:

 Push 5, 6, and 2.

 Apply + to 6 and 2 → Push result 8.

 Apply * to 5 and 8 → Result = 40.

b) Prefix Evaluation:

 Scan the expression from right to left (reverse of postfix).


11 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

 Follow similar steps as postfix evaluation.

Applications:

 Compilers: Converting and evaluating expressions during code compilation.

 Calculators: Handling user-input expressions.

 Parsing Algorithms: For syntactic and semantic analysis.

1.7 Need for prefix and postfix expressions

1. Avoiding Parentheses:

o Infix [(A + B) * C]: Operators are between operands, requiring parentheses for
clarity and precedence.

o Prefix/Postfix: Operators come before (prefix) or after (postfix) operands, eliminating


parentheses. Example:

 Infix: (A + B) * C

 Prefix: * + A B C

 Postfix: A B + C *

2. Simplified Evaluation:

o Infix: Requires manual handling of precedence and parentheses.

o Postfix/Prefix: Evaluated using stacks. In postfix, operands are pushed and operators
apply when encountered. In prefix, read from right to left.

3. No Operator Precedence:

o Infix: Requires precedence rules (e.g., * before +).

o Postfix/Prefix: Operator position determines order, simplifying evaluation.

4. Simplified Parsing in Compilers:

o Infix: Complex parsing due to precedence and parentheses.

o Prefix/Postfix: Easier parsing using stacks, processed left-to-right (postfix) or right-to-


left (prefix).
12 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

5. Memory Efficiency:

o Stacks: Efficiently handle operands and operators sequentially, reducing memory usage
compared to infix evaluation.

1.8 Postfix expression evaluation

Postfix notation, also known as Reverse Polish Notation (RPN), is a way of writing
arithmetic expressions without the need for parentheses. In a postfix expression, the operator
follows the operands. The key advantage of postfix expressions is that they can be evaluated
directly from left to right using a stack, without needing to worry about operator precedence
or parentheses.

How Postfix Evaluation Works:

To evaluate a postfix expression, you follow these steps:

1. Initialize an empty stack.

2. Scan the postfix expression from left to right.

3. For each symbol in the expression:

o If the symbol is an operand (number), push it onto the stack.

o If the symbol is an operator, pop the required number of operands (usually two)
from the stack, perform the operation, and push the result back onto the stack.

4. At the end of the expression, the stack will contain only one element, which is the
result of the expression.

Step-by-Step Example:

Consider the infix expression exp = “a+b*c+d”

and the infix expression is scanned using the iterator i, which is initialized as i = 0.

1st Step: Here i = 0 and exp[i] = ‘a’ i.e., an operand. So add this in the postfix expression.
Therefore, postfix = “a”.
13 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

2nd Step: Here i = 1 and exp[i] = ‘+’ i.e., an operator. Push this into the stack. postfix
= “a” and stack = {+}.

3rd Step: Now i = 2 and exp[i] = ‘b’ i.e., an operand. So add this in the postfix
expression. postfix = “ab” and stack = {+}.

4th Step: Now i = 3 and exp[i] = ‘*’ i.e., an operator. Push this into the stack. postfix
= “ab” and stack = {+, *}.
14 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

5th Step: Now i = 4 and exp[i] = ‘c’ i.e., an operand. Add this in the postfix expression.
postfix = “abc” and stack = {+, *}.

6th Step: Now i = 5 and exp[i] = ‘+’ i.e., an operator. The topmost element of the stack
has higher precedence. So pop until the stack becomes empty or the top element has less
precedence. ‘*’ is popped and added in postfix. So postfix = “abc*” and stack = {+}.

Now top element is ‘+‘ that also doesn’t have less precedence. Pop it. postfix = “abc*+”.
15 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

Now stack is empty. So push ‘+’ in the stack. stack = {+}.

7th Step: Now i = 6 and exp[i] = ‘d’ i.e., an operand. Add this in the postfix expression.
postfix = “abc*+d”.

Final Step: Now no element is left. So, empty the stack and add it in the postfix expression.
postfix = “abc*+d+”.
16 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

Time Complexity:

 Time Complexity: O(n), where n is the number of elements in the postfix expression.

o Each operand is pushed once, and each operator pops operands and pushes the
result once, so the operations are linear in time.

Advantages of Postfix Notation:

 No Need for Parentheses: The position of operators and operands is sufficient to


determine the order of operations, eliminating the need for parentheses.

 Simplified Evaluation: Can be evaluated efficiently using a stack.

 No Operator Precedence: The order of operations is determined by the position of


operators in the expression, making it easier to evaluate than infix expressions.

Applications:

 Calculators: Postfix notation is used in some calculators, particularly scientific ones,


because it simplifies expression evaluation.

 Expression Parsers: Postfix expressions are used in compilers and interpreters for
evaluating arithmetic expressions.

Example: C++ Code to convert infix to postfix

// Function to define precedence of operators


int precedence(char op) {
if (op == '^') return 3;
if (op == '*' || op == '/') return 2;
if (op == '+' || op == '-') return 1;
return 0;
17 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

// Function to convert infix to postfix


string infixToPostfix(string infix) {
stack<char> st; // Stack to hold operators
string postfix = ""; // Resultant postfix expression

for (char ch : infix) {


if (isalnum(ch)) {
// If the character is an operand, add it to postfix
postfix += ch;
} else if (ch == '(') {
// Push '(' onto the stack
st.push(ch);
} else if (ch == ')') {
// Pop and append operators until '(' is found
while (!st.empty() && st.top() != '(') {
postfix += st.top();
st.pop();
}
st.pop(); // Remove '(' from the stack
} else {
// Operator encountered
while (!st.empty() && precedence(st.top()) >=
precedence(ch) && ch != '^') {
postfix += st.top();
st.pop();
}
st.push(ch); // Push current operator onto stack
}
}
18 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

// Pop and append remaining operators in the stack


while (!st.empty()) {
postfix += st.top();
st.pop();
}

return postfix;
}

// Example usage
void main() {
string infix = "A*(B+C)-D/E";
print(infixToPostfix(infix)); // Output: ABC+*DE/-
}
Q. Convert expression (A* B – (C + D * E) ˆ (F * G / H)) stepwise using above
rules. Where ˆ is - exponential operator.

To convert the infix expression A * B – (C + D * E) ^ (F * G / H) to postfix


expression, follow these steps using operator precedence and associativity rules.

Operator Precedence:

1. Parentheses () (highest precedence).

2. Exponentiation ^ (right-to-left associativity).

3. Multiplication *, Division / (left-to-right associativity).

4. Addition +, Subtraction - (left-to-right associativity).

Step-by-Step Conversion:

1. Original Expression: A * B – (C + D * E) ^ (F * G / H)

2. Handle Parentheses:

o Solve (C + D * E):

 D * E → DE*
19 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

 C + (DE*) → CDE*+

o Solve (F * G / H):

 F * G → FG*

 (FG*) / H → FG*H/

The expression becomes: A * B – (CDE*+) ^ (FG*H/)

3. Handle Exponentiation:

o (CDE*+) ^ (FG*H/) → CDE*+FG*H/^

The expression becomes: A * B – CDE*+FG*H/^

4. Handle Multiplication and Subtraction:

o A * B → AB*

o AB* – (CDE*+FG*H/^) → AB*CDE*+FG*H/^–

Final Postfix Expression:

AB*CDE*+FG*H/^–

Key Points:

 Parentheses are removed as postfix does not require them.

 Operators are placed after their operands, respecting precedence and associativity rules.

1.9 Linked Stack and Operations

To implement a stack using the singly linked list concept, all the singly linked list operations
should be performed based on Stack operations LIFO (Last In First Out) and with the help of
that knowledge, we are going to implement a stack using a singly linked list.

So, we need to follow a simple rule in the implementation of a stack which is last in first out
and all the operations can be performed with the help of a top variable.
20 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

In the stack Implementation, a stack contains a top pointer. which is the “head” of the stack
where pushing and popping items happens at the head of the list. The first node has a null in
the link field and second node-link has the first node address in the link field and so on and the
last node address is in the “top” pointer.

The main advantage of using a linked list over arrays is that it is possible to implement a stack
that can shrink or grow as much as needed. Using an array will put a restriction on the
maximum capacity of the array which can lead to stack overflow. Here each new node will be
dynamically allocated. so, overflow is not possible.

Stack Operations:
21 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

push(): Insert a new element into the stack i.e just insert a new element at the beginning of
the linked list.

pop(): Return the top element of the Stack i.e simply delete the first element from the linked
list.

peek(): Return the top element.

display(): Print all elements in Stack.

Push Operation:

 Initialize a node.

 Update the value of that node by data i.e. node->data = data

 Now link this node to the top of the linked list.

 And update top pointer to the current node.

Pop Operation:

 First check if there is any node present in the linked list or not, if not then return.

 Otherwise make pointer say temp to the top node and move forward the top node by
one step.

 Now free this temp node.

Peek Operation:

 Check if there is any node present or not, if not then return.

 Otherwise return the value of top node of the linked list

Display Operation:

 Take a temp node and initialize it with top pointer

 Now start traversing temp till it encounters NULL

 Simultaneously print the value of the temp node

Example: C++ Pseudo Code to implement stack as a linked list

// Define the structure of a node


22 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

struct Node {
int data; // Data part of the node
Node* next; // Pointer to the next node
};

// Define the stack


Node* top = nullptr; // Top of the stack

// Function to check if the stack is empty


bool isEmpty() {
return top == nullptr;
}

// Function to push an element onto the stack


void push(int element) {
// Check for overflow (optional based on memory availability)
Node* newNode = new Node();
if (newNode == nullptr) {
print("Stack Overflow! Cannot push element.");
return;
}

// Create and add the new node to the top


newNode->data = element;
newNode->next = top;
top = newNode;
}

// Function to pop an element from the stack


int pop() {
if (isEmpty()) {
print("Stack Underflow! Cannot pop element.");
23 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

return -1; // Indicate failure


}

// Remove the top node and update the top pointer


int poppedValue = top->data;
Node* temp = top;
top = top->next;
delete temp; // Free memory of the removed node
return poppedValue;
}

// Function to get the top element of the stack


int peek() {
if (isEmpty()) {
print("Stack is empty! No top element.");
return -1; // Indicate failure
}
return top->data;
}

// Example usage
void main() {
push(10); // Push 10 onto the stack
push(20); // Push 20 onto the stack
print(peek()); // Output: 20
print(pop()); // Output: 20
print(pop()); // Output: 10
print(pop()); // Output: Stack Underflow!
}

2. Recursion
24 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

2.1 Concept

Recursion is a programming technique where a function calls itself to solve smaller instances
of a problem. It divides a complex problem into smaller subproblems until a base condition is
met, which stops further recursive calls.

Key Components:

1. Base Case: The condition where the recursion stops to prevent infinite calls.

2. Recursive Case: The part where the function calls itself with smaller inputs.

Advantages:

 Simplifies code for problems like tree traversal, factorial, Fibonacci, etc.

Disadvantages:

 Can lead to stack overflow if the base case isn't defined or too many recursive calls are
made.

 May be less efficient due to repeated computations compared to iterative solutions.

2.2 Variants of Recursion

Recursion are mainly of two types depending on whether a function calls itself from within
itself or more than one function call one another mutually. The first one is called direct
recursion and another one is called indirect recursion. Thus, the two types of recursion are:

1. Direct Recursion: These can be further categorized in different types:

Tail Recursion: If a recursive function calls itself and that recursive call is the last statement
in the function then it’s known as Tail Recursion. After that call the recursive function
performs nothing. The function has to process or perform any operation at the time of calling
and it does nothing at returning time.

Example:

// Recursion function
void fun(int n) {
if (n > 0) {
cout << n << " ";
25 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

// Last statement in the function


fun(n - 1);
}
}

// Driver Code
int main() {
int x = 3;
fun(x);
return 0;
}
Let’s understand the example by tracing tree of recursive function. That is how the calls are
made and how the outputs are produced.

Head Recursion: If a recursive function is calling itself and that recursive call is the first
statement in the function then it’s known as Head Recursion. There’s no statement, no
operation before the call. The function doesn’t have to process or perform any operation at the
time of calling and all operations are done at returning time.

Example:

// Recursive function
26 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

void fun(int n)
{
if (n > 0) {

// First statement in the function


fun(n - 1);

cout << " "<< n;


}
}

// Driver code
int main()
{
int x = 3;
fun(x);
return 0;
}
Let’s understand the example by tracing tree of recursive function. That is how the calls are
made and how the outputs are produced.
27 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

Tree Recursion: To understand Tree Recursion let’s first understand Linear Recursion. If a
recursive function calls itself for one time, then it is known as Linear Recursion. Otherwise,
if a recursive function calls itself for more than one time then it’s known as Tree Recursion.
Example:

void fun(int n)
{
if (n > 0)
{
cout << " " << n;

// Calling once
fun(n - 1);

// Calling twice
fun(n - 1);
}
}

// Driver code
int main()
{
fun(3);
return 0;
}
Let’s understand the example by tracing tree of recursive function. That is how the calls are
made and how the outputs are produced.
28 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

2. Indirect Recursion: In this recursion, there may be more than one functions and they are
calling one another in a circular manner.

Example:

void funB(int n);

void funA(int n)
{
if (n > 0) {
cout <<" "<< n;

// fun(A) is calling fun(B)


funB(n - 1);
}
}

void funB(int n)
{
if (n > 1) {
cout <<" "<< n;

// fun(B) is calling fun(A)


funA(n / 2);
29 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

}
}

// Driver code
int main()
{
funA(20);
return 0;
}
Let’s understand the example by tracing tree of recursive function. That is how the calls are
made and how the outputs are produced.

2.3 Backtracking Algorithmic Strategy

Backtracking is a problem-solving strategy that builds a solution incrementally by exploring


all potential options and discarding solutions that do not satisfy the problem's constraints. It is
commonly used for problems involving permutations, combinations, or searching in structured
data like graphs or trees.

Steps in Backtracking:
30 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

1. Recursive Exploration: Solve the problem step by step recursively.

2. Base Case: Define the condition when a solution is complete.

3. Constraint Checking: Abandon a solution if it violates constraints.

4. Backtrack: Undo the last step (partial solution) and try another path.

Working of Backtracking:

As shown in the image, “IS” represents the Initial State where the recursion call starts to find
a valid solution. “C” represents different Checkpoints for recursive calls

“TN” represents the Terminal Nodes where no further recursive calls can be made, these nodes
act as base case of recursion and we determine whether the current solution is valid or not at
this state.

At each Checkpoint, our program makes some decisions and move to other checkpoints until
it reaches a terminal Node, after determining whether a solution is valid or not, the program
starts to revert back to the checkpoints and try to explore other paths.

For example, in the image TN1…TN5 are the terminal node where the solution is not
acceptable, while TN6 is the state where we found a valid solution.

The back arrows in the image show backtracking in actions, where we revert the changes made
by some checkpoint.

1.1 Use of Stack in Backtracking


31 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.

Backtracking inherently relies on a stack structure, either explicitly or through the function
call stack during recursion.

1. State Management: The stack keeps track of the current state of the problem at each
decision point (e.g., the current position, chosen options, etc.).

2. Undo Operations: When backtracking, the stack allows restoring the previous state by
popping elements, making it easy to explore alternative paths.

3. Implicit Stack in Recursion: In recursive backtracking, the call stack automatically


handles the storage and restoration of states.

4. Explicit Stack for Iterative Backtracking: In non-recursive backtracking, an explicit


stack is used to simulate recursion.

Example: Solving N-Queens Problem

Using a Stack:

 Each stack frame represents the current board state and decisions made (e.g., placing a
queen on a specific row).

 If placing a queen leads to a conflict, backtrack by popping the stack to revert to a


previous state and try another column.

Advantages of Backtracking:

 Efficient for solving constraint-based problems.

 Explores all possible solutions systematically.

 Can be optimized with techniques like pruning (e.g., branch-and-bound).

Applications of Backtracking:

 N-Queens Problem

 Sudoku Solver

 Graph Problems (e.g., Hamiltonian Path, Maze Solving)

 Permutations and Combinations

 String Matching (e.g., Regular Expression Matching)

You might also like