You are on page 1of 14

Master of Computer Application (MCA) – Semester 2

MC0068 – Data Structures using C– 4 Credits


(Book ID: B0701 & B0702)
Assignment Set – 1 (40 Marks)

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


Ans:

Function Pointers

A function pointer is a variable that stores the address of a function that can later be
called through that function pointer. This is useful because functions encapsulate
behavior. For instance, every time you need a particular behavior such as drawing a line,
instead of writing out a bunch of code, all you need to do is call the function. But
sometimes you would like to choose different behaviors at different times in essentially
the same piece of code. Read on for concrete examples.

Example Uses of Function Pointers

Functions as Arguments to Other Functions


If you were to write a sort routine, you might want to allow the function's caller to choose
the order in which the data is sorted; some programmers might need to sort the data in
ascending order, others might prefer descending order while still others may want
something similar to but not quite like one of those choices. One way to let your user
specify what to do is to provide a flag as an argument to the function, but this is
inflexible; the sort function allows only a fixed set of comparison types (e.g., ascending
and descending).

A much nicer way of allowing the user to choose how to sort the data is simply to let the
user pass in a function to the sort function. This function might take two pieces of data
and perform a comparison on them. We'll look at the syntax for this in a bit.

Callback Functions
Another use for function pointers is setting up "listener" or "callback" functions that are
invoked when a particular event happens. The function is called, and this notifies your
code that something of interest has taken place.

Why would you ever write code with callback functions? You often see it when writing
code using someone's library. One example is when you're writing code for a graphical
user interface (GUI). Most of the time, the user will interact with a loop that allows the
mouse pointer to move and that redraws the interface. Sometimes, however, the user will
click on a button or enter text into a field. These operations are "events" that may require
a response that your program needs to handle. How can your code know what's
happening? Using Callback functions! The user's click should cause the interface to call a
function that you wrote to handle the event.

To get a sense for when you might do this, consider what might happen if you were using
a GUI library that had a "create button" function. It might take the location where a
button should appear on the screen, the text of the button, and a function to call when the
button is clicked. Assuming for the moment that C (and C++) had a generic "functions
pointer" type called function, this might look like this:

void create_button( int x, int y, const char *text, function callback_func );

Whenever the button is clicked, callback_func will be invoked. Exactly what


callback_func does depends on the button; this is why allowing the create_button
function to take a function pointer is useful.

Function Pointer Syntax


The syntax for declaring a function pointer might seem messy at first, but in most cases
it's really quite straight-forward once you understand what's going on. Let's look at a
simple example:
void (*foo)(int);
In this example, foo is a pointer to a function taking one argument, an integer, and that
returns void. It's as if you're declaring a function called "*foo", which takes an int and
returns void; now, if *foo is a function, then foo must be a pointer to a function.
(Similarly, a declaration like int *x can be read as *x is an int, so x must be a pointer to
an int.)

The key to writing the declaration for a function pointer is that you're just writing out the
declaration of a function but with (*func_name) where you'd normally just put
func_name.

2. Demonstrate with your own programming example the usage of structures


within an array?

Ans:
Ans –
#include<conio.h>
#include<stdio.h>

void main()
{ struct student
{ int roll;
Char name[20];
}
} std[5]; //declaring an array of structure

For (int i=0;i<5;i++)


{ scanf(“enter roll no: %d”,&std[i].roll);
Scanf(“enter name: %c”,&std[i].name);
}
}

3. Explain the theory of non linear data structures?


Ans –

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.

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

Figure : 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.

Figure: 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.

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


