Sei sulla pagina 1di 37

 

August 2010
Master of Computer Application (MCA) – Semester 2
MC0068 – Data Structures using C – 4 Credits
(Book ID: B0701 & B0702) Assignment Set – 1

1. Describe the usage of pointers in functions with a suitable example.


Ans: Definition of pointer: “A pointer is a variable that can hold the address of the variables,
structures and functions that are used in the program. It contains only the memory location of
the variable rather than its containts”.

Pointers are used with followings:

1. Basic data type variable.

2. Array Subscript variable.

3. Function names.

4. Structure and Union names.

Advantages of Pointers:

1. Pointers are pointing to different data types and structures

2. Manipulation of data at different memory locations is easier.

3. To achieve a clarity and simplicity

4. More compact and efficient coding.

5. To return multiple value via functions.

6. Dynamic memory allocations.

Declaring a pointer variable

Pointers are declared similar to normal variables, but we must specify when we declare them
what they are going to point to it. We declare a pointer to point to an integer, then it cant be
used to point a floating-point value etc.

Pointer Operators:

To declare and refer a pointer variable, provides two special operators & and *.

 
 

1. Address Operator (ampersand) : &

2. Indirectional Operator : *

& ß (ampersand ) This Operator gives the memory Address of the variable.

* ß This Operator gives the value of the variable.

Example : if i = 100

Types of pointer variable declaration:

Example :

char *cptr; pointer to character type variables

int *iptr; *num pointer to integer type variables

float *fptr; pointer to float type variables

char *name[15] pointer to character array

Note: * symbol is part of the variables type.

Example : long int *x, *y;

float *avg, *ratio; etc.

Example: Program to assign the pointer values. (using operator & and *)
#include< iostream.h>
#include<conio.h>
main( )
{
int *x, y; /* xis pointer to integer variable */
clrscr ( );
y = 10;
x = &y; /* y value stored in pointer x.*/

 
 

printf( “Address of y = %d n ” , &y);


printf (“value of y = %d n” , y);
printf( “Address of y = %d n ” , x);
printf( “value of y = %d n ”, *x);
}

Output

Address of y = 65555

Value of y = 10

Address of y = 65555

Value of y = 10

Note:

i) 65555 is a address of &y it should be unsigned +ve.

ii) last statement value of y indirectly by using *x. *x-value at address stored by x.

Therefore * is called indirection operator when used in conjunction with pointers.

Example: Program to assign the values using operator *and &.


#include <iostream.h>
#include <conio.h>
main()
{
int x, y, *ipt; /* ipt is a pointer to integer variable */
clrscr ( );
x = 8;
ipt = & x; /*Address of x is stored in ipt */
y = *ipt; /* Content of pointer goes to y */
printf( “The value of y is = %d n “, y);
}

Output

The value of y is = 8

 
 

Note: Variable y is assigned to value at the address stored in ipt. since ipt contains
address of x, the value at address of x is 8, so * ipt is equal to 10.

Example: Program to use arithmetic operations with pointers.


#include <iostream.h>
#include <conio.h>
main ( )
{
int a, *ipt; /* ipt is a pointer to integer variable. */
int m, n, k;
clrscr( );
a = 150;
ipt = &a; /* address of a is assign to pointer */
m = (*ipt) + +;
n = (*ipt) – -;
k = (*ipt) + +;
print( “value of m = %d n” ,m);
print( “value of n = %d n ” , n);
print( “value of k = %d n ”,k);
}

Pointers used in function

It is mechanism by which pointers can be passed as arguments to the function. Thus, the
data items of the calling program can be accessed by the called program. No values is copied
when pointers are passed as arguments, as in the called by value method. Another important
point is that, if the values are changed in the function this will modify the original contents of
the actual parameters, this is not true in case of call by value method.

When the pointers are passed as an argument we must follow the following points.

a. In the calling program, the function is invoked with a function name and addresses of actual
parameters enclosed within the parenthesis.

Example :

< Function Name>(&var1,&var2,&var3………….&var n)

var à all are actual parameters.

 
 

b. In the called program parameter list, each & every formal parameter (pointers) must be
preceeded by an indirection operatore(*)

Example :

<data type> <function Name>(*v1,*v2,*v3…………*vn )

v –> all are formal parameters (pointers)

Example : Program to illustrate the call by reference method to interchange the value
of 2 integer variable.
main()
{
int num1,num2;
int interchange( int *n1, int *n2);
printf( “Enter any Two integer numbern”);
scanf(“%d %d “, &num1,&num2);
printf(“before interchanging n);
printf(“num1 = %d and num2 = %d”,num1,num2);
interchange(&num1,&num2);
printf(“after interchanging n);
printf(“num1 = %d and num2 = %d” num1, num2);
}
int interchange(int *n1, int *n2)
{
int temp;
temp=*n1;
* n1=*n2;
*n2=temp;
}

