Você está na página 1de 21

Advanced Data Structures and Algorithms in C++

465

C.3 Set No. 1


1.

(a) Each class has some special member-functions, which calls can be inserted by the compiler into a
code without explicit instruction of the programmer. Enumerate such functions, members and
cases, when implicit calls can arise.
(b) If, when creating a variable the programmer explicitly did not initialize it, in some cases, the
compiler itself would give it a certain, predefined initial value, and in some cases the initial value
would be unpredictable. What does it depend on?
(a) Following are the some special member-functions - calls can be inserted by the compiler into a
code without explicit instruction.
Inline function is declared inline using the inline keyword or by being a member function
defined in-class. Compilers are encouraged to generate inline code rather than function calls for
inline functions. Most benefits from in-lining comes with very short functions
Destructor is a member of a class used to clean up before deleting an object. Its name is its
class name prefixed by '~'. For example, MyClass's destructor is ~MyClass(). Destructor is often
used to release resources. A destructor is implicitly called whenever an object goes out of scope or
is deleted.
Virtual destructor is a destructor declared virtual to ensure that the proper derived class
destructor is called if an object of a derived class is deleted through a pointer to a base class. If a
class has any virtual functions, it should have a virtual destructor.
Default constructor is a constructor requiring no arguments - used for default initialization.
The following program illustrates a default constructor, a destructor, and an inline function.
These are implicitly called.
#include <iostream>
using namespace std;
class A
{ int a;
public:
void func() { cout << a << endl; }
void setval(int i) { a = i; }

// destructor
~A() { cout << "Destructing..." << endl; }
// inline function
inline int sqr();
};
int A::sqr()
{ return a*a; }
int main()
{ A ob;
// object ob is created by default constructor
ob.setval(5);
ob.func();
cout << ob.sqr() << endl;
return 0;
}

466

Advanced Data Structures and Algorithms in C++

Output of this program is:


5
25
Destructing...

(b) If the programmer does not initialize the local variables, they will be initialized with garbage
values. The following program prints the unpredictable values.
#include <iostream>
using namespace std;
int main()
// local variables
{ int a;
float x;
char ch;
cout << "Integer: "<< a;
cout << "Float: "<< x;
cout << "Character: "<< ch;
return 0;
}

If we use static modifier for the local variables or declared as global variables, the numeric
variables will be initialed to zero and character variables to NULL.
// declared with static modifier
int main()
{ static int a;
static float x;
static char ch;
. . .
}

2.

// global variables
int a;
float x;
char ch;
int main()
{ . . .
}

(a) When should my destructor be virtual?


(b) What is a virtual constructor?
(c) What is the difference between how virtual and non-virtual member functions are called?
This question is repeated. Refer set-1 of section: C.2 JNTU November 2006.

3.

(a) Explain about try, catch, throw keywords in C++.


(b) Write a program to illustrate the exception handling mechanism in C++.
This question is repeated. Refer set-1 of section: C.2.

4.

(a) What is a linked list / chain. Write the class header for the class chain.
(b) Write the program which gives the constructor and copy constructor for chain.
(a) A linked list abstract data type (ADT) is a list or chain of items where each item refers to the next
one in the list. Each item in a linked list is called a node. Each node contains the data and also the
location of the next item (called as a link). The content of the link varies depending upon

Appendix C: Questions and Answers

467

implementation. If the list is implemented by arrays, then the link field will be the index of the
array; if implementation is through object references, the link will contain the memory address.
The following figure is a schematic diagram of a linked list with four nodes. Each node is
pictured with two parts. The left part represents the information part of the node, which may
contain one or more data items. The right part represents the next field of the node (object
reference) and there is an arrow drawn from it to the next node in the list. This follows the usual
practice of drawing an arrow from a field to a node when the address of the node appears in the
given field. The object reference of the last node contains a special value, called the null. The
null signals the end of the list. In C++, each reference variable either locates an object or is null.
The linked list also contains a list reference variable called start or head that contains the
address of the first node in the list.
head

info next

info next

info next

info next

ADA

RAM

JIM

GOPI null

Node-1

Node-2

Node-3

Node-4

template <class T> // class header of a linked list


class LinkedList
{
private:
struct Node
{ T data;
// data item
Node *next; // points to next node in the list
};
Node *head;

// head points to first node

public:
// constructor
LinkedList();
LinkedList(const LinkedList<T> &);

// copy constructor

//. . . member functions


}

