Data Structures and Algorithms
Data Structures and Algorithms
432
When a class is declared within a function, it is known only to that function and unknown outside
of it. Several restrictions apply to local classes. First, all member functions must be defined within
the class declaration. The local class may not use or access local variables of the function in which
it is declared (except that a local class has access to static local variables declared within the
function or those declared as extern). It may access type names and enumerators defined by the
enclosing function, however. No static variables may be declared inside a local class. Because of
these restrictions, local classes are not common in C++ programming.
(b) One of the more important forms of an overloaded constructor is the copy constructor. Defining a
copy constructor can help you prevent problems that might occur when one object is used to
initialize another.
Yes it is possible, as an example we can describe that the copy constructor applies only to
initializations. For example, assuming a class called myclass, and that y is an object of type
myclass, each of the following statements involves initialization.
myclass x = y; // y explicitly initializing x
func(y);
// y passed as a parameter
y = func();
// y receiving a temporary, return object
(c) Classes are created using the keyword class. A class declaration defines a new type that links code
and data. This new type is used to declare objects of that class. Thus, a class is a logical
abstraction, but an object has physical existence. In other words, an object is an instance of a class.
A general form of a class is:
class class-name
{
private data and functions
access-specifier:
data and functions
. . .
access-specifier:
data and functions
};
433
By default, functions and data variables declared within a class are private to that class and
may be accessed only by other members of the class. The public access specifier allows functions
or data to be accessible to other parts of the program. The protected specifier is needed when
inheritance is involved. Once an access specifier has been used, it remains in effect until either
another access specifier is encountered or the end of the class declaration is reached. Functions
and variables declared within a class are called members of the class.
(d) Encapsulation is the mechanism that binds together code and the data it manipulates, and keeps
both safe from outside interference and misuse. In an object-oriented language, code and data may
be combined in such a way that a self-contained "black box" is created. When code and data are
linked together in this fashion, an object is created. In other words, an object is the device that
supports encapsulation.
Within an object, code, data, or both may be private to that object or public. Private code or
data is known to and accessible only by another part of the object. That is, private code or data
may not be accessed by a piece of the program that exists outside the object. When code or data is
public, other parts of your program may access it even though it is defined within an object.
Typically, the public parts of an object are used to provide a controlled interface to the private
elements of the object.
2.
434
This is not the case. If you call a virtual function inside a constructor, only the local version of
the function is used. That is, the virtual mechanism does not work within the constructor.
This behavior makes sense for two reasons. Conceptually, the constructors job is to bring the
object into existence. Inside any constructor, the object may only be partially formed you can
only know that the base-class objects have been initialized, but you cannot know which classes are
inherited from you. A virtual function call, however, reaches forward or outward into the
inheritance hierarchy. It calls a function in a derived class. If you could do this inside a constructor,
you would be calling a function that might manipulate members that had not been initialized yet, a
definite way for disaster.
(c) A virtual function is a member function that is declared within a base class and redefined by a
derived class. To create a virtual function, precede the function's declaration in the base class with
the keyword virtual. When a class containing a virtual function is inherited, the derived class
redefines the virtual function to fit its own needs. In essence, virtual functions implement the "one
interface, multiple methods" philosophy that underlies polymorphism. The virtual function within
the base class defines the form of the interface to that function. Each redefinition of the virtual
function by a derived class implements its operation as it relates specifically to the derived class.
That is, the redefinition creates a specific method.
When accessed "normally," virtual functions behave just like any other type of class member
function. However, what makes virtual functions important and capable of supporting run-time
polymorphism is how they behave when accessed via a pointer. A base-class pointer can be used
to point to an object of any class derived from that base. When a base pointer points to a derived
object that contains a virtual function, C++ determines which version of that function to call based
upon the type of object pointed to by the pointer. And this determination is made at runtime. Thus,
when different objects are pointed to, different versions of the virtual function are executed. The
same effect applies to base-class references.
3.
435
In this program, the try block contains three statements: S1, S2, S3 and catch(int i) statement that
processes an integer exception. Within the try block, only S1 and S2 will be executed. Once an
exception has been thrown, control passes to the catch expression and the try block is terminated.
That is catch is not called. Rather, program execution is transferred to it. The S3 following the
throw will never be executed.
4.
Develop a C++ class twoStacks in which a single array is used to represent two stacks. Put the bottom
of one stack at one end of array and bottom of second stack at the other end. The two stacks grow
towards middle of the array. The class should contain the methods to perform all operations of stack
ADT.
StackA
StackB
BottomA
topA
topB
BottomB
In this situation, one stack, say A, grows from one end of the array and the other stack, say B, stay at
the other end and grows in the opposite direction, That is towards the direction of A. The operations of
this double stack structure are: push1, push2, pop1 and pop2. We declare two stacks of varying length
in a single array to manipulate the two stacks.
#include <iostream>
#define MAX 10
using namespace std;
class DoubleStack
{ private:
char stack[MAX];
int top1, top2, count; // count denotes number of occupied cells
public:
DoubleStack();
// constructor
void push1(char);
void push2(char);
char pop1(void);
char pop2(void);
};
DoubleStack::DoubleStack()
{ top1 = -1; top2 = MAX; count = 0;
// initialize top1, top2, count
for( int i = 0; i < MAX; i++ )
// initialize all cells of the stack
stack[i] = NULL;
}
436
endl;
endl;
endl;
endl;
5.
437
(a) Explain about the skip list representation of dictionary with an example?
(b) What are the data members of skip list class? Write the constructor for skip list.
(a) Skip list is a data structure that uses a probabilistic algorithm to keep the structure in balance
(invented by William Pugh). The algorithms for skip lists are very simple and elegant. Skip lists
are also very fast. This combination makes them an attractive alternative to other data structures.
Skip lists are made up of a series of nodes connected one after the other. Each node contains a
key-value pair as well as one or more pointers, to nodes further along in the list. The number of
pointers each node contains is determined randomly. This gives skip lists their probabilistic nature,
and the number of pointers a node contains is called its node level.
Each node will have at least one node pointer, and the first pointer will always point to the
next node in the list. In this way, skip lists are very much like linked lists. However, any additional
node pointers can skip one or more intermediate nodes and point to nodes later in the list. This is
where skip lists get their name.
Two nodes that are always present in a skip list are the header node and the NULL node. The
header node marks the beginning of the list and the NULL node marks the end of the list. The
NULL node is unique in that when a skip list is created, it is given a key greater than any legal key
that will be inserted into the skip list. This is important to how skip lists algorithms work.
Skip lists have three more important properties: maximum level, current overall level, and
probability. Maximum level is the highest level a node in a skip list may have. The current overall
level is the value of the node level with the highest level in the skip list. Probability is a value used
in the algorithm for randomly determining the level for each node.
4
3
1
Header
NULL
11
2
3
9
8
13
A Skip List
Level 1 linked list includes all nodes; the level 2 list includes every second node; the level 3
list every fourth node; and so on. In other words, every second node points to positions two nodes
ahead; every fourth node points to positions four nodes ahead; every eighth node points to
positions eight nodes ahead; and so on.
(b) Data members of skip list class:
SkipListNode - contains key value and a forward pointer.
Header pointer of type SkipListNode.
Level of integer type that indicates level of each node.
template <class T>
struct SkipList
{
SkipNode<T> *head;
int level;
SkipList()
// constructor
{
head = new SkipNode<T>(MAX_LEVEL, T());
438
level = 0;
}
// functions
};
6.
Define a class called binarySearchTree to represent a Binary search tree. Extend this class by adding a
public method outputInRange (Low, High) that outputs, in ascending order of key, all elements in a
binary search tree whose key lies between Low and High. Use recursion and avoid entering subtrees
that cannot possibly contain any elements with keys in desired range.
struct BSTNode
{
int data;
BSTNode *left;
BSTNode *right;
BSTNode( int d )
// constructor
{ data = d;
left = right = NULL;
}
};
class BSTree
{
public:
BSTNode *insertTree(BSTNode *p, int key);
BSTNode *search(BSTNode *root, int key);
BSTNode *deleteTree(BSTNode *root, int key);
void outputInRange(BSTNode *root, int low, int high);
void inorder(BSTNode *p);
void preorder(BSTNode *p);
void postorder(BSTNode *p);
};
void BSTree::outputInRange(BSTNode *p, int low, int high)
{ if( p != NULL )
{ outputInRange(p->left, low, high);
if( p->data >= low && p->data <= high )
cout << p->data << " ";
outputInRange(p->right, low, high);
}
}
7.
439
46
22
12 15 20
23 27 29
32
52
40
33 36
41 43
47 49 51
56
53 55
57 59
440
2log2(n+1) 2r
(eq. 2).
That is, 2r 2log2(n+1)
From (eq. 1 and eq. 2) we have
h 2r 2log2(n+1)
That is, h 2log2(n+1) (result equation).
Therefore, the height of a red-black tree is at most 2log2(n+1).
8.
3
K
4
OK
5
Every time we add a new suffix to the tree, we are going to automatically extend the edges
leading into every leaf node by a single character. That character will be the last character in the
new suffix. If the edge is split, its starting point may change, but it will still extend all the way to
the end of the input text. This means that we only have to worry about updating explicit and
implicit nodes at the active point, which was the first non-leaf node. Given this, we would have to
progress from the active point to the empty string, testing each node for update eligibility.
The point where you find the first matching descendant is called the end point. The end point
has an additional feature that makes it particularly useful. Since we were adding leaves to every
suffix between the active point and the end point, we now know that every suffix longer than the
end point is a leaf node. This means the end point will turn into the active point on the next pass
over the tree.
By confining our updates to the suffixes between the active point and the end point, we cut
way back on the processing required to update the tree. And by keeping track of the end point, we
automatically know what the active point will be on the next pass. A first pass at the update
algorithm using this information might look something like this (in C-like pseudo code):
Note:
441
Update( new_suffix )
{
current_suffix = active_point
test_char = last_char in new_suffix
done = false;
while ( !done )
{
if current_suffix ends at an explicit node
{
if the node has no descendant edge starting with test_char
create new leaf edge starting at the explicit node
else done = true;
} else
{ if the implicit node's next char isn't test_char
{ split the edge at the implicit node
create new leaf edge starting at the split in the edge
} else done = true;
}
if current_suffix is the empty string
done = true;
else
current_suffix = next_smaller_suffix( current_suffix )
}
active_point = current_suffix
}
Insert is O(dm) where d is the size of the alphabet and m is length of suffix. Constructing an entire
trie is O(dn), where n is the sum of the lengths of all the suffixes in S.
Search is O(dm) where d is the size of the alphabet and m is the length of the suffix. O(d) at each
node and must visit m nodes. Note: It is possible to improve the O(d) by modifying how the
alphabet is stored in the nodehash table O(1)binary search O(log d).
(b) Properties of a compressed trie:
A compressed trie has internal nodes of degree at least two.
The compressed trie is obtained from a standard trie by compressing chains of redundant
nodes.
Compressed tries remove redundant nodes and replaces them with strings instead of
characters. A node is redundant only if it has no children is not the root.
At each node of the trie, we can store the triplet (i, j, k) instead of the string -where i is the
index into the string S, and j and k define the substring within S[i].
Compressed trie can be stored in space O(n), where n is the length of the string by using O(1)
space index ranges at the nodes.
It can be constructed in O(dn) time, where d is an alphabet size.
It supports arbitrary pattern matching in O(dn) time.
442
(a) Explain about the dynamic memory allocation and de-allocation in C++.
(b) Explain about static inner classes with a program.
(a) C++ provides two dynamic allocation operators: new and delete. These are used to allocate and
free memory at run-time. The new operator allocates memory and returns a pointer to the start of it.
The delete operator frees memory previously allocated using new. The general formats of new
and delete are:
ptr_var = new type;
delete ptr_var;
ptr_var is a pointer variable that receives a pointer to memory that is large enough to hold an item
of type, type.
int main()
{ int *p;
p = new int; // allocate space for an int
if( p == NULL )
{ cout << "Insufficient Memory \n"; return 1; }
*p = 100;
cout << "Address of p is " << p << endl;
cout << "value is " << *p << "\n";
delete p;
// free memory space
return 0;
}
This program generated the following output (Note: Address is displayed as an hexadecimal
number).
Address of p is 0x235f0246
value is 100
Operator new automatically allocates enough memory no need to specify required number of
bytes; new automatically returns a pointer of specified type no need to use explicit type cast.
(b) In C++, it is possible to define one class inside another. A class defined inside another one is
called an inner class. C++ provides two kinds of inner classes: static and non-static. Consider the
following program:
#include <iostream>
using namespace std;
class A
// class with static inner class
{ int y;
public:
static class B
{ int x;
public:
B() { x = 25; }
void func()
{ cout << "Inside static inner class\n"; }
int getx()
{ return x; }
};
443
};
int main()
{
A::B ob;
ob.func();
cout << ob.getx() << endl;
return 0;
}
This program defines the class A which contains a static inner class B. A static inner class behaves
like any outer class. It may contain functions and fields, and it may be instantiated like this:
A::B ob;
This statement creates a new instance of the inner class B. Given such an instance, we can invoke
the func() in the usual way:
ob.func();
Note: it is not necessarily the case that an instance of the outer class A exists even when we have
created an instance of the inner class. Similarly, instantiating the outer class A does not create any
instances of the inner class B.
2.
ptr = &d1;
ptr->show();
ptr = &d2;
444
ptr->show();
return 0;
Abstract class: A class that contains at least one or more pure virtual functions a pure
virtual function has no definition (or body). Example of pure virtual function is: virtual
void func() = 0;
445
Output:
first in child-1
second Method
first in child-2
second Method
3.
Let myclass contain some data items. Create the object of myclass.
myclass ob;
ii. Open the data file: test.dat in output mode (include header file: <fstream>).
ofstream outfile("test.dat", ios::out);
iii. Make the myclass object by reading data items, and write the object.
outfile.write((char*) &ob, sizeof(ob));
v.
(b) An object or group of objects are taken, put on a disk or sent through a wire or wireless transport
mechanism. Later on another computer, reverses the processes, and saves the original object(s).
The basic mechanism is to flatten object(s) into a one dimensional stream of bits, and to turn that
stream of bits back in the original object(s). Objects of classes are sent through human-readable
(text) and human-non readable (binary) formats. This process is known as serialization.
We probably want to use iostream's >> and << operators rather than its read() and
write() methods. The >> and << operators are better for text mode, whereas read() and
write() are better for binary mode.
(c) We can open a file in a different directory such as ..\test.dat.
ofstream outfile("C:\myDir\test.dat", ios::out);
446
(d) How can we tell {if a key, which key} was pressed before the user presses the ENTER key?
Use the header file: <conio.h> and its input character function, getch().
Before pressing the key, the key would display.
#include <iostream>
#include <conio.h>
using namespace std;
int main()
{
char ch;
cout << "Enter a key: ";
ch = getch();
cout << ch << endl;
return 0;
}
4.
What is a sparse matrix? Explain about the linear list representation of a sparse matrix?
This question is repeated. Refer set-3 of section C.1.
5.
Use linear probing, a hash table with b=17 buckets, and the hash function f(k) = k% b; Start with an
empty hash table and insert pairs whose keys are 7, 42, 25, 70, 14, 38, 8, 21, 34, 11. The pairs are
inserted in this order.
(a) Draw the hash table for each insertion?
(b) What is the loading factor after the last insertion?
(c) What is the maximum number of buckets examined in an unsuccessful search of your table?
(d) What is the maximum number of buckets examined in a successful search?
(a) Following table shows keys and their hash values generated by f(k) = k % 17.
key, k
hash value, k%17
7
7
42
8
25
8
70
2
14
14
38
4
8
8
21
4
34
0
11
11
Hash tables are shown for each insertion of the keys (in the given order):
index:
key:
7
7
index:
key:
7
7
8
42
index:
key:
7
7
8
42
9
25
index:
key:
2
70
7
7
8
42
10
11
10
9
25
12
11
10
10
13
12
11
11
14
13
12
12
15
14
13
13
16
15
14
14
16
15
15
16
16
index:
key:
2
70
7
7
index:
key:
2
70
4
38
7
7
8
42
9
25
10
11
12
13
14
14
15
16
index:
key:
2
70
4
38
7
7
8
42
9
25
10
8
11
12
13
14
14
15
16
index:
key:
2
70
4
38
5
21
index:
key:
0
34
2
70
4
38
5
21
7
7
8
42
9
25
10
8
11
12
13
14
14
15
16
index:
key:
0
34
2
70
4
38
5
21
7
7
8
42
9
25
10
8
11
11
12
13
14
14
15
16
8
42
7
7
9
25
8
42
10
9
25
11
10
8
12
11
13
12
14
14
13
15
14
14
447
16
15
16
143
7
6
13
13
1
123
4
3
1234
10
3
12345
3
1
7
7
1
42
8
1
25
8
2
70
2
1
14
14
1
38
4
1
8
8
3
21
4
2
34
0
1
11
11
1
Define a red-black tree? Write the procedures to perform insertion, deletion in a red-black tree?
13
8
13
Black node
17
Red node
17
11
25
15
N
6
22
27
448
A red-black tree is a binary search tree where each node has a color attribute, the value of which is
either red or black. In addition to the ordinary requirements imposed on binary search trees, the
following additional requirements of any valid red-black tree apply:
Root property: The root is black.
External property: All leaves are black, even when the parent is black (The leaves are the null
children.)
Internal property: Both children of every red node are black.
Depth property: Every simple path from a node to a descendant leaf contains the same number of
black nodes, either counting or not counting the null black nodes. (Counting or not counting the
null black nodes does not affect the structure as long as the choice is used consistently.)
Red-Black Tree Insertion
When a node is inserted in the tree it is given the color red. This does not affect the black node count
on any path to a leaf. But it could lead to a single pair of consecutive red nodes in the tree. If the new
node becomes a child of a black node there is no problem. But it may become a child of a red node.
The double red violation will begin at a leaf. The steps below are designed to move the double
violation up toward the root without affecting any path's black node count until it can be eliminated by
bringing down a black from above or it reaches the root where it can be eliminated since the root can
be coloued black without consequence.
Let current refer to the red node that has a red child thereby identifying the location of the violation.
The parent of current will always be black.
1. If current's sibling is red, color black current and its sibling and color parent red. Set current to the
parent of the parent and go to step 4.
2. If current is the right child of its parent, rotate clockwise around current. Otherwise rotate counterclockwise around current. Set current to the new parent of current (the pre-rotation red child of
current). Go to step 3.
3. Current's sibling is black; current's red child is on the opposite side as parent, rotate in the
direction that causes current to become the parent of its pre-rotation parent.
Hence, exchange colous between current and its pre-rotation parent. The algorithm terminates here.
4. If current is the root or the root sentinel, set the root to black and exit; otherwise, repeat.
Red-Black Tree Deletion
Initially current is the node selected for deletion. Suppose it has two children. Then another node is
selected for deletion instead: namely, either a successor of current (a leaf or a node with only a right
child of next greater value), or a predecessor of current (a leaf or node with only a left child of next
smaller value). Either is easy to find. The deleted successor or predecessor is saved until the run
terminates and then it replaces the node originally selected to be deleted. If a successor or predecessor
is necessary, it becomes current when it is found. Due to the use of a successor, only nodes with at
most one child need to be considered for deletion. Assume current initially has at most one child.
1.
2.
3.
If current is a red leaf, delete the leaf; If a successor or predecessor is used, replace the node
selected for deletion with the leaf.
If current is red with one child; this is impossible since the child must be black to avoid two reds
in a row on a path but then the number of blacks on paths through the left side of the node is
different from the number of blacks on paths through the right side.
If current is black with one black; this is impossible since the number of blacks on paths through
the left side of the node is different from the number of blacks on paths through the right side.
4.
5.
7.
449
If current is black with one red child (child must be a leaf); detach current from the tree and make
current's child a child of current's former parent. If current is a successor or predecessor, it
replaces the node selected for deletion. Otherwise current is deleted.
If current is black and has no children, this step may be reached immediately or through Step
[5.1.2] or [5.2.1]. If immediately, current is either the node selected for deletion, or the successor
node, or the predecessor node. In all three cases disconnect it from the tree (that is, replace it in the
tree with a sentinel), but retain the notion of parent, sibling and such as though it is still in the tree
because it may be needed in one or more steps below. If current is the root, just delete it and
terminate.
(5.1.1) If current's sibling is red, exchange the colors of parent and sibling.
(5.1.2) If the sibling is to the left of the node, rotate clockwise around the parent, otherwise
rotate counter-clockwise around the parent.
(5.2.1) If current's sibling is black with two black children, make the sibling red.
(5.2.2) Otherwise, current is red. Make it black.
8.
450
is a kind of paradigm
organizes code into classes that each contain procedures for manipulating instances of that
class alone
The first step in OOP is to identify all the objects you want to manipulate and how they relate
to each other, an exercise often known as data modeling. Once we have identified an object, we
generalize it as a class of objects and define the kind of data it contains and any logic sequences
that can manipulate it. Each distinct logic sequence is known as a method. A real instance of a
class is called an "object" or, in some environments, an "instance of a class." The object or class
instance is what we run in the computer. Its methods provide computer instructions and the class
object characteristics provide relevant data. We communicate with objects - and they
communicate with each other - with well-defined interfaces called messages.
An object is an abstract definition of something, identified by two elements: attributes (data)
and behaviour. First, an object has attributes (sometimes called states or properties), which
describe the object. Second, an object has behaviour, called methods or functions which describe
what the object can do. For example, a car is an object of class, motor-vehicle. The car has
possible attributes such as make, model, year, color, and speed with possible behaviours:
accelerate, brake, honk and so on.
(b) To be truly considered "object oriented", a programming language should support at a minimum
four characteristics.
Encapsulation aids the software designer by enforcing information hiding. Objects encapsulate
data and the procedures for manipulating those data. In a sense, the object hides the details of the
implementation from the user of that object. Encapsulation enables us to create objects that are
self-contained. When combined with polymorphism, encapsulation enables us to create objects
that are self-sufficient, and, therefore easy to reuse. Encapsulation enables us to create black box
functionality (input and output are known, but the process is not known).
Polymorphism: Poly means many and morph means form. A polymorphic program is one that can
take many forms the program is able to adapt automatically. Polymorphism refers to the fact that
a single operation can have different behaviour in different objects. In other words, different
objects react differently to the same message. For example, we can use the expression x + y to
denote the sum of x and y, for many different types of x and y: integers, floating-point numbers,
and complex numbers. We can even define the + operation for two strings to mean concatenation
of the strings.
Inheritance: In object-oriented programming, inheritance is the concept that when a class of
objects is defined, any subclass that is defined can inherit the definitions of one or more general
classes. This means for the programmer that an object in a subclass need not carry its own
definition of data and methods that are generic to the class (or classes) of which it is a part. This
not only speeds up program development; it also ensures an inherent validity to the defined
subclass object (what works and is consistent about the class will also work for the subclass). We
study in detail about inheritance later.
Dynamic binding: Objects could come from anywhere, possibly across the network. You need to
be able to send messages to objects without having to know their specific type at the time we write
our code. Dynamic binding provides maximum flexibility while a program is executing.
2.
What is operator overloading? Explain how to overload the operators in C++ by taking any five
operators.
451
You can redefine or overload the function of most built-in operators in C++. These operators can be
overloaded globally or on a class-by-class basis. Overloaded operators are implemented as functions
and can be member functions or global functions.
An overloaded operator is called an operator function. You declare an operator function with the
keyword operator preceding the operator. Overloaded operators are distinct from overloaded functions,
but like overloaded functions, they are distinguished by the number and types of operands used with
the operator.
Creating a Member Operator Function
A member operator function takes the following general format:
return-type class-member::operator#(arg-list)
{
...
}
return-type: any valid type
#: placeholder
The following program creates a class point, which stores x and y coordinate values. It overloads the +
operator relative to this class.
#include <iostream>
Using namespace std;
class point
{ int x, y;
public:
point() {}
// Constructors
point(int a, int b) { x = a; y = b;
void show()
{ cout << x << ", " << y << "\n"; }
point operator+(point op2);
// operator function
};
point point::operator+(point op2) // Overload + for point
{
point tmp;
tmp.x = x + op2.x;
tmp.y = y + op2.y;
return tmp;
}
void main()
{
point ob1(10, 20), ob2( 5, 30);
ob1.show();
// displays 10, 20
ob2.show();
// displays 5, 30
ob1 = ob1 + ob2; // alternatively (ob1+ob2).show();
ob1.show();
// displays 15, 50
}
452
The operator+() has only one parameter even though it overloads the binary + operator. We might
expect two parameters corresponding to the two operands of a binary operator +. The operator+()
takes only one parameter, the operand on the left side of the + (op2.x) is passed implicitly to the
function through the this pointer. The operand on the right is passed in the parameter op2. When
binary operators are overloaded, it is the object on the left that generates the call to the operator
function.
point point::operator-(point op2)
// Overload - for point
point point::operator=(point op2) // Overload = for point
point point::operator++()
// Overload prefix ++ for point
{ x++;
y++;
return *this;
}
3.
(a)
(b)
(a)
(b)
Write a program to change the case of each word in a file to initial capitals.
Write a program to concatenate the two given strings?
This question relates to FILE I/O not included in the current syllabus.
#include <iostream>
// Concatenation of two strings
using namespace std;
int main()
{
char str1[80] = "Annamacharya Institute of ";
char str2[50] = "Technology & Sciences";
int i, j, n1, n2;
n1 = strlen(str1);
n2 = strlen(str2);
for( i = n1, j=0; j < n2; i++, j++ )
str1[i] = str2[j];
cout << str1 << endl;
return 0;
}
4.
Define the Abstract data type for Stack. Write a C ++ program to implement stack ADT using arrays.
A Stack is an Abstract Data Type (ADT) that supports the following functions:
push(obj):
453
454
// insert a record
// extract the key
// hash the key
// return item
// go to next cell
// wraparound if necessary
}
return dummy; // cannot find item
}
void HashTable::reorganize()
{
for(int i=0; i<arraySize; i++)
{ hashArray[i].empCode = null;
neverUsed[i] = true;
}
for(i=0; i<arraySize; i++)
{ if( remain[i].empCode != null )
insert(remain[i]);
}
cout << "\n Reorganized Table: \n";
displayTable();
}
EmpRecord HashTable::find(int key)
// find item with key
{ EmpRecord dummy; dummy.empCode=null;
int hashVal = hashFunc(key); // hash the key
while(hashArray[hashVal].empCode != null) // until empty cell,
{
if(hashArray[hashVal].empCode == key)
// found the key?
return hashArray[hashVal];
// yes, return item
++hashVal;
// go to next cell
hashVal %= arraySize;
// wraparound if necessary
}
return dummy;
// cannot find item
}
void HashTable::displayTable()
{
string trueFalse;
cout << "<< Table >>" << endl;
for(int i=0; i<arraySize; i++)
{ if( neverUsed[i] ) trueFalse = "true";
else trueFalse = "false";
cout<<i<<": "<<hashArray[i].empCode<<" "<< trueFalse<<endl;
}
}
int main()
{ EmpRecord item;
int aKey;
int recKey[] = {12,17,21,28,35,40,43,52,66,69};
HashTable h(13);
// create table with size = 13
for(int i=0; i < 10; i++)
// insert 10 records
{
item.empCode = recKey[i];
h.insert(item);
}
h.displayTable();
// searching a record
aKey = 65;
item = h.find(aKey);
if(item.empCode != null) cout << aKey << " found"<<endl;
else cout << aKey << " not found"<<endl;
// delete 5 records
h.erase(43);
h.erase(21);
h.erase(69);
h.erase(28);
h.erase(12);
h.erase(35);
return 0;
}
455
456
6. (a) What is an AVL search tree? How do we define the height of it? Explain about the balance factor
associated with a node of an AVL tree.
(b) Explain how an AVL tree can be used to sort a sequence of n elements in O(n log n) time.
This question is repeated. Refer set-3 of section C.1.
7. (a) Prove that net T be a B-tree of order m and height h. Let d = [m/2] and let n be the number of
elements in T.
i. 2d h 1 1 n m n 1
ii.
log m ( n + 1) h log d (
n +1
) +1
2
457
10
11
12
13
14
a
a
458
root
14,14
7, 7
3, 3
0, 0
8, 14
4, 4
2, 4
6, 14
5, 14
14,14
5, 14
14,14
1, 1
2, 4
5, 14
14,14
2, 4
5, 14
6, 14
8, 14
10, 10
6, 14
10,10
Example:
class myclass
{ int a, b;
public:
myclass(int i, int j)
{a=i; b=j;}
void show()
// function
{cout << a << " " << b;}
};
459
460
The functions isEmpty(), push(), and pop() are called member functions because they are part of
the class Stack. The variables a and top are called member variables (or data members). An
object forms a bond between code and data. Only member functions have access to the private
members of their class. Thus, only isEmpty(), push(), and pop() may access a and top. Once we
have defined a class, we can create an object of that type by using the class name. In essence, the
class name becomes a new data type specifier. For example, this creates an object called mystack
of type Stack:
Stack mystack;
When we declare an object of a class, we are creating an instance of that class. In this case,
mystack is an instance of Stack class.
(c) By default, functions and data declared within a class are private to that class and may be
accessed only by other members of the class. The public access specifier allows functions or data
to be accessible to other parts of a program. The protected access specifier is needed only when
inheritance is involved. Once an access specifier has been used, it remains in effect until either
another access specifier is encountered or the end of the class declaration is reached. We may
change access specifications as often as we like within a class declaration. For example, we may
switch to public for some declarations and then switch back to private again.
Consider the class Stack. Within the class, the functions isEmpty(), push(), and pop() can
access the private data members (a and top) of the class. They are not accessible to main()
function where we create the mystack object. We can access the functions as they are public. For
example, the statement in the main() function
cout <<
mystack.top;
2. (a) What is multiple inheritance? Write a program to illustrate the concept of multiple inheritance.
(b) What is hybrid inheritance? Write a program to illustrate the concept of hybrid inheritance.
(a) Multiple inheritance: A derived class may inherit two or more base classes. The following
example illustrates how derived inherits both base1 and base2.
base1
base2
derived
#include <iostream>
Using namespace std;
class base1
{ protected: int x;
public:
void showx() { cout << x << endl; }
};
class base2
461
protected: int y;
public:
void showy() {cout << y << endl;}
};
(a) Write a program that reverses the order of the characters in a string.
(b) A palindrome is a word or group of words that read the same forward and backward. For example
madam or wow. Write a program that takes a string argument from the command line and
prints whether the string was a palindrome or not.
(a) #include <iostream>
462
// argv[0]: program-name,
argv[1]: string
strcpy(str, argv[1]);
int n = strlen(str);
bool isPalindrome = true;
for(int i=0; i<n/2; i++)
if( str[i] != str[n-i-1] )
{ isPalindrome = false;
break;
}
if(isPalindrome)
cout<< "string: "<< str << " is a palindrome" << endl;
else
cout<< "string: "<<str<<" is not a palindrome"<< endl;
return 0;
}
madam
Write a method in C++ to join two doubly linked lists into a single doubly linked list. In a join the
elements of the second list are appended to the end of first list.
Pointer variables head and tail denote left-most and right-most node addresses of the doubly linked list,
respectively. The left and right links of a node are denoted by pointer variables prev and next,
respectively. The following figure illustrates the concatenation of two lists. The formal procedure is as
follows:
1.
2.
3.
head
x A
tail2
head2
D x
463
5.
Use linear probing, a hash table with b=17 buckets, and the hash function f(k) = k% b; Start with an
empty hash table and insert pairs whose keys are 7, 42, 25, 70, 14, 38, 8, 21, 34, 11. The pairs are
inserted in this order.
(a) Draw the hash table for each insertion?
(b) What is the loading factor after the last insertion?
(c) What is the maximum number of buckets examined in an unsuccessful search of your table?
(d) What is the maximum number of buckets examined in a successful search?
This question is repeated. Refer set-2 of this section.
6.
Define a class called binarySearchTree to represent a binary search tree. Extend this class by adding a
public method outputInRange (Low,high) that outputs, in ascending order of key, all elements in a
binary search tree whose key lies between Low and High. Use recursion and avoid entering sub trees
that cannot possibly contain any elements with keys in desired range.
This question is repeated. Refer set-1 of this section C.2.
7.
464
Zig rotation
Zag rotation
Type 2 (Zig-zig Step): This step is done when p is not the root and x and p are either both right
children or are both left children.
p
A
C
A
B
C
Type 3 (Zig-zag Step): This step is done when p is not the root and x is a left child and p is a right
child or vice versa.
8.
A
B