Pointers used in an Array

Pointers can be used with array to increase the efficiency of the execution of the program.
Pointers can be used with single dimensional or multi-dimensional arrar.

Pointers using in Single Dimensional Array.: The name of a array itself designates some
memory location & this location in memory is the address of the very first element of an array,
the address of the first element of array & num[0], where num is an array name.

 
 

Example :

Write a program to use an array of 5 elements & illustrate the relationship between
elements of an array & their address.
main()
{
int arrlist[5];
int *ptr,index,value=3;
ptr = arrlist;
for(index=0; index<5; index++)
{
*(ptr+index)=value++;
printf(“*(ptr+index)=%dtarrlist(index)=%d n”,*(ptr+index),arrlist[index]);
}
}

Output :

*(ptr+index)= 3 arrlist(index)= 3

*(ptr+index)= 4 arrlist(index)= 4

*(ptr+index)= 5 arrlist(index)= 5

*(ptr+index)= 6 arrlist(index)= 6

*(ptr+index)= 7 arrlist(index)= 7

Example :

Write a program to find the sum of 5 elements static in nature using pointers with
function.
main()
{
static int array[5]={200,400,600,800,1000};
int addnum(int *ptr); / * function protype */
int sum;
sum = addnum(array);
printf(“ Sum of all array elements = %d n”,sum);
}

 
 

int addnum(int *ptr)


{
int total = 0, index;
for(index=0; index<5; index++)
total +=(ptr+index);
return(total);
}

2. Demonstrate with your own programming example the usage of structures within an
array.
Ans: Each and every structure must be defined or declared before it appears or using in program.

Syntax:
struct <structurer name>
{
<type1> <field/data1>
<type2> <field/data2>
<type3> <field/data3>
………………………
……………………….
<type n> <field/data n>
};

Example :
Struct student
{
int rollno;
char name[30];
char address[30];
char city[15];
float marks;
};

Initialization of structure: Initializing a structure description of structure member is similar to


initializing static type declaration.

Example: structure student={122,”Sakshi”, “Arvind Appt.”,”Manipal”,560};

Embedded Structure declaration: [Nested]

 
 

It means that, Structure within the another structure is called an embedded structure.

These type of structure declared mainly in two ways that are:

a) Structure may completely defined within the another structure.

b) There may be a separate structure, the embedded structure declared first and the other
structure declared next.

1. sruct emp 2. sruct emp

{ {

int empno; int empno;

char emp_name[30]; char emp_name[30];

int salary; int salary;

struct date_join struct date_join

{ };

int day; struct date_join

int month; {

int year; int day;

}; int month;

}; int year;

};

Processing of Structure: The process of structure is mainly concerned with the accessing
structure member. Each member of a structure is accessed with .(dot) operator to access a
particular member of the structure, the dot operator must be placed between the name of the
structure & the name of the structure member.

Examples:

emp.emp_name, emp.empno , emp.salary etc.

Write a program to accept the student details as roll_no, name, city, marks using
structure and print the details.

 
 

struct std
{
int rollno;
char name[30];
char city[15];
int marks;
} st; /* structure definition */ /* st -> is the structure point */
main() /*main program */
{
printf(“enter the Roll no n”);
scanf(“%d “, &st.rollno);
printf(“enter the Name n”);
scanf(“%s “, st.name);
printf(“enter the city n”);
scanf(“%d “, st.city);
printf(“enter the Marks n”);
scanf(“%d “, &st.marks);

/* printing details */

printf(“Roll Number : %d”,st.rollno);


printf(“Name : %s”, st.name);
printf(“City : %s”, st.city);
printf(“Marks : %d”,st.marks)
}

Structure used with an Array: However we know that different type of data sets cannot be
stored an array, So, to overcome this disadvantage structure can be stored along with its
members in array structure.

Example: Storing 10 students details structure in an array.

 
 

3. Explain the theory of non linear data structures.


Ans: Non Linear Data Structures:

Trees: we consider one of the most Important non-linear Information structures- trees. A tree
Is often used to represent a hierarchy. This is because the relationships between the Items In
the hierarchy suggest the branches of a botanical tree.

For example, a tree-like organization charts often used to represent the lines of responsibility
in a business as shown in Figure. The president of the company is shown at the top of the
tree and the vice-presidents are indicated below her. Under the vice-presidents we find the
managers and below the managers the rest of the clerks. Each clerk reports to a manager.
Each manager reports to a vice-president, and each vice-president reports to the president.

Representing a hierarchy using a tree

 
 

It just takes a little imagination to see the tree in Figure. Of course. The tree is upside-down.
However, this is the usual way the data structure is drawn. The president is called the root of
the tree and the clerks are the leaves.