(b) template <class T>


// constructor
LinkedList<T>::LinkedList()
{ head = NULL; }
template <class T>
LinkedList<T>::LinkedList(const LinkedList &ob) // copy constructor
{
Node *p = new Node;
p = ob.head;
head = p;
}
Create Linked list object, list by using default constructor (in the main() function)
LinkedList<int> list;

468

Advanced Data Structures and Algorithms in C++

Create another Linked list object, list2 by using copy constructor.


LinkedList<int> list2(list);

5. Develop a class for hash table using linear probing and neverUsed concept to handle an erase operation.
Write complete C++ code for all the methods. Include a method to reorganize the table when (say)
60% of the empty buckets have never used equal to false. The reorganization should move pairs
around as necessary and leave a properly configured hash table in which neverUsed is true for every
empty bucket.
This question is repeated. Refer set-3 of section C.2.
6.

What is an AVL tree? Explain about the different rotation patterns in AVL trees for balancing with
appropriate examples?
An AVL tree is a binary search tree in which the heights of the left and right subtrees of the root differ
at most by 1 and in which the left and right subtrees are again AVL trees. Each node of an AVL tree is
associated with a balance factor, that is the left subtree has height greater than, equal to, or less than
that of the right subtree.
An additional field stores the balance factor of a node which denotes the difference of height
between the left and right subtrees of the tree rooted at that node. Balance factor of a node in an AVL
tree can take one of the three possible values: {-1, 0, 1}. The following figure shows an AVL tree and a
non-AVL tree.
A tree rotation is a local operation on a binary search tree that changes the structure preserving inorder traversal key ordering. A tree rotation moves one node up in the tree and one node down. They
are used to restore the balance of the tree, and in particular to decrease its height by moving smaller
subtrees down and larger subtrees up, resulting in improved performance of many tree operations.
0

10
0

20
0

30

60

40
0

50

70
0

90
0

80

95

10
0

20
1

30
0

AVL Tree

60

40
1

50

70
0

90
0

95

80

45

Non-AVL Tree

There are four cases in all, choosing which one is made by seeing the first two nodes from the
unbalanced node to the newly inserted node and matching them to the top most row of the following
diagrams. Root is the initial parent before a rotation and pivot is the child to take the roots place. A, B,
C, and D are the subtrees.

Appendix C: Questions and Answers

Root

(a) Left-Left Case


Right Rotation

Pivot

5
A

3
B

2
D

(b) Right-Right Case

5
C

Root

Left Rotation

Pivot

5
B

7
B

(c) Left-Right Case


Root

5
Root

Pivot

Pivot

4
C

Left Rotation

5
C

Right Rotation

(d) Right-Left Case


3
A
Pivot

3
5

Root

4
C

Right Rotation

Root

4
D

Pivot

5
C

3
B

Left Rotation

5
D

469

470
7.

Advanced Data Structures and Algorithms in C++

What is a balanced search tree? Describe different types of balanced search trees with an example.
A search tree with logarithmic height is called a balanced search tree.
Balanced search trees:
An AVL tree is a binary search tree in which the heights of the left and right subtrees of the root differ
at most by 1 and in which the left and right subtrees are again AVL trees. Each node of an AVL tree is
associated with a balance factor, that is the left subtree has height greater than, equal to, or less than
that of the right subtree.
0

10
0

60
1

20
0

40
0

30

50

90
0

70
0

95

80

AVL Tree

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.
13
8

17

11

25

15

13

Black node

17

Red node

N
6

22

27

Null black node

A red-black tree

A B-tree of order m is an m-way tree in which


1. All leaf nodes are on the same level.
2. All nodes, except the root and the leaves, have between [m/2] and m children.

Appendix C: Questions and Answers

3.
4.
5.

471

The nonleaf nodes store up to m-1 keys to guide the searching; and these keys partition the keys in
the children in the fashion of a search tree.
The root is either a leaf or has between two and m children.
If a node has d number of children, then it must have d-1 number of keys.
46

22

12 15 20

32

52

40

23 27 29

33 36

41 43

47 49 51

56

53 55

57 59

A 4-way search tree

8.

(a) Explain the compressed trie with an example.


