ITSE205-DataStructures and Algorithms PDF
ITSE205-DataStructures and Algorithms PDF
Course Outcomes:
Reference Books:
int sumList(A, n)
{
1 int tot=0;
2 for(int i=0; i<n; i++)
3 tot= tot + A[i];
4 return tot;
}
Line 1 count for one unit.
Line 2 counts for :
Initialization cost is 1,
N+1 for all tests
N for all increments.
Line 3 Count for 2N
Line 4 Count for 1
Total cost: 1 + 1 + N+1 + N+ 2N +1
4N + 4
Thus, we say that this function is O (N).
The function f(n) =O(g(n)) (read as “f of n is big oh of g of n”) iff (if and
only if) there exist positive constants C and no such that f(n) <= c * g(n)
for all n, n>=no.
Big O notation is used in Computer Science to describe the performance or
complexity of an algorithm. Big O specifically describes the worst-case
scenario, and can be used to describe the execution time required or the space
used (e.g. in memory or on disk) by an algorithm.
O(N) : Linear
O(N) describes an algorithm whose performance will grow linearly and
in direct proportion to the size of the input data set.
O(N2) : Quadratic
O(N2) represents an algorithm whose performance is directly
proportional to the square of the size of the input data set. This is common
with algorithms that involve nested iterations over the data set. Deeper
nested iterations will result in O(N3), O(N4) etc.
O(2N) : Exponential
O(2N) denotes an algorithm whose growth doubles with each addition
to the input data set. The growth curve of an O(2 N) function is exponential -
starting off very shallow, then rising meteorically.
An example of an O(2N) function is the recursive calculation of Fibonacci
numbers:
int Fibonacci(int number)
{
if (number <= 1) return number;
return Fibonacci(number - 2) + Fibonacci(number - 1);
}
Example 1:
sum = 0;
for( i = 0; i < n; i++)
sum++;
The running time for the operation sum++ is a constant. The loop runs n
times, hence the complexity of the loop would be O(n)
Example 2:
sum = 0;
for( i = 0; i < n; i++)
for( j = 0; j < n; j++)
sum++;
Example 4:
Suppose that for some algorithm, the exact number of steps is
T(n)=5n2+27n+1005. When n is small, say 1 or 2, the constant 1005 seems
to be the dominant part of the function. However, as n gets larger, the n2
term becomes the most important. In fact, when n is really large, the other
two terms become insignificant in the role that they play in determining the
final result. Again, to approximate T(n) as n gets large, we can ignore the
other terms and focus on 5n2. In addition, the coefficient 5 becomes
insignificant as n gets large. We would say then that the function T(n) has an
order of magnitude f(n)=n2, or simply that it is O(n2).
This is almost the same definition as Big Oh, except that f(n) more than or
equal to cg(n) , this makes g(n) a lower bound function, instead of an upper
bound function. It describes the best that can happen for a given data size.
Ω(f(n)) ≥ { g(n) :
there exists c > 0 and n0 such that
g(n) ≤ c.f(n) for all n > n0}
θ(f(n)) = {g(n)
if and only if g(n) = Ο(f(n)) and g(n) = Ω(f(n))
for all n > n0}
The difference between the orders lies in which elements of an array are
contiguous in memory. In a row-major order, the consecutive elements of a
row reside next to each other, whereas the same holds true for consecutive
elements of a column in a column-major order
Let’s say we have one dimensional array having elements like this:
A1, A2, A3…….An
These elements are stored in my linear memory space as follows:
A1 A2 A3 A4 …..... An
One thing you need to remember is, no matter what type of array it is (1D or
multi-dimensional), they will be always stored linearly in the memory space
as above.
Let’s jump to the case of multi-dimensional arrays now. Imagine a 3×3
matrix like this:
Example:
Consider following example in which a two dimensional array consist of two
rows and four columns is stored sequentially in row major order as:
2.0 Stacks
Stack is a non-linear data structure that allows insertion and deletion to be
made at only at one end.
Stack is an ordered group of homogeneous items of elements. In stack,
elements are added to and removed from the top of the stack (the most
recently added items are at the top of the stack). The last element to be added
is the first to be removed (LIFO: Last In First Out list). It is also called as
FILO (First in Last Out).
Two basic operations of a stack are Push and Pop. Inserting an element
is called as Push. Deleting a topmost element is called as pop. Elements are
inserted and deleted from only one end.
Stacks can be implemented using:
Static Implementation (Using Arrays)
Dynamic Implementation (Using Linked List)
They are used in many applications, including the following.
1: Internet Web browsers store the addresses of recently visited sites on a
stack. Each time a user visits a new site, that site’s address is “pushed” onto
the stack of addresses. The browser then allows the user to “pop” back to
previously visited sites using the “back” button.
2: Text editors usually provide an “undo” mechanism that cancels recent
editing operations and reverts to former states of a document. This undo
operation can be accomplished by keeping text changes in a stack.
Example: The below table illustrates the stack operations and their effects.
Initially the stack is empty.
Operation Output Stack
contents
pop() Error –
(No elements in stack)
push(7) - [7]
push(2) - [7,2]
push(4) - [7,2,4]
pop() 4 [7,2]
Pop() 2 [7]
Push(3) - [7,3]
Push(1) - [7,3,1]
Empty() False [7,3,1]
Size() 3 [7,3,1]
Top() 1 [7,3,1]
Pop() 1 [7,3]
Top() 3 [7,3]
Pop() 3 [7]
Pop() 7 []
Empty() True []
Top() Error – []
(No elements in stack)
Pop() Error – []
(No elements in stack)
Push operation:
To convert any Infix expression into Postfix or Prefix expression we can use
the following procedure...
1. Find all the operators in the given Infix Expression.
2. Find the order of operators evaluated according to their Operator
precedence.
3. Convert each operator into required type of expression (Postfix or
Prefix) in the same order.
2. Tie Breaker
When an operand lies between two operators that have the same priority,
the operand associates with the operator on the left.
a + b - c
a * b / c / d
3. Delimiters
Sub expression within delimiters is treated as a single operand, independent
from the remainder of the expression.
(a + b) * (c – d) / (e – f)
Infix Postfix
(A+B)/D AB+D/
(A+B) / (D+E) AB+DE+/
(A-B/C +E) / (A+B) ABC/-E+AB+/
A*(B+C)/D ABC+*D/
( A + B - C ) * D – ( E + F ) AB + C – D * E F + -
2-3*4+5 234*-5+
(2-3)*(4+5) 23-45+*
2-(3*4+5) 234*5+-
3+4*5/6 345*6/+
3*2+25/5 32*25 5 /+
2.5 Queues
Data structure in which the elements are added at one end, called the rear,
and deleted from the other end, called the front. A queue is a First In First
Out(FIFO) data structure.
Insertions are made at one end (rear) whereas deletions are made at other
end (front). Insertion at rear end also called as enqueue. Deletion at front
end also referred as dequeue.
Fundamental Operations
Insert element into Queue
Delete element from Queue
Print Queue elements
Other Operations
Get front element
Get Rear element
ITSE205- Data Structures and Algorithms P a g e 33 | 115
Applications:
1. Servicing request of a single shared resource (printer, disk, cpu)
2. Asynchronous transmission of data (data not necessarily received at the
same rate as sent) between two processes (IO buffers).
Example: pipes, file IO, sockets
3. Call center phone system which will use queue to hold people in line until
service representative is free.
4. Computer systems must often provide a “holding area” for messages
between two processes, two programs, or even two systems. This holding
area is usually called a “buffer” and is often implemented as a queue
5. Round robin technique for processor scheduling.
Three major variations of Queues are:
1. Circular queue
2. Double ended queue (de-queue)
3. Priority queue
t=q[fr];
if(fr==rr)
fr=rr=-1;
else
fr++;
return t;
}
void que::display()
{
if(fr == rr)
cout<<endl<<"\t Queue is Empty ";
else
{
int k;
cout<<endl<<"\t Queue elements are "<<endl;
for(k=fr; k<=rr; k++)
cout<<"\t"<<q[k];
}
3.0 Introduction
A linked list is a data structure consisting of a group of nodes which together
represent a sequence.
In simplest form,
Each node is composed of a data and a reference (in other words, a
link) to the next node.
A linked list is a linear collection of data elements, called nodes, linked to one
another by means of pointers.
Each node is divided into two parts: the first part contains the information of
the element, and the second part contains the address of the next node in the
linked list. Address part of the node is also called linked or next field or Link
field.
Each element (we will call it a node) of a list is comprising of two items - the
data and a reference to the next node.
The last node has a reference to null.
The entry point (first node) into a linked list is called the Root or head or
front of the list.
Two basic Types of Linked Lists
1. Singly Linked List
2. Doubly Linked List
We can make as a Circular Linked List
It similar to
Insert at beginning
Delete from beginning of a linked list.
#include <iostream>
using namespace std;
class Node
{
public:
int data;
Node *addr;
}*top;
int main()
{
myClass *obj;
obj = new myClass();
top = NULL;
int ch, val;
do
{
cout<<endl<<"1. Push to stack ";
cout<<endl<<"2. Pop from stack ";
cout<<endl<<"3. Print stack elements ";
cout<<endl<<"4. exit";
class myClass
{
public:
myClass()
{
f = NULL;
r = NULL;
}
void insertQue(int p);
void delet();
void display();
};
int main()
{
myClass *ob;
ob = new myClass();
In a single linked list, every node has link to its next node in the sequence.
So, we can traverse from one node to other node only in one direction and
we cannot traverse back. We can solve this kind of problem by using double
linked list.
Double linked list can be defined as follows...
Double linked list is a sequence of elements in which every element
has links to its previous element and next element in the sequence.
class doubly
{
public :
void addend(int p);
void display();
};
if(start==NULL)
{
start=temp1;
temp1->next=NULL;
temp1->prev=NULL;
}
else
{
temp2=start;
while(temp2->next!=NULL)
temp2=temp2->next;
temp2->next=temp1;
temp1->prev=temp2;
temp1->next=NULL;
}
cout<<endl<< "node added at end " ;
}
int main()
{
start=NULL;
int ch, val;
doubly *d = new doubly();
do
{
cout<<endl<<"1. add node at end ";
cout<<endl<<"2. display node values ";
cout<<endl<<"3. exit ";
cout<<endl<<"select choice ";
cin>>ch;
switch(ch)
{
case 1:
cout<<endl<<"enter value to add ";
cin>>val;
d->addend(val);
break;
case 2:
d->display();
break;
}
}while(ch!=3);
cout<<endl<<"Doubly list tested ";
return 0;
}
#include <iostream>
using namespace std;
int linear(int *ar,int key, int siz)
{
for(int k=0; k<siz; k++)
if (ar[k]==key)
return (k+1);
return -1;
}
int main()
{
int *a,t,n,k,pos;
return 0;
}
while(low<=high)
{
if(ar[mid]==key)
return mid+1;
if(ar[mid]>key)
high = mid -1;
else
low = mid +1;
mid = (low+high)/2;
}
return -1;
}
int main()
{
int *a,t,n,k,pos;
return 0;
}
#include <iostream>
using namespace std;
class selection
{
int siz, *ar;
public:
void getInput();
void selectSort();
void display();
};
void selection :: getInput()
{
cout<<endl<<"enter array size ";
cin>>siz;
ar = new int[siz];
for(int k=0; k<siz; k++)
{
cout<<endl<<"Enter array Value ";
cin>>ar[k] ;
}
}
void selection ::display()
{
for(int k=0; k<siz; k++)
cout<<endl<<ar[k];
}
void selection ::selectSort()
{
class Bubble
{
int siz, *ar;
public:
void getInput();
void BubbleSort();
void display();
};
void Bubble :: getInput()
{
ITSE205- Data Structures and Algorithms P a g e 68 | 115
cout<<endl<<"enter array size ";
cin>>siz;
ar = new int[siz];
for(int k=0; k<siz; k++)
{
cout<<endl<<"Enter array Value ";
cin>>ar[k] ;
}
}
void Bubble ::display()
{
for(int k=0; k<siz; k++)
cout<<endl<<ar[k];
}
void Bubble ::BubbleSort()
{
for(int k=0; k<siz-1; k++)
{
for(int m=0; m<siz-1-k; m++)
{
if (ar[m]>ar[m+1])
{
int temp=ar[m];
ar[m]=ar[m+1];
ar[m+1]=temp;
}
}
}
}
int main()
{
Bubble *s = new Bubble();
s->getInput();
cout<<endl<<"Array values before sorting ";
s->display();
s->BubbleSort();
cout<<endl<<"Array values after sorting ";
s->display();
return 0;
}
The insertion sort, unlike the other sorts, passes through the array only
once. The insertion sort is commonly compared to organizing a handful of
playing cards. You pick up the random cards one at a time. As you pick up
each card, you insert it into its correct position in your hand of organized
cards.
Imagine that you are playing a card game. You're holding the cards in your
hand, and these cards are sorted. The dealer hands you exactly one new card.
You have to put it into the correct place so that the cards you're holding are
still sorted. In selection sort, each element that you add to the sorted subarray
is no smaller than the elements already in the sorted subarray. But in our card
example, the new card could be smaller than some of the cards you're already
holding, and so you go down the line, comparing the new card against each
card in your hand, until you find the place to put it. You insert the new card
in the right place, and once again, your hand holds fully sorted cards. Then
the dealer gives you another card, and you repeat the same procedure. Then
another card, and another card, and so on, until the dealer stops giving you
cards.
Example:
Consider the following array a with 7 elements as
35 , 20 , 40 , 100 , 3 , 10 , 15
Since ar[5] is less than ar[4],ar[3],ar[2] and ar[1] , therefore insert ar[5]
before ar[1] , giving the following array.
Since ar[6] is less than ar[5],ar[4],ar[3], and ar[2] , therefore insert ar[6]
before ar[2] giving the following sorted array.
#include <iostream>
using namespace std;
class Insertion
{
int siz, *ar;
public:
void getInput();
void InsertSort();
void display();
};
void Insertion :: getInput()
{
cout<<endl<<"enter array size ";
cin>>siz;
ar = new int[siz];
for(int k=0; k<siz; k++)
{
cout<<endl<<"Enter array Value ";
cin>>ar[k] ;
}
}
void Insertion ::display()
{
for(int k=0; k<siz; k++)
cout<<endl<<ar[k];
}
ITSE205- Data Structures and Algorithms P a g e 71 | 115
void Insertion ::InsertSort()
{
int k,temp,j;
for(k=1;k<=siz;k++)
{
temp=ar[k]; j=k-1;
while((temp<ar[j])&&(j>=0))
{
ar[j+1]=ar[j];
j=j-1 ;
}
ar[j+1]=temp;
}
}
int main()
{
Insertion *s = new Insertion();
s->getInput();
cout<<endl<<"Array values before sorting ";
s->display();
s->InsertSort();
cout<<endl<<"Array values after sorting ";
s->display();
return 0;
}
Array1 and Array2 are two sorted arrays. Now, let us merge these two arrays
into a single array.
Again divide
Again Divide
The base cases are subarrays of fewer than two elements, just as in merge
sort. In merge sort, you never see a subarray with no elements, but you can
in quicksort, if the other elements in the subarray are all less than the pivot
or all greater than the pivot.
Let's go back to the conquer step and walk through the recursive sorting of
the subarrays. After the first partition, we have subarrays of [5, 2, 3] and [12,
7, 14, 9, 10, 11], with 6 as the pivot.
To sort the subarray [5, 2, 3], we choose 3 as the pivot. After partitioning,
we have [2, 3, 5]. The subarray [2], to the left of the pivot, is a base case
when we recurse, as is the subarray [5], to the right of the pivot.
To sort the subarray [12, 7, 14, 9, 10, 11], we choose 11 as the pivot,
resulting in [7, 9, 10] to the left of the pivot and [14, 12] to the right. After
these subarrays are sorted, we have [7, 9, 10], followed by 11, followed by
[12, 14].
Here is how the entire quicksort algorithm unfolds. Array locations in blue
have been pivots in previous recursive calls, and so the values in these
locations will not be examined or moved again:
Example:
Consider the following 9 numbers:
493 812 715 710 195 437 582 340 385
We should start sorting by comparing and ordering the one's digits:
Notice that the numbers were added onto the list in the order that they were
found, which is why the numbers appear to be unsorted in each of the sublists
above. Now, we gather the sublists (in order from the 0 sublist to the 9 sublist)
into the main list again:
340 710 812 582 493 715 195 385 437
Now, the sublists are created again, this time based on the ten's digit:
4.9 Hashing
We develop different data structures to manage data in the most efficient
ways. Let’s look at the common data structures we have:
Arrays: Searching an array in the worst case is O(N). Cannot grow and shrink.
Linked Lists: This has also the search rime similar to an array O(N). can grow
and shrink. But searching is still the problem.
Binary search: This has a better search time of O(logN). The better compared
to above all.
We can improve the search time by using an approach called hashing. This
led us to the use of Hash Tables using hashing. In theory, insertion, deletion
and lookups can done in constant time that is O(1) using hashing.
Hashing is the process of indexing and retrieving element (data) in a data
structure to provide faster way of finding the element using the hash key.
Hash Table is basically an array which stores the data. But the data storing
process is done with the help of a function which is known as Hash Function.
Hashing is the process of mapping large amount of data item to a smaller
table with the help of a hashing function. The essence of hashing is to facilitate
the next level searching method when compared with the linear or binary
search. The advantage of this searching method is its efficiency to hand vast
amount of data items in a given collection (i.e. collection size).
In other words, hashing is a technique to convert a range of key values into
a range of indexes of an array. We're going to use modulo operator to get a
range of key values.
ITSE205- Data Structures and Algorithms P a g e 81 | 115
Hash Table
Hash Table is a data structure which stores data in an associative manner. In
a hash table, data is stored in an array format, where each data value has its
own unique index value. Access of data becomes very fast if we know the
index of the desired data.
Hash Table is the result of storing the hash data structure in a smaller table
which incorporates the hash function within itself. The Hash Function primarily
is responsible to map between the original data item and the smaller table
itself. Here the mapping takes place with the help of an output integer in a
consistent range produced when a given data item (any data type) is provided
for storage and this output integer range determines the location in the
smaller table for the data item. In terms of implementation, the hash table is
constructed with the help of an array and the indices of this array are
associated to the output integer range.
Following are basic primary operations of a hash table:
Search − search an element in a hash table.
Insert − insert an element in a hash table.
Delete − delete an element from a hash table.
The load factor of a hash table is the ratio of the number of keys in the table
to the size of the hash table.
Note:
The higher the load factor, the slower the retrieval.
With open addressing, the load factor cannot exceed 1.
With chaining, the load factor often exceeds 1.
There are eight hashing methods they are:
Direct method
Subtraction method
Modulo-division
Midsquare
Digit extraction
Rotation
Folding
Pseudorandom generation
Modulo-division Method
This is also known as division remainder method.
This algorithm works with any list size, but a list size that is a prime
number produces fewer collisions than other list sizes.
The formula to calculate the address is:
Address = key MODULO listsize + 1
Where listsize is the number of elements in the array.
Example:
Given data :
Keys are : 137456 214562 140145
137456 % 19 +1 = 11
214562 % 19 + 1 = 15
140145 % 19 + 1 = 2
Midsquare Method
In midsquare hashing the key is squared and the address is selected from the
middle of the square number.
Another numerical technique for constructing a hash function is called
the mid-square method. We first square the item, and then extract some
portion of the resulting digits. For example, if the item were 44, we would first
compute 442=1,936. By extracting the middle two digits, 93, and performing
the remainder step, we get 5 (93 % 11). Below table shows items under both
the remainder method and the mid-square method. You should verify that
you understand how these values were computed.
Collision Resolution
When two items hash to the same slot, we must have a systematic method
for placing the second item in the hash table. This process is called collision
resolution.
One method for resolving collisions looks into the hash table and tries to find
another open slot to hold the item that caused the collision. A simple way to
do this is to start at the original hash value position and then move in a
sequential manner through the slots until we encounter the first slot that is
empty. Note that we may need to go back to the first slot (circularly) to cover
the entire hash table. This collision resolution process is referred to as open
addressing in that it tries to find the next open slot or address in the hash
table. By systematically visiting each slot one at a time, we are performing an
open addressing technique called linear probing
4. Implement selection sort that sorts both characters and integers based on user choice.
5. Implement linear search technique that searches for both characters and integers
5.0 Introduction
A tree is a data structure for representing hierarchical data.
A tree can be defined as a finite set of one or more data items (or nodes)
such that:
1). There is a special node called the root of the tree.
2). Removing nodes (or data item) are partitioned into number of mutually
exclusive (i.e., disjoined) subsets each of which is itself a tree, are called
sub tree.
Representation of Tree
A
List Representation
• ( A ( B ( E ( K, L ), F ), C ( G ), D ( H ( M ), I, J ) ) )
• The root comes first, followed by a list of sub-trees B C D
E F G H I J
K L M
5.1 Why Tree?
Unlike Array and Linked List, which are linear data structures, tree is
hierarchical (or non-linear) data structure.
1) One reason to use trees might be because you want to store information
that naturally forms a hierarchy. For example, the file system on a computer:
2) If we organize keys in form of a tree (with some ordering e.g., BST), we
can search for a given key in moderate time (quicker than Linked List and
slower than arrays). Self-balancing search trees like AVL and Red-Black
trees guarantee an upper bound of O(Log n) for search.
3) We can insert/delete keys in moderate time (quicker than Arrays and
slower than Unordered Linked Lists). Self-balancing search
trees like AVL and Red-Black trees guarantee an upper bound of O(Log n) for
insertion/deletion.
4) Like Linked Lists and unlike Arrays, Pointer implementation of trees don’t
have an upper limit on number of nodes as nodes are linked using pointers.
Recursive definition:
• a binary tree is empty;
• or it consists of
• a node (the root) that stores an element
• a binary tree, called the left sub tree of T(T1)
• a binary tree, called the right sub tree of T (T2)
Examples:
A complete binary tree is one where all the levels are full with exception to
the last level and it is filled from left to right.
Searching Process:
Example:
Search for a node 45, from the below binary search tree.
1. Start at the root (25) node. 45 is greater than 25, search in the right
subtree.
2. 45 is less than 50, search in 50’s left subtree.
3. 45 is greater than 35, search in 35’s right subtree.
4. 45 is greater than 44, but 44 has no right subtree, so 45 is not in BST.
ITSE205- Data Structures and Algorithms P a g e 97 | 115
Deleting a node in a BST
We need to consider three different cases:
(1) Deleting a leaf node
(2) Deleting a node with only one child
(3) Deleting a node with two children
2. Heap Property: All nodes are either [greater than or equal to] or [less
than or equal to] each of its children. If the parent nodes are greater than
their children, heap is called a Max-Heap, and if the parent nodes are smaller
than their child nodes, heap is called Min-Heap.
In min-heap, first element is the smallest. So, when we want to sort a list in
ascending order, we create a min-heap from that list, and picks the first
element, as it is the smallest, then we repeat the process with remaining
elements.
In max-heap, the first element is the largest, hence it is used when we need
to sort a list in descending order.
Exercises:
1. Construct all possible binary trees with 4 nodes
2. Write preorder, inorder and postorder values for the following expression
trees.
6. 0 Introduction
Graphs are one of the unifying themes of computer science.
A graph G = (V;E) is defined by a set of vertices V , and a set of
edges E consisting of ordered or unordered pairs of vertices from V.
The pair is ordered because (u, v) is not same as (v, u) in case of directed
graph(di-graph). The pair of form (u, v) indicates that there is an edge from
vertex u to vertex v. The edges may contain weight/value/cost.
Graphs are used to represent many real life applications: Graphs are used to
represent networks. The networks may include paths in a city or telephone
network or circuit network. Graphs are also used in social networks like
LinkedIn, Facebook. For example, in Facebook, each person is represented
with a vertex (or node).
Applications
Electronic applications.
networks (roads, flights, communications)
scheduling (project planning)
Connecting with friends on social media, where each user is a vertex,
and when users connect they create an edge.
Using GPS/Google Maps/Yahoo Maps, to find a route based on shortest
route.
Google, to search for webpages, where pages on the internet are linked
to each other by hyperlinks; each page is a vertex and the link between
two pages is an edge.
Graph theoretical concepts are widely used in Operations Research.
Adjacency Matrix:
Adjacency Matrix is a 2D array of size V x V where V is the number of
vertices in a graph.
Let the 2D array be adj[][], a slot adj[i][j] = 1 indicates that there is an edge
from vertex i to vertex j. Adjacency matrix for undirected graph is always
symmetric. Adjacency Matrix is also used to represent weighted graphs. If
adj[i][j] = w, then there is an edge from vertex i to vertex j with weight w.
Adjacency List:
An array of linked lists is used. Size of the array is equal to number of vertices.
Let the array be array[]. An entry array[i] represents the linked list of vertices
adjacent to the ith vertex. This representation can also be used to represent
a weighted graph. The weights of edges can be stored in nodes of linked lists.
Following is adjacency list representation of the above graph.
Example:
depth-first-search
1. mark vertex as visited
2. for each adjacent vertex
3. if unvisited
1. do a depth-first search on adjacent vertex
dfs(G, u):
while u has an unvisited neighbour in G
v := an unvisited neighbour of u
Example:
Algorithm:
BFS(G,V)
Let Q be a Queue
Q.enqueue(V)
Label V as discovered
while Q is not empty
v <- Q.dequeue()
for all edges from v to w in G.adjacentEdges(v) do
if w is not labeled as discovered
Q.enqueue(w)
label w as discovered.
Applications
Shortest Path, the shortest path is the path with least number of edges.
With Breadth First, we always reach a vertex from given source using
minimum number of edges. Also, in case of unweighted graphs, any
spanning tree is Minimum Spanning Tree and we can use either Depth or
Breadth first traversal for finding a spanning tree.
Peer to Peer Networks. In Peer to Peer Networks like Bit Torrent, Breadth
First Search is used to find all neighbour nodes.
Crawlers in Search Engines: Crawlers build index using Breadth First. The
idea is to start from source page and follow all links from source and keep
doing same. Depth First Traversal can also be used for crawlers, but the
advantage with Breadth First Traversal is, depth or levels of built tree can
be limited.