A tree is extremely useful for certain kinds of computations. For example. Suppose we wish to
determine the total salaries paid to employees by division or by department. The total of the
salaries in division A can be found by computing the sum of the salaries paid in departments
Al and A2 plus the salary of the vice-president of division A. Similarly. The total of the salaries
paid in department Al is the sum of the salaries of the manager of department Al and of the
two clerks below her.

Clearly, in order to compute all the totals. It is necessary to consider the salary of every
employee. Therefore, an implementation of this computation must visit all the employees in
the tree. An algorithm that systematically visits all the items in a tree is called a tree traversal.

In the same chapter we consider several different kinds of trees as well as several different
tree traversal algorithms. In addition. We show how trees can be used to represent arithmetic
expressions and how we can evaluate an arithmetic expression by doing a tree traversal. The
following is a mathematical definition of a tree:

Definition (Tree) A tree T is a finite. Non-empty set of nodes ,

T = {r} U TI, U T2 U …U Tn with the following properties:

3. A designated node of the set, r, is called the root of the tree: and

4. The remaining nodes are partitioned into n≥ O subsets T, T. …Tn each of which is a tree
for convenience, we shall use the notation T= {r. T, T, …T} denote the tree T.

Notice that Definition is recursive-a tree is defined in terms of itself! Fortunately, we do not
have a problem with infinite recursion because every tree has a finite number of nodes and
because in the base case a tree has n=0 subtrees.

It follows from Definition that the minimal tree is a tree comprised of a single root node. For
example Ta = {A}.

Finally. The following Tb = {B, {C}} is also a tree

Ta = {D, {E. {F}}, {G.{H,II}}, {J, {K}. {L}}, {M}}}