Ans –
#include<stdio.h>
#include<conio.h>
#define Max 5
int a [Max] , top=-l;-
void push()
{
int ele;
char ch;
if(top= =-l)
top=0;
do
{
if(top>=5)
{
printf("Stack if full");
break;
else
{
clrscr();
printf ( "Enter element to be insertedn" ) ;
scanf(“%d”,&ele);
a[top++] =ele;
}
printf ( "Do you want to add more elements: ?n") ;
scanf ( "n%c" , &ch);
printf(“%c”,ch)
}while((ch= = ‘Y’)||(ch==’Y’));
}
void pop( )
{
if(top= =-l)
{
printf ( "stack is underflown");
}
else
{
for(int i=top-l;i>=0;i–)
printf ("%dn", a [i] ) ;
}
}
void main()
{
clrscr ( ) ;
char c;
int choice;
do
{
clrscr( );
printf ("Enter Your Choicen");
printf("l -> Pushn");
printf ("2 -> Popn");
scanf ("%d", &Choice);
if(choice= =l)
push( );
else if (choice= = 2)
pop( );
else
printf(“invalid choice”);
printf(“Do You Want to continuen”);
scanf(“n%c”,&c);
}while((c= = ‘y’)||(c= = ‘Y’));
}
5. Describe the theory and applications of Double Ended Queues (Deque) and
circular queues?
Ans: Double Ended Queues
In computer science theory, a deque (short for double-ended queue—usually
pronounced deck) is an abstract list type data structure, also called a head-tail
linked list, for which elements can be added to or removed from the front (head) or

back (tail).Deque is sometimes written dequeue, but this use is generally deprecated in
technical literature or technical writing because dequeue is also a verb meaning "to
remove from a queue". Nevertheless, several libraries and some writers, such as Aho,
Hopcroft, and Ullman in their textbook Data Structures and Algorithms, spell it dequeue.
DEQ and DQ are also used.

This differs from the queue abstract data type or First-In-First-Out List (FIFO), where
elements can only be added to one end and removed from the other. This general data
class has some possible sub-types:

• An input-restricted deque is one where deletion can be made from both ends,
but input can only be made at one end.

• An output-restricted deque is one where input can be made at both ends, but
output can be made from one end only.
Both the basic and most common list types in computing, the queues and stacks can
be considered specializations of deques, and can be implemented using deques.

Circular queues

A circular is a data structure that uses a single, fixed-


size queue as if it were connected end-to-end. This structure lends itself
easily to buffering data streams

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

B) Insertion of Nodes into a BST

Ans:

A prominent data structure used in many systems programming applications for


representing and managing dynamic sets.
Average case complexity of Search, Insert, and Delete Operations is O(log n), where n is
the number of nodes in the tree.

DEF: A binary tree in which the nodes are labeled with elements of an ordered dynamic
set and the following BST property is satisfied: all elements stored in the left subtree of
any node x are less than the element stored at x and all elements stored in the right
subtree of x are greater than the element at x.

An Example: shows a binary search tree. Notice that this tree is obtained by inserting the
values 13, 3, 4, 12, 14, 10, 5, 1, 8, 2, 7, 9, 11, 6, 18 in that order, starting from an empty
tree.

Note that inorder traversal of a binary search tree always gives a sorted sequence of the
values. This is a direct consequence of the BST property. This provides a way of sorting a
given sequence of keys: first, create a BST with these keys and then do an inorder
traversal of the BST so created.

Note that the highest valued element in a BST can be found by traversing from the root in
the right direction all along until a node with no right link is found (we can call that the
rightmost element in the BST).

The lowest valued element in a BST can be found by traversing from the root in the left
direction all along until a node with no left link is found (we can call that the leftmost
element in the BST).

Search is straightforward in a BST. Start with the root and keep moving left or right
using the BST property. If the key we are seeking is present, this search procedure will
lead us to the key. If the key is not present, we end up in a null link.

Insertion in a BST is also a straightforward operation. If we need to insert an element x,


we first search for x. If x is present, there is nothing to do. If x is not present, then our
search procedure ends in a null link. It is at this position of this null link that x will be
included.

If we repeatedly insert a sorted sequence of values to form a BST, we obtain a


completely skewed BST. The height of such a tree is n - 1 if the tree has n nodes. Thus,
the worst case complexity of searching or inserting an element into a BST having n nodes
is O(n)
An example of a binary search tree

Insertion of Nodes into a BST