(b) How will the KMP algorithms behave if the pattern and/or the text are null (have length zero)?
Will they crash? If not, will their output be meaningful and correct?
(a) The trie (standard trie) for a set of strings S is an ordered tree such that:
Each node but the root is labeled with a character.
The children of a node are alphabetically ordered.
The paths from the external nodes to the root yield the strings of S.
Example: Standard trie for the set of strings,
S = {BEAR, BELL, BID, BULL, BUY, SELL, STOCK, STOP}

B
E

U
L

Standard trie

A standard trie will most likely waste a fair bit of space if there is not a large collection of
words or if the alphabet is large. One way to reduce the space is to use a compressed trie.
Compressed tries remove redundant nodes and replace them with strings instead of characters
A node is redundant if it only has one child and is not the root.

472

Advanced Data Structures and Algorithms in C++

A compressed trie has internal nodes of degree at least two. It is obtained from a standard
trie by compressing chains of redundant nodes. The above figure shows a standard trie, and it is
converted into a compressed trie (shown below).

B
E
AR

S
U

ID
LL

LL

ELL

Y
C

Compressed trie

(b) The KMP algorithm compares the pattern to the text in left-to-right. If the pattern matches with the
text, then the algorithm returns true; otherwise false. Consider the following examples:
T, text = MISSISSIPPI

P, pattern = SSIPP

As the pattern matches with the text, the algorithm returns true.
T = MISSISSIPPI

P = MIPPI

In this case, the pattern does not match with the text, so KMP returns false.
T = MISSISSIPPI

P =

In this example, the pattern is null. As the text is terminated with null (the last character of the text
is null), the pattern with null matches with the text, and KMP does not crash and returns true.
T =

P = SS

In this case, the text is null whereas the pattern contains SS. The pattern does not match with the
text, so KMP returns false without crashing.
T =

P =

If both text and pattern are null, the null pattern matches with the null text, KMP returns true with
normal termination.

C.3 Set No. 2


1.

(a) Can you think of a situation where your program would crash without reaching the breakpoint
which you set at the beginning of main()?
(b) When are copy constructors called?
(c) Can a copy constructor accept an object of the same class as parameter, instead of reference of the
object?

Appendix C: Questions and Answers

473

(a) Consider the following short program:


#include <iostream>
using namespace std;
class myclass
{ int a, b, c;
public:
myclass(int x, int y)
{ a=x;
b=y;
c=a/b;
}
};
myclass ob(5, 0);
int main()
{
cout << "Normal termination..." << endl;
return 0;
}
Suppose we set the breakpoint at the beginning of the main(), execution of program will stop at
the statement: c=a/b because it is a runtime error division by zero.

(b) When an object is passed to a function, a copy of that object is made and this copy becomes the
parameter in the function. When the function terminates, the copy of the argument (i.e., the
parameter) is destroyed. When a copy of an argument is made during a function call, the normal
constructor is not called. Instead, the objects copy constructor is called. If a class does not
explicitly define a copy constructor, as is the case here, then C++ provides one by default. The
default copy constructor creates an identical copy of the object.
(c) This question is repeated. Refer set-1 of section C.2.
2.

(a) Explain the need for Virtual Destructor.


(b) Can we have Virtual Constructors?
This question is repeated. Refer set-3 of section C.1.

3.

(a) Write a program to merge the contents of two given files?


(b) Write a program to count the number of lines in the given file?
This question is repeated. Refer set-2 of section C.1.

4.

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.
This question is repeated. Refer set-4 of section C.2.

5. (a) What is the structure to represent node in a skip list. Write the constructor for a skip list.
(b) Write a method in C++ to erase a pair in the dictionary with key theKey in a skip list
representation. What is the complexity of this method?
This question is repeated. Refer set-1 of section C.1.

474
6.

Advanced Data Structures and Algorithms in C++

Define a binary search tree? Write the procedures to perform insertion, deletion and searching in a
binary search tree?
A binary search tree is a binary tree that is either empty or in which every node contains a key and
satisfies the conditions:
1. The key in the left child of a node (if it exists) is less than the key in its parent node.
2. The key in the right child of a node (if it exists) is greater than the key in its parent node.
3. The left and right subtrees of the root are again binary search trees.
The binary search tree is represented by a linked binary tree structure. Each node of the tree has
three fields: data, left and right. The ptr is a pointer variable that points to the current node of the tree.
Procedure Search:
Input: root of the binary search tree and node (key) to be searched.
Output: returns key node if search is successful; otherwise NULL.
1. ptr = root
2. Repeat the steps 3 to 5, while( ptr NULL )
3. If key = ptr->data, then return ptr
// search is successful
4. Else if key < pt->data, then ptr = pt->left
// move to the left subtree
5. Else ptr = ptr->right
// move to the right subtree
6. Return NULL.
// search is unsuccessful
Procedure Insert (recursive method):
BSTNode *insertTree(BSTNode *root, int key)
{ if(root == NULL )
root = new BSTNode(key);
else if( key < root->data)
root->left=insertTree(root->left,key);
else
root->right=insertTree(root->right,key);
return root;
}