How do Ta Tb. & Tc resemble their arboreal namesake? The similarity becomes apparent
when we consider the graphical representation of these trees shown in Figure. To draw such
a pictorial representation of a tree, T = {r. T1 ,T2, …Tn, beside each other below the root.
Finally, lines are drawn from rto the roots of each of the subtrees. T1T2…….Tn

 
 

Examples of trees

Of course, trees drawn in this fashion are upside down. Nevertheless, this is the conventional
way in which tree data structures are drawn. In fact, it is understood that when we speak of
“up” and “down,” we do so with respect to this pictorial representation. For example, when we
move from a root to a subtree, we will say that we are moving down the tree.

The inverted pictorial representation of trees is probably due to the way that genealogical
lineal charts are drawn. A lineal chart is a family tree that shows the descendants of some
person. And it is from genealogy that much of the terminology associated with tree data
structures is taken.

Figure shows one representation of the tree Tc defined in Equation. In this case, the tree is
represented as a set of nested regions in the plane. In fact, what we have is a Venn diagram
which corresponds to the view that a tree is a set of sets.

An alternate graphical representation for trees

Binary Tree: Used to implement lists whose elements have a natural order (e.g. numbers)
and either (a) the application would like the list kept in this order or (b) the order of elements
is irrelevant to the application (e.g. this list is implementing a set).

Each element in a binary tree is stored in a "node" class (or struct). Each node contains
pointers to a left child node and a right child node. In some implementations, it may also
contain a pointer to the parent node. A tree may also have an object of a second "tree" class
(or struct) which as a header for the tree. The "tree" object contains a pointer to the root of the
tree (the node with no parent) and whatever other information the programmer wants to
squirrel away in it (e.g. number of nodes currently in the tree).

In a binary tree, elements are kept sorted in left to right order across the tree. That is if N is a
node, then the value stored in N must be larger than the value stored in left-child(N) and less
than the value stored in right-child(N). Variant trees may have the opposite order (smaller

 
 

values to the right rather than to the left) or may allow two different nodes to contain equal
values.

Hash Tables: A very common paradigm in data processing involves storing information in a
table and then later retrieving the information stored there. For example, consider a database
of driver’s license records. The database contains one record for each driver’s license issued.
Given a driver’s license number. we can look up the information associated with that number.
Similar operations are done by the C compiler. The compiler uses a symbol table to keep
track of the user-defined symbols in a Java program. As it compiles a program, the compiler
inserts an entry in the symbol table every time a new symbol is declared. In addition, every
time a symbol is used, the compiler looks up the attributes associated with that symbol to see
that it is being used correctly.

Typically the database comprises a collection of key-and-value pairs. Information is retrieved


from the database by searching for a given key. In the case of the driver’~ license database,
the key is the driver’s license number and in the case of the symbol table, the key is the name
of the symbol.

In general, an application may perform a large number of insertion and/ or look-up operations.
Occasionally it is also necessary to remove items from the database. Because a large
number of operations will be done we want to do them as quickly as possible.

Hash tables are a very practical way to maintain a dictionary. As with bucket sort, it assumes
we know that the distribution of keys is fairly well-behaved.

Once you have its index. A hash function is a mathematical function which maps keys to
integers.

In bucket sort, our hash function mapped the key to a bucket based on the first letters of the
key. "Collisions" were the set of keys mapped to the same bucket. If the keys were uniformly
distributed. then each bucket contains very few keys!

The resulting short lists were easily sorted, and could just as easily be searched

We examine data structures which are designed specifically with the objective of providing
efficient insertion and find operations. In order to meet the design objective certain
concessions are made. Specifically, we do not require that there be any specific ordering of
the items in the container. In addition, while we still require the ability to remove items from
the container, it is not our primary objective to make removal as efficient as the insertion and
find operations.

 
 

Ideally we would’ build a data structure for which both the insertion and find operations are
0(1) in the worst case. However, this kind of performance can only be achieved with complete
a priori knowledge. We need to know beforehand specifically which items are to be inserted
into the container. Unfortunately, we do not have this information in the general case. So, if
we cannot guarantee 0(1) performance in the worst case, then we make it our design
objective to achieve 0(1) performance in the average case.

The constant time performance objective immediately leads us to the following conclusion:
Our implementation must be based in some way Kh element of an array in constant time,
whereas the same operation in a linked list takes O{k) time.

In the previous section, we consider two searchable containers-the ordered list and the sorted
list. In the case of an ordered list, the cost of an insertion is 0(1) and the cost of the find
operation is O(n). For a sorted list the cost of insertion is O(n) and the cost of the find
operation is O(log n) for the array implementation.

Clearly, neither the ordered list nor the sorted list meets our performance objectives. The
essential problem is that a search, either linear or binary, is always necessary. In the ordered
list, the find operation uses a linear search to locate the item. In the sorted list, a binary
search can be used to locate the item because the data is sorted. However, in order to keep
the data sorted, insertion becomes O(n).

In order to meet the performance objective of constant time insert and find operations. we
need a way to do them without performing a search. That is, given an item x, we need to be
able to determine directly from x the array position where it is to be stored.

Hash Functions: It is the job of the hash function to map keys to integers. A good hash
function:

1. Is cheap to evaluate

2. Tends to use all positions from O…M with uniform frequency.

3. Tends to put similar keys in different parts of the tables (Remember the Shifletts!!)

The first step is usually to map the key to a big integer, for example

 
 

k=wth

h = S 1284 x char (key[I])

1=0

This last number must be reduced to an integer whose size is between 1 and the size of our
hash table. One way is by h(k) = k mod M where M is best a large prime not too close to 2i -1,
which would just mask off the high bits. This works on the same principle as a roulette wheel!

 
 

4. Write a program in C showing the implementation of stack operations using structures.


Ans: C program to simulate the stack operations using structures

#include<stdio.h>

#include <process.h>

#define ST ACK_SIZE 5

struct stack

int items[STACK_SIZE];

int top;

};

typedef struct stack STACK;

int is_empty(STACK *s)

if (s->top = = -1) return -1; /* Stack empty */

return 0; /* Stack is not empty */

int is_full(STACK *s)

if ( s->top == STACK _ SlZE -1) return 1; /* Stack is full */

return 0; /* Stack is not full */

void push(int item, STACK *s)

if ( is_full(s) )

printf("Stack Overflown");

return;

 
 

s->top+ +; /* Update top to point to next item */

s->items[s->top] = item; /* Insert the item into the stack*/

int pop(ST ACK *s)

int item;

if ( is_ empty(s) )

printf("Stack Underflown");

return 0;

item = s->items[s->top]; /* Access the top element */

s->top–; /* Update the pointer to point to previous item

return item; /* Return the top item to the calling function */

void display(STACK s)

int i;

if ( is_empty(&s) )

printf("Stack is emptyn");

return 0;

printf("The contents of the stackn");

for (i = 0; i<= s.top; i + +)

 
 

printf("%dn ",s.items[i];

void main()

int item; /* Item to be inserted */

int choice; /* Push, pop, display or quit */

STACK s; /* To store items */

s.top = -1; /* Stack is empty initially */

for (;;)

printf("1: Push 2: Popn");

printf("3: Disply 4: Exitn");

printf("Enter the choicen");

scanf("%d",&choice);

switch(choice)

case 1:

printf("Enter the item to be inserted\n");

scanf("%d",& item);

push(item, & s);

break;

case 2:

item = pop(&s);

if (item != 0)

printf("Item deleted = %dn", item);

 
 

break;

case 3:

display(s);

break;

default: exit(0);

}
5. Describe the theory and applications of Double Ended Queues (Deque) and circular
queues.
Ans: The double-ended queue also called Deque is discussed here. Deque is a special type of
data structure in which insertions and deletions will be done either at the front end or at the
rear end of the queue. The operations that can be performed on deques are

