0% found this document useful (0 votes)
93 views12 pages

Doubly Linked List & Recursion

A doubly linked list contains an extra pointer called the previous pointer in addition to the next pointer and data found in a singly linked list. Recursion is when a function calls itself directly or indirectly. It can be used to solve problems like towers of Hanoi and tree traversals. The recursive function must have a base case, otherwise a stack overflow may occur. Backtracking is an algorithmic technique that tries to build a solution incrementally, removing partial solutions that don't satisfy constraints. It can solve problems like the N-queen problem by placing queens one by one and checking for clashes.

Uploaded by

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

Doubly Linked List & Recursion

A doubly linked list contains an extra pointer called the previous pointer in addition to the next pointer and data found in a singly linked list. Recursion is when a function calls itself directly or indirectly. It can be used to solve problems like towers of Hanoi and tree traversals. The recursive function must have a base case, otherwise a stack overflow may occur. Backtracking is an algorithmic technique that tries to build a solution incrementally, removing partial solutions that don't satisfy constraints. It can solve problems like the N-queen problem by placing queens one by one and checking for clashes.

Uploaded by

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

(Doubly Linked List & Recursion)

Doubly Linked list:


A Doubly Linked List (DLL) contains an extra pointer, typically called previous pointer,
together with next pointer and data which are there in singly linked list.

// A linked list node


class Node
{
public:
int data;
Node* next;
Node* prev;
};

Insertion in Doubly linked list:


• Add a node at the front:

• Add a node after a given node:



Add a node at the end:

Recursion:
The process in which a function calls itself directly or indirectly is called recursion and the corresponding
function is called as recursive function.
Using recursive algorithm, certain problems can be solved quite easily. Examples of such problems
are Towers of Hanoi (TOH), Inorder/Preorder/ HYPERLINK "https://www.geeksforgeeks.org/tree-
traversals-inorder-preorder-and-postorder/"Postorder HYPERLINK "https://www.geeksforgeeks.org/tree-
traversals-inorder-preorder-and-postorder/" Tree Traversals, DFS of Graph, etc.

In the recursive program, the solution to the base case is provided and the solution of the bigger
problem is expressed in terms of smaller problems.
int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}

In the above example, base case for n < = 1 is defined and larger value of number can be solved
by converting to smaller one till base case is reached.
Why Stack Overflow error occurs in recursion?
If the base case is not reached or not defined, then the stack overflow problem may arise. Let us
take an example to understand this.
int fact(int n)
{
// wrong base case (it may cause
// stack overflow).
if (n == 100)
return 1;

else
return n*fact(n-1);
}
If fact (10) is called, it will call fact (9), fact (8), and fact (7) and so on but the number will never
reach 100. So, the base case is not reached. If the memory is exhausted by these functions on the
stack, it will cause a stack overflow error.
What is the difference between direct and indirect recursion?
A function fun is called direct recursive if it calls the same function fun. A function fun is called
indirect recursive if it calls another function say fun_new and fun_new calls fun directly or
indirectly.
#include<iostream>
using namespace std;
//Direct Recursion
int factorial (int n)
{
if (n==1 || n==0)
return 1;
else
return n*factorial(n-1);
}

int main()
{
int f = factorial(5);
cout << f;
}

#include<iostream>
using namespace std;
//Indirect Recursion
int func1(int);
int func2(int);
int func1(int n)
{
if (n<=1)
return 1;
else
return func2(n);
}
int func2(int n)
{
return func1(n-1);
}
int main()
{
int f = func1(5); cout<<f;}
Here, recursion takes place in 2 steps, unlike direct recursion.

• First, func1 calls func2


• Then, func2 calls back the first calling function func1.

Tail Recursion:
A recursive function is tail recursive when recursive call is the last thing executed by the function. For
example the following C++ function print () is tail recursive.
/ An example of tail recursive function
void print(int n)
{ if (n < 0) return;
cout << " " << n;
// The last executed statement is recursive call
print(n-1);
}