// insert into an empty tree


// if new node < node,
// insert into left subtree
// if new node is node,
//insert into rt. subtree

Deleting a node is the most complicated common operation required for binary search trees. We start
by finding the node we want to delete. Once we have found the node, there are three cases to consider.
1. The node to be deleted has no children.
2. The node to be deleted has one child.
3. The node to be deleted has two children.
Case-1:
If node has no children
If parents left child is node to be deleted,
Set parents left link NULL
If parents right child is node to be deleted,
Set parents right link NULL
Case-2:
If node (p) has right child
If parents left child is = p,

Appendix C: Questions and Answers

475

Set parents left link to right link of p


If parents right child is = p,
Set parents rt. link to rt. link of p
If node (p) has left child
If parents left child is = p,
Set parents left link to left link of p
If parents right child is = p,
Set parents right link to left link of p
Case-3:
Find the inorder successor of the node to be deleted and also the parent of this inorder successor.
Replace the node to be deleted by its inorder successor.
Delete the inorder successor by making either left or right link of the parent to NULL.
7. (a) Draw the order 7 B-tree resulting from inserting the following keys into an initially empty tree T:
4, 40, 23, 50, 11, 34, 62, 78, 66, 22, 90, 59, 25, 72, 64, 77, 39, 12
(b) Write pseudo code for Right Rotate in red-black tree.
(a) B-Tree (order 7)
(1) After adding 4, 40, 23, 50, 11, 34

4 11 23 34 40 50

(2) Addition of 62 causes split

34

4 11 23

(3) After adding 78, 66, 22, 90

40 50 62

34

4 11 22 23

(4) Addition of 59 causes split


of right child node
4 11 22 23

(5) After adding 25, 72, 64, 77


we have
4 11 22 23 25

(6) After adding 39, 12

4 11 12 22 23 25

40 50 62 66 78 90

34 62

40 50 59

66 78 90

34 62

40 50 59

64 66 72 77 78 90

34 62

39 40 50 59

64 66 72 77 78 90

476

Advanced Data Structures and Algorithms in C++

(b) Pseudo code for Right Rotate in red-black tree:


P is the left child of Q. A, B, and C are subtrees. Right rotation is performed with Q as the root.
Q
P

Let P be Q's left child.


Set P to be the new root.
Set Q's left child to be P's right child.
Set P's right child to be Q.
Set A as the left child of P.
Set B as the left child of Q.
Set C as the right child of Q.
8.

(a) Explain the KMP flow chart for the pattern ABAABA where {A, B, C}
(b) Explain the complexity of Brute Force pattern matching algorithm.
(a) The KMP flowchart is not included in the current syllabus.
(b) Complexity of Brute Force pattern matching algorithm:
This is a simple method to string matching. It starts the comparison of P (string pattern) and T
(text string) from the first character of T and the first character of P. If there is a mismatch, the
comparison starts from the second character of T, and so on.
m = pattern length
n = text length
The running time of Brute Force pattern matching algorithm is not efficient in the worst case.
For each character index in T, we have to perform up to m character comparisons to find that P
does not match T at the current index. The outer while-loop is performed at most n-m+1 times, and
the inner loop is executed at most m times. Hence, the running time of the algorithm is O((nm+1)m), which is O(nm). The algorithm has a quadratic running time O(n2), when m = n/2.

C.3 Set No. 3


1. (a) Explain the need for OOP? And also explain the principles of Object Oriented Programming?
(b) Explain the differences between procedural languages and Object Oriented languages.
(a) As complexity of programs is increasing, OOPs are needed for creation of large complex
applications e.g., word processor or spreadsheet. It is easy to maintain, modify, and debug the
OOP.
The principles of OOPs are Polymorphism, Encapsulation, and Inheritance. Reuse is
considered to be the fourth feature that helps define an OOP. Reuse is simply the capability to use
the same code easily in multiple programs without substantial rewriting. If we effectively
implement the three key features, we will automatically obtain reuse.