„ Insert an item from front end

„ Insert an item from rear end

„ Delete an item from front end

„ Delete an item from rear end

„ Display the contents of queue

The three operations insert rear, delete front and display and the associated operations to
check for an underflow and overflow of queue have already been discussed in ‘ordinary
queue’. In this section, other two operations i.e., insert an item at the front end and delete an
item from the rear end are discussed.

 
 

To insert an item at front end of queue

a) Insert at the front end

Consider queue shown in above fig (a). Observe that, the front end identified by f is 0 and
rear end identified by r is -1. Here, an item can be inserted first by incrementing r by 1 and
then insert an item. If the front pointer f is not equal to 0 as shown in above fig. (b), an item
can be inserted by decrementing the front pointer .f by 1 and then inserting an item at that
position. Except for these conditions, it is not possible to insert an item at the front end. For
example, consider the queue shown in above figure (c). Here, an item 10 is already present in
the first position identified by f and so, it is not possible to insert an item. The complete C
function to insert an item is shown in below example. Example 1: Function to insert an item at
the front end

b) Delete from the rear end

To delete an element from the rear end, first access the rear element and then decrement
rear end identified by r. As an element is deleted, queue may become empty. If the queue is
empty, reset the front pointer f to 0 and rear pointer r to -1 as has been done in an ordinary
queue. We delete an element only if queue is not empty. The complete C function to delete
an item from the rear end is shown in below example.

Example: C program to implement double-ended queue

#include <stdio.h>

#include <process.h>

#define QUEUE_SIZE 5

int qfull(int count)

return ( count = = QUEUE_SIZE ) ? I: 0; /* return true if Q is full; otherwise false */

 
 

int qempty (int count)

return ( count == 0 ) ? 1: 0; /* return true if Q is empty; otherwise false */

void insert_front(int item, int q[ ], int *f, int *r)

if( *f= = 0 && *r = = -1)

q[++(*r)] = item;

else if ( *f ! = 0)

q[--(*f)]=item;

else

printf("Front insertion not possiblen");

void insert_rear (int item, int q[], int *r)

if ( qfull(*r) ) /* Is queue full ? */

printf("Queue overflown");

return;

/* Queue is not full */

q[+ +(*r)] = item; /* Update rear pointer and insert a item */

void delete_front(int q[], int *f, int *r)

if ( qempty(*f, *r) )

printf("Queue underflown");

return;

 
 

printf("The element deleted is %dn", q[(*f)+ +]);

if(*f> *r)

*f=O,*r=-l;

void delete_rear(int q[],int *f, int *r)

if ( qempty(*f,*r) )

printf("Queue underflown");

return;

printf("The element deleted is %dn".q[(*r)--]);

if (*f > *r)

*f = 0, *r = -1 ;

void display(int q[], int f, int r)

int i;

if ( qempty(f,r) )

printf("Queue is emptyn");

return;

printf("Contents of queue isn");

for(i=f;i<= r; i+ +)

 
 

printf(" %dn",q[i]);

void main()

int choice,item,f,r,q [10];

f=0; r = -1;

for (;;)

printf(" 1:Insert_front 2:lnsert_rearn");

printf("3: Delete_front 4: Delete_rearn" );

printf("5: Display 6:Exitn");

printf("Enter the choicen");

scanf("%d" ,&choice );

switch ( choice )

case 1:

printf("Enter the item to be insertedn");

scanf("%d",& item);

insert_ front(item, q, &f, &r);

break;

case 2:

printf("Enter the item to be insertedn");

scanf("%d",& item);

insert_rear(item, q, &r);

break; case 3:

delete _front(q, &f, &r);

break;

case 4:

delete_rear(q, &f, &r);

break;

 
 

cases 5:

display(q, f, r);

break;

default: .

exit(0);

Circular Queue: In an ordinary queue, as an item is inserted, the rear end identified by r is
incremented by 1. Once r reaches QUEUE_SIZE -1, we say queue is full. Note that even if
some elements are deleted from queue, because the rear end identified by r is still equal to
QUEUE_SIZE-l, item cannot be inserted. Pot details refer in Disadvantage of ordinary queue.
This disadvantage is overcome using circular queue. The pictorial representation of a circular
queue and its equivalent representation using an array are given side by side in below fig in
next page.

Assume that circular queue contains only one item as shown in below fig. (a). In this case, the
rear end identified by r is 0 and front end identified by f is also 0. Since rear end is
incremented while insertion, just before inserting the first item, the value ‘of r should be -1
(Note: An item is inserted only at the rear end and so, only r is incremented by 1 and not f) so
that after insertion, f and r points to an item 10. So, naturally, the initial values of f and r
should be 0 and -1.