The tail recursive functions considered better than non-tail recursive functions as tail-recursion can be
optimized by compiler. The idea used by compilers to optimize tail-recursive functions is simple, since
the recursive call is the last statement, there is nothing left to do in the current function, so saving the
current function’s stack frame is of no use.
Tail recursive is better than non-tail recursive as tail-recursion can be optimized by modern compilers.
Modern compiler basically do tail call elimination to optimize the tail recursive code.
If we take a closer look at above function, we can remove the last call with goto.
// Above code after tail call elimination
void print(int n)
{
start:
if (n < 0)
return;
cout << " " << n;
// Update parameters of recursive call
// and replace recursive call with goto
n = n-1
goto start;
}

Consider the following function to calculate factorial of n. It is a non-tail-recursive function. Although it


looks like a tail recursive at first look. If we take a closer look, we can see that the value returned by fact
(n-1) is used in fact (n), so the call to fact (n-1) is not the last thing done by fact (n).
#include<iostream>
using namespace std;
// A NON-tail-recursive function. The function is not tail
// recursive because the value returned by fact(n-1) is used in
// fact(n) and call to fact(n-1) is not the last thing done by fact(n)
unsigned int fact(unsigned int n)
{ if (n == 0) return 1;
return n*fact(n-1);
}
// Driver program to test above function
int main()
{
cout << fact(5);
return 0;
}

The above function can be written as a tail recursive function. The idea is to use one more
argument and accumulate the factorial value in second argument. When n reaches 0, return the
accumulated value.

#include<iostream>
using namespace std;
// A tail recursive function to calculate factorial
unsigned factTR(unsigned int n, unsigned int a)
{
if (n == 0) return a;

return factTR(n-1, n*a);


} // A wrapper over factTR
unsigned int fact(unsigned int n)
{
return factTR(n, 1);
} // Driver program to test above function
int main()
{
cout << fact(5);
return 0;
}
Disadvantages of recursive programming over iterative programming?

Note that both recursive and iterative programs have the same problem-solving powers, i.e.,
every recursive program can be written iteratively and vice versa is also true. The recursive
program has greater space requirements than iterative program as all functions will remain in the
stack until the base case is reached. It also has greater time requirements because of function
calls and returns overhead.

Advantages of recursive programming over iterative programming?

Recursion provides a clean and simple way to write code. Some problems are inherently
recursive like tree traversals, Tower of Hanoi, etc. For such problems, it is preferred to write
recursive code. We can write such codes also iteratively with the help of a stack data structure.
For example Inorder Tree Traversal without Recursion, Iterative Tower of Hanoi.

Back Tracking:
Backtracking is an algorithmic-technique for solving problems recursively by trying to build a solution
incrementally, one piece at a time, removing those solutions that fail to satisfy the constraints of the
problem at any point of time.
Backtracking can be defined as a general algorithmic technique that considers searching every possible
combination in order to solve a computational problem.
Pseudo Code for Backtracking:
• Recursive backtracking solution
void findSolutions(n, other params) :
if (found a solution) :
solutionsFound = solutionsFound + 1;
displaySolution();
if (solutionsFound >= solutionTarget) :
System.exit(0);
return
for (val = first to last) :
if (isValid(val, n)) :
applyValue(val, n);
findSolutions(n+1, other params);
removeValue(val, n);

• Finding whether a solution exists or not


boolean findSolutions(n, other params) :
if (found a solution) :
displaySolution();
return true;

for (val = first to last) :


if (isValid(val, n)) :
applyValue(val, n);
if (findSolutions(n+1, other params))
return true;
removeValue(val, n);
return false;

Let us try to solve a standard Backtracking problem, N-Queen Problem.


The N Queen is the problem of placing N chess queens on an N×N chessboard so that no two queens
attack each other. For example, following is a solution for 4 Queen Problem.

The expected output is a binary matrix which has 1s for the blocks where queens are placed. For example,
following is the output matrix for the above 4 queen solution.
{ 0, 1, 0, 0}
{ 0, 0, 0, 1}
{ 1, 0, 0, 0}
{ 0, 0, 1, 0}