Appendix C: Questions and Answers

477

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. In C++, polymorphism is
achieved in two ways by overloading functions at compile-time and by virtual functions at
runtime.
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.
Data Abstraction & Encapsulation: Combining data and functions into a single unit is called
encapsulation. Data encapsulation is the most striking feature of a class. The data is not accessible
to the outside world. Those functions which are combined in the class can access the data. These
functions provide the interface between the objects data and the program. This insulation of data
from direct access by the programmer is called data hiding.
Inheritance is the capability to create new objects that expand the characteristics of existing
objects. For example, a square object contains length of a side and a function to compute the area
of the square. Using inheritance, we can extend the square into a cube. Actually the cube object
inherits the square object. All of the characteristics of the square object become part of the cube.
The cube object modifies the existing area function to return the volume of the cube.
(b) Differences between procedural languages and Object Oriented languages.
Procedure-Oriented Programming

Object-Oriented Programming

The programmer must handle all possible


input and output, writing code to handle all
input and formatting output.

The object is responsible for its own input and output,


protecting the variables from invalid values and
appropriately formatting output.

Debugging can often be difficult, especially


finding errors because of changes to code.

Debugging is much easier. Since the object is programmed


to behave in a specific way, it is easy to pinpoint errors.

It is difficult to reuse procedures in other


code, since they are usually program
specific.

It is easy to reuse code. Once an object is defined, it can be


used in any program.

Data abstraction is hard to implement.

Data abstraction is easy to implement.


Principle of data hiding helps the programmer to build
secure programs that cannot be invaded by code in other
parts of the program.
It is possible to have multiple instances of an object to coexist without any interference.
Software complexity can be managed it is easy to
partition the work in a project based on the objects.
Object-oriented systems can be easily upgraded from small
to large systems.
Message passing techniques for a communication between
objects makes the interface descriptions with external
systems much simpler.

2.

(a) What are the different types of polymorphism?


(b) What are virtual functions? How to implement virtual functions in C++.
This question is repeated. Refer set-2 of section C.1.

478
3.

Advanced Data Structures and Algorithms in C++

(a) Write a program to replace a word with an other word in a given file?
(b) Write a program to count the number of occurrences of a word in a given file?
This question relates to FILE I/O not included in the latest syllabus of R07.

4.

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 the first list.
This question is repeated. Refer set-4 of section C.2.

5.

(a) Explain about the skip list representation of dictionary with an example?
(b) What are the data members of skipList class? Write the constructor for skipList.
This question is repeated. Refer set-1 of section C.2.

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 the section C.2.

7. (a) What is the maximum number of disk accesses needed to delete an element that is in a non-leaf
node of a B-tree of order m.
(b) Write insertion algorithm of red-black tree. Also analyze its time complexity
(a) This question is repeated. Refer set-4 of the section C.1..
(b) Refer Q# 6 of set-2 in section C.2.
8.

(a) Explain the compressed trie with an example.


(b) How will the KMP algorithms behave if the pattern and/or the text are null (have length zero)?
Will they crash? If not, will their output be meaningful and correct.
(a) This question is repeated. Refer set-1 of this section C.3.
(b) This is a repeated question. Refer set-1 of this section.

C.3 Set No. 4


1. (a) Can you think of a situation where your program would crash without reaching the breakpoint
which you set at the beginning of main()?
(b) When are copy constructors called?
(c) Can a copy constructor accept an object of the same class as parameter, instead of reference of the
object?
This question is repeated. Refer set-2 of this section C.3.
2.

(a) Explain the need for Virtual Destructor.


(b) Can we have Virtual Constructors?
This question is repeated. Refer set-3 of the section C.1.

Appendix C: Questions and Answers

3.

479