The configuration shown in below fig. (b) is obtained after inserting 20 and 30. To insert an
item, the rear pointer r has to be incremented first. For this, any of the two statements shown
below can be used.

r = r + l or r = (r + 1) %QUEUE_SIZE

Both statements will increment r by 1. But, we prefer the second statement. We see why this
method is preferred instead of a simple statement

r= r+1

 
 

Pictorial representation of a circular queue

 
 

The queue shown in fig.(c) is obtained after inserting 40 and 50. Note that at this point, the
queue is full. Suppose we delete two items 10 and 20 one after the other. The resulting queue
is shown in fig. (d). Now try to insert an item 60. If the statement

r= r+1

is used to increment rear pointer, the value of r will be 5. But because this is a circular queue r
should point to 0. This can be achieved using the statement

r = (r + 1) % QUEUE_SIZE

After execution of the above statement r will be 0. If this approach is used to check for
overflow or underflow, we use a variable count that contains the number of items in the queue
at any instant. So as an item is inserted, increment count by 1 and as an item is deleted
decrement count by 1. By this it is ensured that at any point of time, count always contains the
total number of elements in the queue. So, if queue is empty, count will be 0 and if queue is
full, count will be QUEUE_SIZE. Thus we can easily implement the two functions qfull( ) and
qempty( ) and these functions are shown in below examples 1 and 2 respectively.

Example 1: Function to check queue overflow

int qfull(int count)

return ( count = = QUEUE_SIZE ) ? I: 0; /* return true if Q is full; otherwise false */

Example 2: Function to check queue underflow

int qempty (int count)

return ( count == 0 ) ? 1: 0; /* return true if Q is empty; otherwise false */

Example 3: Function to insert an item at the rear end

void insert_rear(int item, int q[], int *r, int *count)

if ( qfull(*count) )

 
 

printf("Overflow of queuen");

return;

*r = (*r + 1) % QUEUE_SIZE ; /* increment rear pointer */

q[*r] = item; /* Insert the item */

*count += 1; /* update the counter */

a) To insert from the front end

Note that insertion is done only when queue is not full. So, if queue is not full, to insert an
item, increment rear end identified by r by 1 and then insert. Also, as an item is inserted
update the value of count by 1. The variable count is used to check for overflow or underflow.
The function to insert an item into the queue is shown in the above example 3.

b) To delete from front end

As in an ordinary queue, the element at the front end of the queue has to be deleted. ‘So,
access an item which is at the front end by specifying q[f] and update the front end identified f
to point to next front item. The front end identified by f can be incremented using the following
statement

f= (f+ 1) % QUEUE_SIZE;

As an item is deleted from a queue, the count should be decremented by 1. The complete C,
function to delete an element from the circular queue is shown in below example 4.

Example 4: Function to delete an item from the front end of queue

void delete_front(int q[], int *f, int *count)

if ( qempty(*count) )

printf("Underflow of queuen");

return;

 
 

printf("The deleted element is %dn",q[*f]); /* Access the item */

*f = (*f + 1) % QUEUE_SIZE; /* Point to next first item */

*count -= 1; /* update counter */

c) To display queue contents

If queue is not empty, elements in the queue should be displayed from the front end identified
by f to the rear end identified by r. The total number of items to be displayed can be obtained
from the variable count. This can be achieved by initializing the index variable i to the front
end identified by f and incrementing i each time using the statement

i= (i + 1)% QUEUE_SIZE;

count number of times. As the index variable i point to the each item in the queue, the queue
contents can be accessed and displayed one by one. The function to display the contents of
circular queue is shown in below example 5.

Example 5: Function to display the contents of the queue

void display(int q[], int f, int count)


{
int i, j ;
if ( qempty(count) )
{
printf("Q is emptyn");
return;
}
printf(“Contents of queue isn”);
i= f; /* Point to front of queue */
for ( j = 1; j <= count; j++)
{
printf(%d “,q[i]); /* access, the item */
i= (i + 1) % QUEUE_SIZE; /* Point to next item */
}
printf(“n”);
}

 
 

6. With the help of a suitable numerical example, describe the following concepts of a
Binary Search Tree:

A) Analysis of BST
Ans: It is a directed tree in which outdegree of each node is less than or equal to two i.e., each
node in the tree can have 0, or 2 children. An empty tree is also a binary tree. A binary tree
can also be defined recursively as follows:

A binary tree is a finite set with the following properties:

1. The first subset contains only one element and it is called root of the tree. If root contains
NULL it is called empty binary tree.

2. The second subset is a binary tree called left sub tree. The left sub tree can be empty.

3. The third subset is a binary tree called right sub tree. The right sub tree can be empty.