Backtracking Algorithm: The idea is to place queens one by one in different columns, starting from the
leftmost column. When we place a queen in a column, we check for clashes with already placed queens.
In the current column, if we find a row for which there is no clash, we mark this row and column as part
of the solution. If we do not find such a row due to clashes then we backtrack and return false.

Naive Algorithm:
Generate all possible configurations of queens on board and print a configuration that satisfies the given
constraints.
while there are untried configurations
{
generate the next configuration
if queens don't attack in this configuration then
{
print this configuration;
}
}

Backtracking Algorithm
The idea is to place queens one by one in different columns, starting from the leftmost column. When we
place a queen in a column, we check for clashes with already placed queens. In the current column, if we
find a row for which there is no clash, we mark this row and column as part of the solution. If we do not
find such a row due to clashes then we backtrack and return false.
1) Start in the leftmost column
2) If all queens are placed
return true
3) Try all rows in the current column.
Do following for every tried row.
a) If the queen can be placed safely in this row
then mark this [row, column] as part of the
solution and recursively check if placing
queen here leads to a solution.
b) If placing the queen in [row, column] leads to
a solution then return true.
c) If placing queen doesn't lead to a solution then
unmark this [row, column] (Backtrack) and go to
step (a) to try other rows.
3) If all rows have been tried and nothing worked,
return false to trigger backtracking.

Exercises
Question – 01

Given a sorted doubly linked list of positive distinct elements, the task is to find pairs in doubly linked list
whose sum is equal to given value x, without using any extra space ?

Example: Input: head: 1 <-> 2 <-> 4 <-> 5 <-> 6 <-> 8 <-> 9

x=7

Output: (6, 1), (5, 2)

Question- 02

Write a recursive function to traverse the elements of a Singly Linked List.

Question-03

Write a recursive function to reverse the elements of Singly Linked List.

Question-04

Write a recursive function to find the digital root of a number.

number = 12345

digital root: 1+2+3+4+5= 15 ------> 1+5 =6

The digital root of 12345 is 6.

Question – 05

A palindrome is a string that reads the same both forward and backward. For example, the string
"madam" is a palindrome. Write a program that uses a recursive function to check whether a string is a
palindrome. Your program must contain a value-returning recursive function that returns true if the
string is a palindrome and false otherwise. Do not use any global variables; use the appropriate
parameters.
Question – 06

Write a recursive function, reverse_Digits that takes an integer as a parameter and returns the number
with the digits reversed. Also, write a program to test your function.

Question – 07

Make a program for N-Queen Problem in C++.


Question – 08

Rat in maze problem


A Maze is given as N*N binary matrix of blocks where source block is the upper left most block i.e., maze
[0][0] and destination block is lower rightmost block i.e., maze [N-1][N-1]. A rat starts from source and
has to reach the destination. The rat can move only in two directions: forward and down.
In the maze matrix, 0 means the block is a dead end and 1 means the block can be used in the path from
source to destination. Note that this is a simple version of the typical Maze problem. For example, a
more complex version can be that the rat can move in 4 directions and a more complex version can be
with a limited number of moves.

Gray blocks are dead ends (value = 0).

{1, 0, 0, 0}
{1, 1, 0, 1}
{0, 1, 0, 0}
{1, 1, 1, 1}

{1, 0, 0, 0}
{1, 1, 0, 0}
{0, 1, 0, 0}
{0, 1, 1, 1}

Naive Algorithm
The Naive Algorithm is to generate all paths from source to destination and one by one check if
the generated path satisfies the constraints.

while there are untried paths


{
generate the next path
if this path has all blocks as 1
{
print this path;
}
}

Backtracking Algorithm

If destination is reached
print the solution matrix
Else
a) Mark current cell in solution matrix as 1.
b) Move forward in the horizontal direction and recursively check if
this move leads to a solution.
c) If the move chosen in the above step doesn't lead to a solution
then move down and check if this move leads to a solution.
d) If none of the above solutions works then unmark this cell as 0
(BACKTRACK) and return false.

You might also like