(a) What are some ways try / catch / throw can improve software quality?
(b) How can we handle a constructor that fails?
(c) How can we handle a destructor that fails?
(a) It is better to have less number of if statements in the program. We will not have to check for
errors each time we call a function. Conditional statements are known to contain more bugs than
other statements. With less if tests, we will have better and faster software.
Conditional statements are error-prone because they are used to handle complicated scenarios,
where an action can result in many different outcomes, which affect the next actions. In order to
make errors less probable, one has to simplify the model of the desired behaviour of the software.
The problem is the complexity that leads to if statements, not the if keyword, and using different
keywords is not going to solve the problem by itself.
Exceptions are supposed to simplify the error handling model based on the assumption that in
most cases, a function that detected an error cannot handle it, and has to propagate it to the caller.
Finally, a "high-level" enough caller is reached and actually makes a decision (pops up an error
message, tries a different action, etc.).
(b) Constructors do not have a return type, so it is not possible to use return codes. The best way to
signal constructor failure is therefore to throw an exception. We should throw the exception to let
the invoker know that the instance of the object has not been created successfully, so that the
invoker can perform appropriate actions.
We have to be careful when throwing exceptions in a constructor - that is, if we want clean
running code. Constructors should also return valid objects but sometimes cannot, due to errors.
The best way to deal with something of this scenario is to present clear error messages that let the
user know what they failed to give or present, and second is to use the finally clause. The finally
clause will give about the only way to "clean up" your code after failure, although we must be
careful here also. Using the finally clause means that it will execute every time the code is run,
which means some kind of flag must be created for proper cleanup.
(c) It is better to write a message to a log-file instead of throwing an exception. The C++ rule is that
we must never throw an exception from a destructor that is being called during the stack
unwinding process of another exception. For example, if someone says throw a function(), the
stack will be unwound all the stack frames between the throw [...]. The problem is that
destructors are called when exceptions are thrown so that functions propagating errors to their
callers can clean up resources. The destructor can also be called in such a situation. And when an
exception is already thrown, throwing another one will result in a call to terminate(), killing
the process.

4.

(a) What is a disjoint set? Define the ADT for a disjoint set.
(b) Write algorithms for the Union and find operations of disjoint sets.
(a) A disjoint-set is a data structure or a collection, C = {S1, S2 Sn} of distinct dynamic sets. Each
set is identified by a member of the set, called representative.
Some applications involve grouping n distinct objects into a collection of disjoint sets. Two
important operations are then finding which set a given object belongs to and uniting the two sets.
A disjoint set ADT supports the following operations: Suppose x and y are elements.
MAKE-SET(x): Creates a new set {x}. x must not be in any other set.
UNION(x, y): Combines the set that contains x with the set that contains y.
FIND-SET(x): FIND-SET(x)= FIND-SET(y) when x and y are in the same set.

480

Advanced Data Structures and Algorithms in C++