Binary tree operations

A binary tree is a tree in which no node can have more than two subtrees. In other words, a
node can have zero, one, or two sub trees. In other words A tree in which every parent has
one or two children (but not more than that) is called as binary tree.

The “root” component of binary tree is the forefother of all children. But it can have only up to
two children one “right” child and “left” child. These children can become fathers and each can
produce only two children. In fact a child can become a father, grandfather, great grandfather
and son. Fig shows five binary trees all with three nodes.

Là left Rà Right

 
 

We can have binary trees with any number of nodes.

“A child cannot have more than one father. If it is so then the tree is not a binary tree. A binary
tree node cannot have more than two subtrees.”

Strictly binary tree

If the outdegree of every node in a tree is either 0 or 2, then the tree is said to be strictly
binary tree i.e., each node can have maximum two children or empty left and empty right
child. The trees shown in fig. 6.2.e and fig. 6.2.f are strictly binary trees.

Example:

A binary tree is said to be strictly binary if every non-leaf node has non-empty left and right
subtrees. Fig shows a strictly binary tree.

A Strictly binary tree

=>The binary tree in the above fig is not strictly binary because the non-leaf node E has no
right sub-tree and the non-leaf node C has no left sub-tree.

Complete binary tree

A strictly binary tree in which the number of nodes at any level i is 2i-1, then the tree is said to
be a complete binary tree. The tree shown in fig. 6.2.f is a strictly binary tree and at the same
time it is a complete binary tree.

Example:

 
 

A Complete Binary tree

In a complete binary tree, the total number of nodes at level 0 is 1 i.e., 2°

Number of nodes at level 1 is 2 i.e., 21

Number of nodes at level 2 is 4 i.e., 22

Number of nodes at level 3 is 8 i.e., 23

……………………………………

……………………………………

……………………………………

Number of nodes at the last level d is 2d.

It is clear from the figure that all the nodes in the final level d are leaf nodes and the total
number of leaf nodes is 2d. In a complete binary tree, we can easily find the number of non-
leaf nodes. Now, let us find the number of non-leaf nodes in a complete binary tree. Total
number of nodes in the complete binary tree =

2° + 21 + 22 + ………….2d.

Summation of this series is given by

S = a( rn- 1) 1( r- 1)

where a = 1, n = d+ 1 and r = 2

So, total number of nodes nt = 2d+1- 1

 
 

Nodes at level d are all leaf nodes. So, number of non-leaf nodes is given by 2d+1 – 1 –2d
which is equal to 2d – 1.

B) Insertion of Nodes into a BST


Ans: Insertion Operation: Suppose the node pointed to by temp has to be inserted whose
information field contains the item ‘I’ as shown in below figure.

To insert an item

Let ‘d’ be an array, which contains only the directions where the node temp has to be
inserted. If ‘d’ contains ‘LRLR’, from the root node first moves towards left(L), then right(R),
then left(L) and finally move towards right(R). Finally if the pointer points to NULL, at that
position, node temp can be inserted otherwise, node temp can not be inserted.

To achieve this, one has to start from the root node. Let us use two pointers prev and cur
where prev always points to parent node and cur points to child node. Initially cur points to
root node and prev points to NULL. To start with, one can write the following statements.

prev = NULL

cur = root

Now keep updating the node pointed to by cur towards left if the direction is (’L ‘) otherwise,
update towards right. The pointer variable prev always points to the parent node and cur
points to the child node. Once all directions are over, if cur points to NULL, insert the node
temp towards left or right based on the last direction. Otherwise, display an error message. In
the following algorithm, an index variable i is used to access the directions. The C function to
insert a node is shown in below example

Example: Function to insert an item into a tree

NODE insert(int item, NODE root)

 
 

NODE temp; /* Node to be inserted */

NODE cur /* Child node */

NODE prev; /* Parent node */

char direction[.10]; /* Directions where the node has to be inserted */

int i; /* Maximum depth where a node can be inserted */

temp = getnode(); /* Obtain a node from the availability list */

temp->info = item; /* Copy the necessary information */

temp->llink = temp->rlink = NULL;

if ( root = = NULL ) return temp; /* Node is inserted for the first time */

printf("Give the directions where you want to insertn");

scanf("%s", direction);

toupper(direction); /* Convert the directions to upper case */

prev = NULL;

cur = root;

/* find the position to insert */

for ( i = 0; i< strlen(direction) && cur != NULL; i+ +)

prev= cur; /* Parent */

if ( direction[i] == ‘L’ ) /* If direction is (L) move towards left */.

cur = cur->llink;

else /* Otherwise move towards right */

cur = cur->rlink;

if ( cur != NULL ÷÷ i!= strlen(direction))