We begin by examining the root node. If the tree is null, the value we are searching for
does not exist in the tree. Otherwise, if the value equals the root, the search is successful.
If the value is less than the root, search the left subtree. Similarly, if it is greater than the
root, search the right subtree. This process is repeated until the value is found or the
indicated subtree is null. If the searched value is not found before a null subtree is
reached, then the item must not be present in the tree.

Here is the search algorithm in the Python programming language:

# 'node' refers to the parent-node in this case


def search_binary_tree(node, key):
if node is None:
return None # key not found
if key < node.key:
return search_binary_tree(node.leftChild, key)
elif key > node.key:
return search_binary_tree(node.rightChild, key)
else: # key is equal to node key
return node.value # found key

… or equivalent Haskell:

searchBinaryTree _ NullNode = Nothing


searchBinaryTree key (Node nodeKey nodeValue (leftChild, rightChild)) =
case compare key nodeKey of
LT -> searchBinaryTree key leftChild
GT -> searchBinaryTree key rightChild
EQ -> Just nodeValue

This operation requires O(log n) time in the average case, but needs O(n) time in the
worst case, when the unbalanced tree resembles a linked list (degenerate tree).

Assuming that BinarySearchTree is a class with a member function "search(int)" and a


pointer to the root node, the algorithm is also easily implemented in terms of an iterative
approach. The algorithm enters a loop, and decides whether to branch left or right
depending on the value of the node at each parent node.

bool BinarySearchTree::search(int val)


{
Node *next = this->root();

while (next != NULL) {


if (val == next->value()) {
return true;
} else if (val < next->value()) {
next = next->left();
} else {
next = next->right();
}
}
//not found
return false;
}

Insertion

Insertion begins as a search would begin; if the root is not equal to the value, we search
the left or right subtrees as before. Eventually, we will reach an external node and add the
value as its right or left child, depending on the node's value. In other words, we examine
the root and recursively insert the new node to the left subtree if the new value is less
than the root, or the right subtree if the new value is greater than or equal to th

7. Explain the theory of Minimum spanning trees.


Ans –

Tree is one of the most efficient data structure used in a computer program. There
are many types of tree. Binary tree is a tree that always have two branches, Red-Black-
Trees, 2-3-4 Trees, AVL Trees, etc. A well balanced tree can be used to design a good
searching algorithm.

A Tree is an undirected graph that contains no cycles and is connected. Many


trees are what is called rooted, where there is a notion of the "top" node, which is called
the root. Thus, each node has one parent, which is the adjacent node which is closer to the
root, and may have any number of children, which are the rest of the nodes adjacent to it.
The tree above was drawn as a rooted tree.

Spanning trees

A spanning tree of a graph is just a subgraph that contains all the vertices and is a
tree. A graph may have many spanning trees; for instance the complete graph on four
vertices

Minimum spanning trees

Now suppose the edges of the graph have weights or lengths. The weight of a tree
is just the sum of weights of its edges. Obviously, different trees have different lengths.
The problem: how to find the minimum length spanning tree?

The standard application is to a problem like phone network design. You have a
business with several offices; you want to lease phone lines to connect them up with each
other; and the phone company charges different amounts of money to connect different
pairs of cities. You want a set of lines that connects all your offices with a minimum total
cost. It should be a spanning tree, since if a network isn’t a tree you can always remove
some edges and save money.
A less obvious application is that the minimum spanning tree can be used to
approximately solve the traveling salesman problem. A convenient formal way of
defining this problem is to find the shortest path that visits each point at least once. Note
that if you have a path visiting all points exactly once, it’s a special kind of tree. For
instance, in the example above, twelve of sixteen spanning trees are actually paths. If you
have a path visiting some vertices more than once, you can always drop some edges to
get a tree. So in general the MST weight is less than the TSP weight, because it’s a
minimization over a strictly larger set.

On the other hand, if you draw a path tracing around the minimum spanning tree,
you trace each edge twice and visit all points, so the TSP weight is less than twice the
MST weight. Therefore this tour is within a factor of two of optimal.

8. Explain the following graph problems:


A) Telecommunication problem
B) Knight Moves
Ans –

Telecommunication problem

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.

Knight moves

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.

You might also like