Example of disjoint set: Determine the connected components of an undirected graph, G = {V, E}.
Connected-Components(G)
1. for each vertex v V[G]
2.
do Make-Set (v)
3. for each edge (u, v) E[G]
4.
do if Find-Set(u) Find-Set(v)
5.
then Union((u, v)

Same-Component((u, v)
1. if Find-Set(u) = Find-Set(v)
2.
then return TRUE
3. else return FALSE

(b) Disjoint sets are implemented by linked lists. Each set is represented as a linked list, with head and
tail; and each node contains data value, next node pointer and back-to-representative pointer.
tail

head

Set {A, B, C}
size = 3

C X

B
tail

head

Set {P, Q}
size = 2

Q X

tail

head

UNION of
two sets

MAKE-SET(x)
head[x] = x
tail[x] = x
next[x] = NULL
size[x] = 1
FIND-SET(x)
return head[x]

5.

C X

UNION(x, y)
x = FIND-SET(x)
y = FIND-SET(y)
if size[x] < size[y]
then swap (x, y)
next[tail[x]] = y
tail[x] = tail[y]
size[x] = size[x] + size[y]
while y NULL
do head[y] = x
y = next[y]

(a) What is linear probing? Write a C++ program that gives the data members and constructors for the
hash table class that uses linear probing.
(b) Write the C++ program that gives the method search of a hash table.
(a) Linear probing is a collision-resolution technique: A hash table in which a collision is resolved by
putting the item in the next empty place in the array following the occupied place.
Suppose a small company with 12 employees assigns 2-digit employee numbers to each
employee. We map these 12 employee record keys into 13 two-digit table indices: 00, 01, 02 11,
12. We apply division method of hashing for employee numbers. We choose a prime number M
(table size) close to 12, such as M = 13. Now the table index range is 0 to 12 (0 to M-1).

Appendix C: Questions and Answers

481

Initially, we insert 10 employees into the hash table. So we take an array size of 13 (a prime
number). In linear probing we search sequentially for vacant cells. For example, the employee
number 17 is occupied in the cell 4. When we try to insert 69 which maps to index 4 of the table,
we go to 5, then 6, and so on, incrementing the index until we find an empty cell. This is called
linear probing because it steps sequentially along the line of cells. As cell 4 is not empty, we place
69 at index 6.

Table index
key
Hash value

0
52
0

1
40
1

2
28
2

3
66
1

4
17
4

5
43
4

6
69
4

8
21
8

9
35
9

10

11

12
12
12

We generate hash values for 10 employee numbers (keys) using division method (hash value
or table index = key mod 13) and insert them into hash table.
C++ code that gives the data members and constructors for the hash table class that uses linear
probing:
struct EmpRecord
{ int empCode;

// key
// other data may be included here

};
class HashTable
{ private:
EmpRecord *hashArray;
int arraySize;

// array holds hash table


// size of hash table

public:
// constructor
HashTable(int size);
int hashFunc(int key);
void insert(EmpRecord item);
EmpRecord delet(int key);
EmpRecord search(int key);
void displayTable();
};

// constructor
HashTable::HashTable(int size)
{ hashArray = new EmpRecord[size];
arraySize = size;
for( int i=0; i<arraySize; i++) // initialize hashArray with null = -1
hashArray[i].empCode = null;
}

(b) EmpRecord HashTable::search(int key)


{

EmpRecord dummy;
dummy.empCode=null;
int hashVal = hashFunc(key); // hash the key
while(hashArray[hashVal].empCode != null)

// find item with key

// until empty cell,

482

Advanced Data Structures and Algorithms in C++

// found the key?


if(hashArray[hashVal].empCode == key)
return hashArray[hashVal];
// yes, return item
// go to next cell
++hashVal;
// wraparound if necessary
hashVal %= arraySize;
}
return dummy;

// cannot find item

6.

Start with an empty red-black tree and insert the following keys in the given order:
20, 10, 5, 30, 40, 57, 3, 2, 4, 35, 25, 18, 22, 21.
Draw the figures depicting the tree immediately after each insertion and following the rebalancing
rotation or color change (if any). Label all nodes with their color and identify their rotation types (if
any) that is done.
The following keys are inserted into an empty red-black tree in the given order:
20, 10, 5, 30, 40, 57, 3, 2, 4, 35, 25, 18, 22, 21
All insertions start with a red node. Root always will be black. No consecutive two red nodes.
Properties of red-black tree must be restored after every key insertion rotation and/or color change
(1) Insert 20 as red
node, make it
black root.

20

20

(2) Insert 10

20
10

(3) Insert 5
Right rotation
and recolor.

20
10
5

(4) Insert 30
Recolor.

10
5

10

10
5

20

10
20

5
30

20
30

20

Appendix C: Questions and Answers

(5) Insert 40
Left rotation and
Recolor.

10
5

10
20

5
30

10
30

30

40

20

40

20

40

(6) Insert 57
Recolor

10
5

10
5

30

30

40

20

40

20
57

(7) Insert 3
No fix

57

10
5

30
40

20

57

(8) Insert 2
Right rotation
and recolor.

10
5

3
2

10
3

30

20

40

30

57

40

20

57

10
3

30

20

40
57

483

484

Advanced Data Structures and Algorithms in C++

(9) Insert 4
Recolor

10

10

30

40

20

57

(10) Insert 35
No fix

30
40

20

57

10
3

30

40

20

5
4

35

(11) Insert 25 and 18


No fix needed.

57

10
3

30

18

(12) Insert 22
Recolor

40

20

25

35

57

10

10

3
2

40

20

5
4

30

18

25
22

35

2
57

30

40

20

5
18

25
22

35

57

Appendix C: Questions and Answers

(12) Insert 21
Right rotation
Recolor

10

10

3
2
4

30
40

20

5
18

485

25

35

2
57

22

30

18

4
10

21

21

3
2

22

35

57

25

30
40

20

5
4

40

20

18

21

22

35

57

25

7.

(a) Describe the B-trees? Explain the advantages of B-trees.


(b) Prove that let T be a red-black tree with n interval nodes then no node has depth greater than 2
log(n+1).
This question is repeated. Refer set-1 of section C.2.

8.

(a) Explain the Boyar Moore algorithm with an example.


(b) What are the advantages and disadvantages of tries with respect to a binary search tree?
This question is repeated. Refer set-2 of section C.2.

Você também pode gostar