printf("Insertion not possiblen");

freenode(temp);

 
 

`return root;

/* insert the node at the leaf level */

if ( direction[i-1 ] == ‘L’ ) /* Attach the node to the left of the parent * /

prev->llink = temp;

else

prev->rlink = temp; /* Attach the node to the right of the parent */

return root;

7. Explain the Bellman Ford algorithm with respect to Minimum Spanning Trees.
Ans: Spanning Trees: A spanning tree of a graph is an undirected tree consisting of only those
edges necessary to connect all the nodes in the original graph. For any pair of nodes there
exists only one path between them and the insertion of any edge to a spanning tree forms a
unique cycle. Those edges left out of the Spanning tree that were present in the original graph
connect paths together in the tree.

If a DFS is used, those edges traversed by the algorithm form the edges of the tree, referred
to as depth first spanning tree. If a BFS is used, the spanning tree is formed from those edges
traversed during the search, producing a breadth first spanning tree.

Examples:

 
 

Network: A network is a graph that has weights or costs associated with its edges. It is also
called weighted graph.

Eg.:

Minimum spanning tree: This is a spanning tree that covers all vertices of a network such
that the sum of the costs of its edges is minimum. There are two algorithms for finding the
minimum spanning tree, given a network.

1) Kruskal’s algorithm 2) Prim’s algorithm

1. Kruskal’s algorithm to find min. spanning tree.

Step 1: Arrange all edges in ascending order of their cost to form an input set.

Step 2: From this input set, take each edge and if it does not form a cycle, include it in the
output set, which forms the minimum spanning tree. The sum of the costs of the
edges in the output set is the least.

Note: If a vertex u is reachable from vertex w and w is reachable from u, then the path
between u and w is called a cycle. Eg. in fig. 7.10 above, the set [(1, 2) (2, 3), (3, 4),
(4, 1)] is a cycle and the superset [(1, 2) (2, 5) (5, 6) (6, 4) (4, 1)] also is a cycle.

Eg. Applying Kruskal’s algorithm to the network of fig. 7.10, we get

Step 1: Input set = { (1, 3), (4, 6), (2, 5), (3, 6), (2, 3), (3, 4), (1, 4), (1, 2), (3, 5), (5, 6)}

Step 2: Output set = { (1, 3) (4, 6) (2, 5), (3, 6), (2, 3)}

Minimum cost = 1+2+3+4+5 = 15.

Thus, minimum spanning tree is an shown:

 
 

Bellman-Ford Algorithm: It is a more generalized single-source shortest path algorithm


which can find the shortest path in a graph with negative weighted edges. If there is no
negative cycle in the graph, this algorithm will updates each d[v] with the shortest path from s
to v, fill up the predecessor list "pi", and return TRUE. However, if there is a negative cycle in
the given graph, this algorithm will return FALSE.

BELLMAN_FORD(Graph G,double w[ ][ ],Node s)

initialize_single_source(G,s)

for i=1 to |V[G]|-1

for each edge (u,v) in E[G]

relax(u,v,w)

for each edge (u,v) in E[G]

if d[v] > d[u] + w(u, v) then

return FALSE

return TRUE

8. Explain the following graph problems:

A) Telecommunication problem
Ans: A graph is a collection of vertices V and a collection of edges E consisting of pairs of vertices.
Think of vertices as locations. The set of vertices is the set of all the possible locations. In this
analogy, edges represent paths between pairs of those locations. The set E contains all the
paths between the locations.

 
 

Representation

The graph is normally represented using that analogy. Vertices are points or circles, edges
are lines between them.

In this example graph:

V = {1, 2, 3, 4, 5, 6}

E = {(1,3), (1,6), (2,5), (3,4), (3,6)}.

Each vertex is a member of the set V. A vertex is sometimes called a node.

Each edge is a member of the set E. Note that some vertices might not be the end point of
any edge. Such vertices are termed isolated.

Sometimes, numerical values are associated with edges, specifying lengths or costs; such
graphs are called edge-weighted graphs (or weighted graphs). The value associated with an
edge is called the weight of the edge. A similar definition holds for node-weighted graphs.

Telecommunication: Given a set of computers and a set of wires running between pairs of
computers, what is the minimum number of machines whose crash causes two given
machines to be unable to communicate? (The two given machines will not crash.)

Graph: The vertices of the graph are the computers. The edges are the wires between the
computers. Graph problem: minimum dominating sub-graph.

B) Knight Moves
Ans: Given: Two squares on an 8×8 chessboard. Determine the shortest sequence of knight
moves from one square to the other.

Graph: The graph here is harder to see. Each location on the chessboard represents a
vertex. There is an edge between two positions if it is a legal knight move. Graph Problem:
Single Source Shortest Path.

Potrebbero piacerti anche