mc0068_august 2010_data structures using c

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 *.

Upload: harvinder-singh

Post on 14-Oct-2014

386 views

Category:

Documents


0 download

DESCRIPTION

MC0068_August 2010_Data Structures using C

TRANSCRIPT

Page 1: MC0068_August 2010_Data Structures using C

 

 

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 *.

Page 2: MC0068_August 2010_Data Structures using C

 

 

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.*/

Page 3: MC0068_August 2010_Data Structures using C

 

 

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

Page 4: MC0068_August 2010_Data Structures using C

 

 

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.

Page 5: MC0068_August 2010_Data Structures using C

 

 

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.

Page 6: MC0068_August 2010_Data Structures using C

 

 

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);

}

Page 7: MC0068_August 2010_Data Structures using C

 

 

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]

Page 8: MC0068_August 2010_Data Structures using C

 

 

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

{

int empno;

char emp_name[30];

int salary;

struct date_join

{

int day;

int month;

int year;

};

};

2. sruct emp

{

int empno;

char emp_name[30];

int salary;

struct date_join

};

struct date_join

{

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.

Page 9: MC0068_August 2010_Data Structures using C

 

 

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.

Page 10: MC0068_August 2010_Data Structures using C

 

 

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

Page 11: MC0068_August 2010_Data Structures using C

 

 

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

Page 12: MC0068_August 2010_Data Structures using C

 

 

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

Page 13: MC0068_August 2010_Data Structures using C

 

 

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.

Page 14: MC0068_August 2010_Data Structures using C

 

 

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

Page 15: MC0068_August 2010_Data Structures using C

 

 

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!

Page 16: MC0068_August 2010_Data Structures using C

 

 

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;

Page 17: MC0068_August 2010_Data Structures using C

 

 

}

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 + +)

{

Page 18: MC0068_August 2010_Data Structures using C

 

 

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);

Page 19: MC0068_August 2010_Data Structures using C

 

 

}

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.

Page 20: MC0068_August 2010_Data Structures using C

 

 

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 */

Page 21: MC0068_August 2010_Data Structures using C

 

 

}

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;

Page 22: MC0068_August 2010_Data Structures using C

 

 

}

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+ +)

Page 23: MC0068_August 2010_Data Structures using C

 

 

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;

Page 24: MC0068_August 2010_Data Structures using C

 

 

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

Page 25: MC0068_August 2010_Data Structures using C

 

 

Pictorial representation of a circular queue

Page 26: MC0068_August 2010_Data Structures using C

 

 

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) )

{

Page 27: MC0068_August 2010_Data Structures using C

 

 

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;

}

Page 28: MC0068_August 2010_Data Structures using C

 

 

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”);

}

Page 29: MC0068_August 2010_Data Structures using C

 

 

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

Page 30: MC0068_August 2010_Data Structures using C

 

 

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:

Page 31: MC0068_August 2010_Data Structures using C

 

 

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

Page 32: MC0068_August 2010_Data Structures using C

 

 

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)

{

Page 33: MC0068_August 2010_Data Structures using C

 

 

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);

Page 34: MC0068_August 2010_Data Structures using C

 

 

`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:

Page 35: MC0068_August 2010_Data Structures using C

 

 

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:

Page 36: MC0068_August 2010_Data Structures using C

 

 

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.

Page 37: MC0068_August 2010_Data Structures using C

 

 

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.