C++ Fundamentals
C++ Fundamentals
TIRUNELVELI 627003.
[ISO 9001: 2000 CERTIFIED INSTITUTION]
DEPARTMENT OF INFORMATION
TECHNOLOGY
1
http://www.francisxavier.ac.in
UNIT I
OBJECT ORIENTED PROGRAMMING FUNDAMENTALS
C++ Programming features - Data Abstraction - Encapsulation - class - object constructors static members constant members member functions pointers
references - Role of this pointer Storage classes function as arguments.
Data abstraction refers to, providing only essential information to the outside world
and hiding their background details, i.e., to represent the needed information in
program without presenting the details.
Data abstraction is a programming (and design) technique that relies on the separation
of interface and implementation.
Let's take one real life example of a TV, which you can turn on and off, change the
channel, adjust the volume, and add external components such as speakers, VCRs,
and DVD players, BUT you do not know its internal details, that is, you do not know
how it receives signals over the air or through a cable, how it translates them, and
finally displays them on the screen.
Thus, we can say a television clearly separates its internal implementation from its
external interface and you can play with its interfaces like the power button, channel
changer, and volume control without having zero knowledge of its internals.
Now, if we talk in terms of C++ Programming, C++ classes provides great level of
data abstraction. They provide sufficient public methods to the outside world to play
with the functionality of the object and to manipulate object data, i.e., state without
actually knowing how class has been implemented internally.
In C++, we use classes to define our own abstract data types (ADT). You can use the
cout object of class ostream to stream data to standard output like this:
#include <iostream.h>
int main( )
{
cout << "Hello C++" <<endl;
return 0;
}
Here, you don't need to understand how cout displays the text on the user's screen.
You need to only know the public interface and the underlying implementation of
cout is free to change.
3
http://www.francisxavier.ac.in
In C++, we use access labels to define the abstract interface to the class. A class may
contain zero or more access labels:
Members defined with a public label are accessible to all parts of the program. The
data-abstraction view of a type is defined by its public members.
Members defined with a private label are not accessible to code that uses the class.
The private sections hide the implementation from code that uses the type.
There are no restrictions on how often an access label may appear. Each access label
specifies the access level of the succeeding member definitions. The specified access
level remains in effect until the next access label is encountered or the closing right
brace of the class body is seen.
Class internals are protected from inadvertent user-level errors, which might corrupt
the state of the object.
The class implementation may evolve over time in response to changing requirements
or bug reports without requiring change in user-level code.
By defining data members only in the private section of the class, the class author is
free to make changes in the data. If the implementation changes, only the class code
needs to be examined to see what affect the change may have. If data are public, then
any function that directly accesses the data members of the old representation might
be broken.
Any C++ program where you implement a class with public and private members is
an example of data abstraction. Consider the following example:
#include <iostream.h>
using namespace std;
class Adder
{
public:
4
http://www.francisxavier.ac.in
// constructor
Adder(int i = 0)
{
total = i;
}
// interface to outside world
void addNum(int number)
{
total += number;
}
// interface to outside world
int getTotal()
{
return total;
};
private:
// hidden data from outside world
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
When the above code is compiled and executed, it produces the following result: Total 60
Above class adds numbers together, and returns the sum. The public members
addNum and getTotal are the interfaces to the outside world and a user needs to
know them to use the class. The private member total is something that the user
doesn't need to know about, but is needed for the class to operate properly.
5
http://www.francisxavier.ac.in
Data encapsulation is a mechanism of bundling the data, and the functions that use
them and data abstraction is a mechanism of exposing only the interfaces and hiding
the implementation details from the user.
C++ supports the properties of encapsulation and data hiding through the creation of
user-defined types, called classes. We already have studied that a class can contain
private, protected and public members. By default, all items defined in a class are
private.
For example:
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
private:
double length;
// Length of a box
double breadth;
// Breadth of a box
double height;
// Height of a box
};
The variables length, breadth, and height are private. This means that they can be
accessed only by other members of the Box class, and not by any other part of your
program. This is one way encapsulation is achieved.
To make parts of a class public (i.e., accessible to other parts of your program), you
must declare them after the public keyword. All variables or functions defined after
the public specifier are accessible by all other functions in your program.
Making one class a friend of another exposes the implementation details and reduces
encapsulation. The ideal is to keep as many of the details of each class hidden from all
other classes as possible.
6
http://www.francisxavier.ac.in
#include <iostream.h>
#include<conio.h>
class Adder
{
public:
// constructor
Adder(int i = 0)
{
total = i;
}
// interface to outside world
void addNum(int number)
{
total += number;
}
// interface to outside world
int getTotal()
{
return total;
};
private:
// hidden data from outside world
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
7
http://www.francisxavier.ac.in
When the above code is compiled and executed, it produces the following result: Total 60
Above class adds numbers together, and returns the sum. The public members
addNum and getTotal are the interfaces to the outside world and a user needs to know them
to use the class. The private member total is something that is hidden from the outside world,
but is needed for the class to operate properly.
Designing Strategy:
Most of us have learned through bitter experience to make class members private by
default unless we really need to expose them. That's just good encapsulation.
This wisdom is applied most frequently to data members, but it applies equally to all
members, including virtual functions.
1.4 CLASS:
In oop, class is a user defined data type that can hold data and their associated
functions into single unit.
The class members are divided into two types:
1. Data Member
2. Function Member
In class there are certain privilege for accessing both data and function members.
They are said to be access specifiers.
It is divided into three categories. They are,
1.Public
2.private
3.Protected
By default the data members are private. So the private members of a class are not
accessible from outside the class. If the data members are specified as public then the
members of a class can be accessible from inside and outside of the class.
8
http://www.francisxavier.ac.in
Syntax of a class
class class_name
{
Access Specifier:
Data Members
Access Specifier:
Function Members
};
Example:
Consider an example of class
class student
{
public:
int rollno,age;
Data Members
char *name;
void getdetail()
{
Cin>>rollno>>age;
Cin>>name;
}
Function Members
void printdetail()
{
Cout<<rollno<<age;
Cout<<name;
}
};
In this example a class named student with data members such rollno, age and name
and with function members getdetail() and printdetail() is defined within the class.The access
specifiers of this class is public.
9
http://www.francisxavier.ac.in
Return type specifies the type of a function. Class name specifies the name of a class
in which the function belongs. The operator:: denotes scope resolution operator. Function
name specifies the name of a function.
Example:
class student
{
public:
int rollno,age;
char *name;
void getdetail();
void printdetail();
};
void student::getdetail()
{
Cin>>rollno>>age;
Cin>>name;
}
void student::printdetail()
{
Cout<<rollno<<age;
Cout<<name;
}
10
http://www.francisxavier.ac.in
1.5 OBJECTS:
Objects are the variables of user defined data type called class. Once a Class has been
created we can declare any number of variables belongs to that class. In the above example
class student we can declare any number of variables of class student in the main function.
Using objects we can access any public members of a class using Dot Operator.
Syntax:
Class_Name object1, object2, object3.object n;
void main ()
{
class Name
s1.getdetail (); Object s1 access (call) the function member getdetail () of student class
s1.printdetail ();
11
http://www.francisxavier.ac.in
Class student
Function Members:
Void getdetail()
Void printdetail()
Object s1:
Object s2:
Object s3:
1.6 CONSTRUCTOR:
The main use of constructors is to initialize objects. The function of initialization is
automatically carried out by the use of a special member function called a constructor.
General Syntax of Constructor
A constructor is a special member function that takes the same name as the class
name. The default constructor for a class X has the form
X::X()
In the above example, the arguments are optional. The constructor is automatically
named when an object is created. A constructor is named whenever an object is defined or
dynamically allocated using the new operator.
There are several forms in which a constructor can take its shape namely:
Default Constructor:
This constructor has no arguments in it. The default Constructor is also called as the
no argument constructor.
For example:
class Exforsys
{
12
http://www.francisxavier.ac.in
private:
int a,b;
public:
Exforsys();
...
};
Exforsys :: Exforsys()
{
a=0;
b=0;
}
Parameterized Constructor:
A default constructor does not have any parameter, but if you need, a constructor
can have parameters. This helps you to assign initial value to an object at the time of its
creation as shown in the following example:
#include <iostream>
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(double len); // This is the constructor
private:
double length;
};
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// Main function for the program
int main( )
{
Line line(10.0);
// get initially set length.
cout << "Length of line : " << line.getLength() <<endl;
// set line length again
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Object is being created, length = 10
Length of line : 10
Length of line : 6
Copy constructor:
This constructor takes one argument, also called one argument constructor. The main
use of copy constructor is to initialize the objects while in creation, also used to copy an
object. The copy constructor allows the programmer to create a new object from an existing
one by initialization.
For example to invoke a copy constructor the programmer writes:
Exforsys e3(e2);
or
14
http://www.francisxavier.ac.in
Exforsys e3=e2;
Both the above formats can be sued to invoke a copy constructor.
For Example:
#include <iostream>
using namespace std;
class Exforsys
{
private:
int a;
public:
Exforsys()
{
}
Exforsys(int w)
{
a=w;
}
Exforsys(Exforsys& e)
{
a=e.a;
cout << " Example of Copy Constructor";
}
void result()
{
cout<< a;
}
};
void main()
{
Exforsys e1(50);
Exforsys e3(e1);
cout<< "ne3=";e3.result();
}
15
http://www.francisxavier.ac.in
Destructors
Destructors are also special member functions used in C++ programming language.
Destructors have the opposite function of a constructor. The main use of destructors is to
release dynamic allocated memory.
Destructors are used to free memory, release resources and to perform other clean up.
Destructors are automatically named when an object is destroyed. Like constructors,
destructors also take the same name as that of the class name.
Like the constructor, the destructor must also be defined in the public. The destructor
must be a public member.
The Destructor does not take any argument which means that destructors cannot be
overloaded.
For example:
class Exforsys
{
private:
...
public:
Exforsys()
{
}
~ Exforsys()
{
}
}
16
http://www.francisxavier.ac.in
Example Program:
#include<iostream.h>
#include<conio.h>
class count
{
public:
static int countt;
void dispcount()
{
countt++;
cout<<"Object\t"<<countt<<"\n";
}
};
int count::countt;
void main()
{
count c1,c2,c3,c4,c5;
clrscr();
c1.dispcount();
c2.dispcount();
c3.dispcount();
17
http://www.francisxavier.ac.in
c4.dispcount();
c5.dispcount();
getch();
}
In this example class count has a static data member countt.This program is used for
counting the number of objects which is declared in the class.So when an object c1 access the
function dispcount() the static variable has the value 1.when s5 access the function the value
will be incremented to 5.
Output:
Object 1
Object 2
Object 3
Object 4
Object 5
NOTE:
Once the static data member is defined it is automatically initialized to zero.
Initially the value for the data members is set by constructor during the object
creation.
Constant objects can access only the constant member function .The constant function
is read only function it cannot alter the value of object data members
18
http://www.francisxavier.ac.in
{
//read only function
}
};
void main()
{
const class_name object( parameter);
//constant objects
Ex:
void printdetails() const
{
cout<<Roll No is:<<rollno;
cout<<Name is:<<name;
cout<<Address is:<<address;
rollno=10; / / the following line is erroneous
}
//it is allowed
19
http://www.francisxavier.ac.in
{
cout<<"Time is:"<<hour<<"Hour:\t"<<minute<<"Minutes:\t"<<second<<"Seconds:\n";
}
~time()
{
}
void get() const
{
cin>>hour>>minute>>second;
}
};
void main()
{
clrscr();
const time t1(8,55,50);
//Constant objects
t1.disp();
t1.get();
t1.disp();
getch();
}
20
http://www.francisxavier.ac.in
Output
Time is:8Hour: 55Minutes:
50Seconds:
5
50
40
Time is:8Hour: 55Minutes:
50Seconds:
In this example, the object of this time class is set with the default value of 8 hour 55
minutes and 50 seconds and it is initialized with constructor. Since this object t1 is a n
constant object it cannot be modified and it can access only the constant function in a class.
Syntax
class stat
{
static return_type function_name()
{
Statement;
}
};
Calling Static Function in the main Function
void main()
{
class_name::static function name();
}
21
http://www.francisxavier.ac.in
Example:
#include<iostream.h>
#include<conio.h>
class count
{
public:
static int countt;
count()
{
countt++;
cout<<"Object\t"<<countt<<"\n";
}
static void statfun()
{
cout<<"Object\t"<<countt<<"\n";
}
};
int count::countt;
void main()
{
count c1,c2,c3,c4,c5;
clrscr();
count::statfun();//calling the constructor using classname
getch();//scope resolution operator and the static funtion name
}
Output:
Object 5
The programmer must note the following while using static member functions:
A static member function can only access static member data, static member functions
and data and functions outside the class. The programmer must take note not to use
static member function in the same manner as non-static member function, as nonstatic member function can access all of the above including the static data member.
22
http://www.francisxavier.ac.in
A non-static member function can be declared as virtual but care must be taken not to
declare a static member function as virtual. .
The programmer must first understand the concept of static data while learning the
context of static functions. It is possible to declare a data member of a class as static
irrespective of it being a public or a private type in class definition. If a data is
declared as static, then the static data is created and initialized only once. Non-static
data members are created again and again. For each separate object of the class, the
static data is created and initialized only once. As in the concept of static data, all
objects of the class in static functions share the variables. This applies to all objects of
the class.
A non-static member function can be called only after instantiating the class as an
object. This is not the case with static member functions. A static member function
can be called, even when a class is not instantiated.
A static member function cannot have access to the 'this' pointer of the class.
1.10 POINTERS:
Every storage location of memory has an associated address. The address is a number
that grows sequentially. For every program placed in memory, each variable or function in
the program has an associated address.
23
http://www.francisxavier.ac.in
For Example
#include <iostream>
using namespace std;
void main()
{
int exf=200;
int test=300;
cout << endl << &exf << endl << &test;
}
The output of the above program could be one of the following, and at each run it
differs slightly:
The &exf has the address associated with the integer variable exf and the &test has
the address associated with the integer variable test which are displayed using the cout
statement.
Using the understanding of address of operators, the discussion turns to the concept of
pointers.
exforsys = 100;
test = exforsys;
x = &exforsys;
Using the above information, the assignment takes place as below:
24
http://www.francisxavier.ac.in
exforsys is an integer variable having the value of 100 stored in memory address location
3501.
When the variable exforsys is assigned to the variable test in the second statement:
test = exforsys;
The value of the variable exforsys 100 is copied to the variable test.
In the third statement, the address of the variable exforsys is denoted by reference operator
&exforsys is assigned to the variable x as:
x = &exforsys;
The address of the variable 3501 and not the contents of the variable exforsys is
copied into the variable x. The pointers concept fits in this statement. Pointers are the
variables that store the reference to another variable. Pointers are variables that store the
address of the variable that it is pointed by. Variable x is referred to as the pointer in the
above example.
The programmer must note that the address operator placed before a variable is not
the same as operator & placed after the variable. For example, &x is not same as x&.
Variable &x refers to address operator whereas x& refer to reference operator&. Pointer is a
variable that holds the address, also called pointer variable.
1.11 REFERENCES:
A reference variable is an alias, that is, another name for an already existing variable.
Once a reference is initialized with a variable, either the variable name or the reference name
may be used to refer to the variable.
i = 17;
Read the & in these declarations as reference. Thus, read the first declaration as "r is
an integer reference initialized to i" and read the second declaration as "s is a double
reference initialized to d.". Following example makes use of references on int and double:
#include <iostream.h>
int main ()
{
// declare simple variables
int
i;
26
http://www.francisxavier.ac.in
double d;
// declare reference variables
int& r = i;
double& s = d;
i = 5;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
d = 11.7;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}
When the above code is compiled together and executed, it produces the following result:
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7
References are usually used for function argument lists and function return values. So
following are two important subjects related to C++ references which should be clear to a
C++ programmer: we can implement call by reference concept using pointers.
Here is another example of call by reference which makes use of C++ reference:
#include <iostream.h>
// function declaration
void swap(int& x, int& y);
int main ()
{
// local variable declaration:
int a = 100;
int b = 200;
cout << "Before swap, value of a :" << a << endl;
cout << "Before swap, value of b :" << b << endl;
27
http://www.francisxavier.ac.in
return 0;
}
/* put y into x */
return;
}
When the above code is compiled and executed, it produces the following result:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100
Example:
#include <iostream.h>
28
http://www.francisxavier.ac.in
class Box
{
public:
// Constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length;
// Length of a box
double breadth;
// Breadth of a box
double height;
// Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5);
// Declare box1
// Declare box2
if(Box1.compare(Box2))
{
cout << "Box2 is smaller than Box1" <<endl;
}
else
{
29
http://www.francisxavier.ac.in
When the above code is compiled and executed, it produces the following result:
Constructor called.
Constructor called.
Box2 is equal to or larger than Box1
Automatic:
Variables defined within the function body are called automatic variables. Auto is the
keyword used to declare automatic variables. By default and without the use of a keyword,
the variables defined inside a function are automatic variables.
For instance:
void exforsys( )
{
auto int x;
auto float y;
...
}
is the same as
30
http://www.francisxavier.ac.in
void exforsys( )
{
int x;
float y;
//Automatic Variables
...
}
In the above function, the variable x and y are created only when the function
exforsys( ) is called. An automatic variable is created only when the function is called. When
the function exforsys( ) is called, the variables x and y are allocated memory automatically.
External:
External variables are also called global variables. External variables are defined
outside any function, memory is set aside once it has been declared and remains until the end
of the program. These variables are accessible by any function. This is mainly utilized when a
programmer wants to make use of a variable and access the variable among different function
calls.
Static:
The static automatic variables, as with local variables, are accessible only within the
function in which it is defined. Static automatic variables exist until the program ends in the
same manner as external variables. In order to maintain value between function calls, the
static variable takes its presence.
For example:
#include <iostream.h>
using namespace std;
int exforsys(int);
int exforsys2(int);
void main( )
{
int in;
int out;
while(1)
{
31
http://www.francisxavier.ac.in
32
http://www.francisxavier.ac.in
address. Function pointers can be used to simplify code by providing a simple way to select a
function to execute based on run-time values.
#include <iostream.h>
int add(int first, int second)
{
return first + second;
}
int subtract(int first, int second)
{
return first - second;
}
int operation(int first, int second, int (*functocall)(int, int))
{
return (*functocall)(first, second);
}
void main()
{
int a, b;
int (*plus)(int, int) = add;
int (*minus)(int, int) = subtract;
a = operation(7, 5, plus);
b = operation(20, a, minus);
cout << "a = " << a << " and b = " << b << endl;
}
33
http://www.francisxavier.ac.in
UNIT II
OBJECT ORIENTED PROGRAMMING CONCEPTS
String Handling Copy Constructor - Polymorphism compile time and run time
polymorphisms function overloading operators overloading dynamic memory
allocation - Nested classes - Inheritance virtual functions.
34
http://www.francisxavier.ac.in
Actually, you do not place the null character at the end of a string constant. The C++
compiler automatically places the '\0' at the end of the string when it initializes the array.
#include <iostream.h>
int main ()
{
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
When the above code is compiled and executed, it produces result something as follows:
Greeting message: Hello
{
char str1[10] = "Hello";
char str2[10] = "World";
char str3[10];
int len ;
// copy str1 into str3
strcpy( str3, str1);
cout << "strcpy( str3, str1) : " << str3 << endl;
// concatenates str1 and str2
strcat( str1, str2);
cout << "strcat( str1, str2): " << str1 << endl;
// total lenghth of str1 after concatenation
len = strlen(str1);
cout << "strlen(str1) : " << len << endl;
return 0;
}
When the above code is compiled and executed, it produces result something as follows:
strcpy( str3, str1) : Hello
strcat( str1, str2): HelloWorld
strlen(str1) : 10
str3 = str1;
cout << "str3 : " << str3 << endl;
// concatenates str1 and str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// total lenghth of str3 after concatenation
len = str3.size();
cout << "str3.size() : " << len << endl;
return 0;
}
When the above code is compiled and executed, it produces result something as follows:
str3 : Hello
str1 + str2 : HelloWorld
str3.size() : 10
int a;
public:
Exforsys()
{}
Exforsys(int w)
{
a=w;
}
Exforsys(Exforsys& e)
{
a=e.a;
cout << " Example of Copy Constructor";
}
void result()
{
cout<< a;
}
};
void main()
{
Exforsys e1(50);
Exforsys e3(e1);
cout<< "ne3=";
e3.result();
}
2.3 POLYMORPHISM:
Polymorphism is one of the most important features of Object Oriented
Programming. Polymorphic variables can take up objects or values of different types as
values and polymorphic functions and procedures can be used as objects or values of
different types. It refers to exhibiting different behaviour by an instance in different
situations.
1. Function overloading
2. Operator overloading.
Method overloading means having two or more methods with the same name
but with different signatures.
Method overriding means having two or more methods with the same name,
same signature but with different implementation.
When the source code is compiled, the functions to be invoked are bound to the
compiler during compile time, as to invoke which function depending upon the type and
number of arguments. Such a phenomenon is referred to early binding, static linking or
compile time polymorphism.
For example:
#include <iostream.h>
//function prototype
int multiply(int num1, int num2);
float multiply(float num1, float num2);
void main()
{
//function call statements
int ans1=multiply(4,3);
// first prototype is invoked as arguments
// are of type int
float ans2 = multiply(2.5, 4.5);
//second prototype is invoked as arguments are of type float
}
The compiler checks for the correct function to be invoked by matching the type of
arguments and the number of arguments including the return type. The errors, if any, are
reported at compile time, hence referred to as compile time polymorphism.
Unary Operators:
As the name implies, it operates on only one operand. Some unary operators are
named ++ also called the Increment operator, -- also called the Decrement Operator, ! , ~ are
called unary minus.
Binary Operators:
It operates on two operands. Some binary operators are
arithmetic operators,
:: - is the scope resolution operator which is used to use the function definition
outside the class. The usage of this is clearly defined in our earlier section of How
to define class members.
For example
Suppose we have a class say Exforsys and if the programmer wants to define a
operator overloading for unary operator say ++, the function is defined as,
41
http://www.francisxavier.ac.in
Inside the class Exforsys the data type that is returned by the overloaded operator is
defined as,
class Exforsys
{
private:
..
public:
void operator ++();
.
};
#include <iostream.h>
using namespace std;
42
http://www.francisxavier.ac.in
class Exforsys
{
private:
int x;
public:
Exforsys( ) { x=0; }
//Constructor
void display();
void operator ++( );
};
void main( )
{
Exforsys e1,e2;
43
http://www.francisxavier.ac.in
In the above example we have created 2 objects e1 and e2 f class Exforsys. The
operator ++ is overloaded and the function is defined outside the class Exforsys.
When the program starts the constructor Exforsys of the class Exforsys initialize the
values as zero and so when the values are displayed for the objects e1 and e2 it is displayed
as zero. When the object ++e1 and ++e2 is called the operator overloading function gets
applied and thus value of x gets incremented for each object separately. So now when the
values are displayed for objects e1 and e2 it is incremented once each and gets printed as one
for each object e1 and e2.
public:
44
http://www.francisxavier.ac.in
Exforsys()
//Constructor
{ x=0; y=0; }
void getvalue( )
{
cout << "n Enter value for x: ";
cin >> x;
cout << "n Enter value for y: ";
cin>> y;
}
void displayvalue( )
{
cout << "value of x is: " << x << "; value of y is: " << y;
}
45
http://www.francisxavier.ac.in
In the above example, the class Exforsys has created three objects e1, e2, e3. The
values are entered for objects e1 and e2. The binary operator overloading for the operator '+'
is declared as a member function inside the class Exforsys. The definition is performed
outside the class Exforsys by using the scope resolution operator and the keyword operator.
e3= e1 + e2; The binary overloaded operator '+' is used. In this statement, the
argument on the left side of the operator '+', e1, is the object of the class Exforsys in which
the binary overloaded operator '+' is a member function. The right side of the operator '+' is
e2. This is passed as an argument to the operator '+' . Since the object e2 is passed as
argument to the operator '+' inside the function defined for binary operator overloading, the
values are accessed as e2.x and e2.y. This is added with e1.x and e1.y, which are accessed
directly as x and y. The return value is of type class Exforsys as defined by the above
example.
There are important things to consider in operator overloading with C++ programming
language. Operator overloading adds new functionality to its existing operators. The
programmer must add proper comments concerning the new functionality of the overloaded
operator. The program will be efficient and readable only if operator overloading is used only
when necessary.
If we are not in need of dynamically allocated memory anymore, you can use delete
operator, which de-allocates memory previously allocated by new operator.
We can do this using the new operator with the following statements:
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
The memory may not have been allocated successfully, if the free store had been used
up. So it is good practice to check if new operator is returning NULL pointer and take
appropriate action as below:
return 0;
}
If we compile and run above code, this would produce the following result:
Value of pvalue : 29495
Following the similar generic syntax of new operator, you can allocat for a multidimensional array as follows:
However, the syntax to release the memory for multi-dimensional array will still
remain same as above:
delete [] pvalue;
#include <iostream.h>
class Box
{
public:
Box() {
cout << "Constructor called!" <<endl;
}
~Box() {
cout << "Destructor called!" <<endl;
}
};
int main( )
{
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // Delete array
return 0;
}
If we were to allocate an array of four Box objects, the Simple constructor would be
called four times and similarly while deleting these objects, destructor will also be called
same number of times.
If we compile and run above code, this would produce the following result:
Constructor called!
Constructor called!
49
http://www.francisxavier.ac.in
Constructor called!
Constructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Syntax
class Nesting_Class
{
Public:
Data Members of Nesting Class;
class Nested_Class
{
public:
Data Member of Nested Class
Function Members of Nested Class
};
};
void main()
{
Nesting_class::Nested_class Object;
object.Nested_class Datamember=value;
object.Nested_class Functionmember();
}
50
http://www.francisxavier.ac.in
Example:
#include<iostream.h>
#include<conio.h>
class fxclg //Nesting Class
{
public:
class cse
//Nested Class
{
public:
char name[10],dept[5];
int age;
void get()
{
cout<<"Enter Details Name Age Dept:\n";
cin>>name;
cin>>age;
cin>>dept;
cout<<"\n";
}
void put()
{
cout<<"name\t"<<name<<"age:\t"<<age<<"dept:\t"<<dept;
}
};
};
void main()
{
clrscr();
fxclg::cse cs1;
cs1.get();
cs1.put();
getch();
}
51
http://www.francisxavier.ac.in
Output:
Enter Details Name Age Dept:
kumar
24
CSE
name
kumar age:
24 dept: CSE
In this example, the class cse (nested class) is defined within the class fxclg(nesting
class),To declare the objects of a nested class we have to follow the procedure, fxclg::cse c1;
Using this object it access the data and function member of nested class cse
2.9 INHERITANCE:
One of the most important concepts in object-oriented programming is that of
inheritance. Inheritance allows us to define a class in terms of another class, which makes it
easier to create and maintain an application. This also provides an opportunity to reuse the
code functionality and fast implementation time.
When creating a class, instead of writing completely new data members and member
functions, the programmer can designate that the new class should inherit the members of an
existing class. This existing class is called the base class, and the new class is referred to as
the derived class.
The idea of inheritance implements the is a relationship. For example, mammal IS-A
animal, dog IS-A mammal hence dog IS-A animal as well and so on.
Type of Inheritance:
Single Inheritance
Multiple Inheritance
Multilevel Inheritance
Hybrid Inheritance
Hierarchical Inheritance
Example:
#include<iostream.h>
#include<conio.h>
class student
{
public:
char name[10],collname[10],dept[5];
void getfun()
{
cout<<"Enter Name,College,Department\n";
cin>>name>>collname>>dept;
}
};
char sub1[5],sub2[5],sub3[5];
void getdet()
{
getfun();
cout<<"Enter subject 1,2,3";
cin>>sub1>>sub2>>sub3;
}
void putdet()
{
cout<<"Name\t"<<name<<"College name\n"<<collname<<"Department\t"<<dept
<<"Subject 1,2,3\t"<<sub1<<sub2<<sub3;
}
};
void main()
{
clrscr();
cse c1;
c1.getdet();
c1.putdet();
getch();
}
Output:
Enter Name,College,Department
paul
fx
cse
Enter subject 1,2,3
maths
pds
evs
Name
paul College name fx Department cse subject 1,2,3 maths pds evs
54
http://www.francisxavier.ac.in
Example:
#include<iostream.h>
#include<conio.h>
class student
{
public:
char name[10],collname[10],dept[5];
void getfun()
{
cout<<"Enter name,college name,department\n";
cin>>name>>collname>>dept;
}
};
class internal
{
public:
char sub1[5],sub2[5],sub3[5];
int im1,im2,im3;
void getdet()
{
cout<<"Enter subject 1,2,3\n";
cin>>sub1>>sub2>>sub3;
cout<<"Enter internal marks for subject1,2,3\n";
55
http://www.francisxavier.ac.in
cin>>im1>>im2>>im3;
}
};
void main()
{
clrscr();
cse c1;
c1.calc();
c1.putdet();
getch();
}
56
http://www.francisxavier.ac.in
Output:
Enter name,college name,department
paul
fx
cse
Enter subject 1,2,3
maths
pds
dbms
Enter internal marks for subject1,2,3
50
50
50
Enter external marks for subject1,2,3
50
50
50
name
paul collname
sub1
Total
100
fx
sub2 100
dept cse
sub3 100
The mechanism of deriving a class from another derived class is known as multilevel
inheritance.
57
http://www.francisxavier.ac.in
Example:
STUDENT
TEST
RESULT
#include<iostream.h>
#include<conio.h>
#include<iomanip.h>
class student
{
public:
char name[10],clgname[10];
int age,year;
void get()
{
cout<<"Enter the Name,College Name,Age, Year:\n";
cin>>name>>clgname>>age>>year;
}
void put()
{
cout<<"Name:\t"<<"College Name:\t"<<"Age:\t"
<<"Year:\t"<<"\n";
cout<<name<<"\t"<<clgname<<"\t"<<age<<"\t"<<year<<"\n";
}
};
class test:public student
{
public:
int m1,m2,m3;
void getmarks()
58
http://www.francisxavier.ac.in
{
cout<<"Enter mark1,2,3";
cin>>m1>>m2>>m3;
}
void putmarks()
{
cout<<"Mark1:\t"<<"Mark2\t"<<"Mark3\t"<<"\n";
cout<<m1<<"\t"<<m2<<"\t"<<m3<<"\n";
}
};
class result:public test
{
public:
int i1,i2,i3,t1,t2,t3;
void getinternal()
{
cout<<"Enter the internal marks:";
cin>>i1>>i2>>i3;
}
void finalmark()
{
t1=i1+m1;
t2=i2+m2;
t3=i3+m3;
cout<<"Result M1,M2,M3:";
cout<<"\t"<<t1<<"\t"<<t2<<"\t"<<t3<<"\n";
}
};
void main()
{
clrscr();
result r1;
r1.get();
59
http://www.francisxavier.ac.in
r1.getmarks();
r1.getinternal();
r1.put();
r1.putmarks();
r1.finalmark();
getch();
}
In this example the student class is derived into test class and then the test class
further derived into result class. So the result class inherits the properties of both student and
test class.
Example:
#include <iostream.h>
class Side
{
public:
int l;
void set_values (int x)
{
l=x;
}
};
class Square: public Side
60
http://www.francisxavier.ac.in
{
public:
int sq()
{
return (l *l);
}
};
class Cube:public Side
{
public:
int cub()
{
return (l *l*l);
}
};
int main ()
{
Square s;
s.set_values (10);
cout << "The square value is::" << s.sq() << endl;
Cube c;
c.set_values (20);
cout << "The cube value is::" << c.cub() << endl;
return 0;
}
Output:
The square value is:: 100
The cube value is::8000
In the above example the two derived classes "Square", "Cube" uses a single base
class "Side". Thus two classes are inherited from a single class. This is the hierarchical
inheritance OOP's concept in C++.
61
http://www.francisxavier.ac.in
Example:
#include <iostream.h>
class mm
{
protected:
int rollno;
public:
void get_num(int a)
{
rollno = a;
}
void put_num()
{
cout << "Roll Number Is:"<< rollno << "\n";
}
};
class marks : public mm
{
protected:
int sub1;
int sub2;
public:
void get_marks(int x,int y)
62
http://www.francisxavier.ac.in
{
sub1 = x;
sub2 = y;
}
void put_marks(void)
{
cout << "Subject 1:";
cout<< sub1 ;
cout<< "\n";
cout << "Subject 2:";
cout << sub2;
cout << "\n";
}
};
class extra
{
protected:
float e;
public:
void get_extra(float s)
{
e=s;
}
void put_extra(void)
{
cout << "Extra Score::" << e;
cout<< "\n";
}
};
class res : public marks, public extra
{
protected:
float tot;
63
http://www.francisxavier.ac.in
public:
void disp(void)
{
tot = sub1+sub2+e;
put_num();
put_marks();
put_extra();
cout << "Total:"<< tot;
}
};
int main()
{
res std1;
std1.get_num(10);
std1.get_marks(10,20);
std1.get_extra(33.12);
std1.disp();
return 0;
}
Output:
Roll Number Is: 10
Subject 1: 10
Subject 2: 20
Extra score:33.12
Total: 63.12
64
http://www.francisxavier.ac.in
If a base class and derived class has same function and if you write code to access that
function using pointer of base class then, the function in the base class is executed even if, the
object of derived class is referenced with that pointer variable.
Example:
#include <iostream.h>
class CPolygon
{
public:
int width, height;
void set_values (int a, int b)
{
width=a; height=b;
}
virtual int area ( )
{
Cout<<Base Class Area of CPolygon;
};
class CRectangle: public CPolygon
{
public:
int area (void)
{
return (width * height);
}
};
class CTriangle: public CPolygon
{
public:
int area ( )
{
return (width * height / 2);
}
65
http://www.francisxavier.ac.in
};
void main ()
{
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << ppoly1->area( ) << endl;
cout << ppoly2->area( ) << endl;
}
Output:
20
10
In this example class CPolygon includes a virtual function area( ). In this function we
included the cout statement. When the base class pointer have derived class object and if the
pointer points the function area() then the base class area() function is overloaded with the
derived class function area().
66
http://www.francisxavier.ac.in
UNIT III
C++ PROGRAMMING ADVANCED FEATURES
Abstract class Exception handling - Standard libraries - Generic Programming templates class template - function template STL containers iterators function
adaptors allocators -Parameterizing the class - File handling concepts.
67
http://www.francisxavier.ac.in
try
throw
catch
//try block
{
.
throw exception;
68
http://www.francisxavier.ac.in
}
catch(type arg) // Catches exception
{
.
.. // Block of statements that handles the exception
}
Example:
#include <iostream>
int main()
cout << start;
try
{
// start
// throw an error
// catch an error
Exception Types
Synchronous Exception:
Out of range
Over flow
Asynchronous Exception
Keyboard interrupts
69
http://www.francisxavier.ac.in
Division by zero
#include<iostream.h>
catch( int a)
int main()
{ // catch an error
j=0;
try
return 0;
When the program enters the try block it is said to be in the guarded section.
In this program when the value of j is zero an exception is created and thrown.
70
http://www.francisxavier.ac.in
Note that the statement after throw statement in try block is not executed.
Once the exception is thrown the catch block catches the value (here zero) and
handles it.
Syntax:
try
{ function(arg);
}
catch(type arg)
{
-----}
#include<iostream.h>
void main()
int x,y;
int c; if(b==0)
cout<<ENTER
TWO
NUMBERS;
cin>>x>y;
throw b;
try
else
c=a/b;
catch(int k)
{
cout<<Divide By Zero exception;
}
}
71
http://www.francisxavier.ac.in
Throwing Mechanisms:
An exception is thrown by using the throw keyword from inside the try block.
throw (exception)
throw exception
throw
Catching Mechanisms
If the data type specified by a catch, matches that of the exception, then catch
statement is executed.
If there are multiple catches for a try, only one of the matching catch is selected and
that corresponding catch block is executed.
Syntax:
try
{
any statements
if (some condition) throw value1;
else if (some other condition)
throw value2;
else if (some last condition)
throw valueN;
}
catch (type1 name)IPLUS.COM
72
http://www.francisxavier.ac.in
{
any statements
}
catch (type2 name)
{
any statements
}
catch (typeN name)
{
any statements
}
Example:
#include<iostream.h>
void multiple_catch(int value)
{
try
{
if (value==0) //throw an int value
throw 1;
else if (value==1) //throw a char
throw a;
else //throw float
throw 1.1;
}
catch(char c)
{
cout<<Character value is thrown <<c;
}
catch(int i)
{
cout<<Integer value is thrown<<i;
}
catch(float f)
73
http://www.francisxavier.ac.in
{
cout<<Float value is thrown<<f;
}
}
void main()
{
cout<<Main Program<<endl;
multiple_catch(0);
multiple_catch(1);
multiple_catch(5);
}
Rethrowing ExceptionsTHIPLUS.COM
Syntax:
try
{
.
throw a;
}
catch (char c)
{
throw;
Example:
#include <iostream.h>
class sam
{
int erno;
public:
sam (int errno)
{
exno=errno;
}
74
http://www.francisxavier.ac.in
void shoex()
{
cout<<error no:<<exno;
}
};
void ergen()
{
try
{
sam s1(20);
int c;
cin>>c;
switch (c)
{
case 1:
throw 10;
case 2:
throw a;
case 3:
throw s1;
case 4:
throw welcome;
}
catch (int ie)
{
cout<<ie<<ie;
throw; //rethrowing
}
catch (char ce)
{
cout <<ce<<ce;
throw; //rethrowing
}
}
75
http://www.francisxavier.ac.in
void main ()
{
try
{
ergen();
throw 10;
}
catch (int)
{
cout <<caught integer;
}
catch(char)
{
cout<<caught char;
}
catch (sam s2)
{
s2.show x();
}
Terminate Function
Terminate () is the function which calls abort() to exit the program in the event of run time
error related to exceptions. The user can provide his or her own terminate function instead of
built-in terminate.
Use:
Used to close all open files & deallocate resources before quitting the program.
Syntax: set_terminate (myterminate);
Unexpected Function
76
http://www.francisxavier.ac.in
Example:
#include <iostream.h>
void myunexpected ()
{
cout << "unexpected called\n";
throw 0; // throws int (in exception-specification)
}
void myfunction
() throw (int)
{
throw 'x'; // throws char (not in exception-specification)
}
int main (void)
{
set_unexpected (myunexpected);
try
{
myfunction();
}
catch (int)
{
cout << "caught int\n";
}
catch (...)
{
cout << "caught some other exception type\n";
}
return 0;
}
Uncaught Exception()
This function returns true if an exception has been thrown but not yet caught.
77
http://www.francisxavier.ac.in
Syntax:
bool
uncaught_exceptions.
if
(uncaught_exception(
))
{
//Do not call the function which might throw an exception
}
Otherwise
{
Follow the natural sequence of the destructor Algorithm
}
A stream is a sequence of bytes (or) conceptually pipe like constructs used for
providing I/O.
The source stream that provides data to the program is called the input stream.
The destination stream that receives output from the program is called the output
stream.
It provides the basic support for formatted and unformatted I/O operations.
istream:THIPLUS.COM
ostream:
streambuf:
iostream:
Inherits the properties of ios, istream and ostream through multiple inheritance.
The input data are separated by white spaces and should match the type of variable in cin.
cout<<variable 1<<.<<variable n;
This statement is used for displaying data on the screen.
putc() and getc() functions :
getc() and putc() are used for reading and writing to the streams.
Get() reads a character at a time from the input stream and put() writes a character
at a time to the output stream.
The difference between getline() and read() is that : getline() terminates when a new
line is entered but read() does not stop when a new line is encountered.
read() stops only when end of file (ctrl + z ) is encountered.
The getline() also stops reading from input if end of file is specified.
MANIPULATORS:
The choice between manipulators and ios functions to solve formatting problems
sometimes depends on the preference of the user.
Manipulators:
setw()
setprecison()
setfill()
setiosflags()
80
http://www.francisxavier.ac.in
resetiosflags()
Characteristics of manipulators:
Manipulators are easy to write and produce more readable codes and make the
program short.
When a manipulator does not take any arguments , it is passed without the ()
parenthesis.
Generic programming means that you are not writing source code that is compiled as-is but that you
collection of other objects. But there's much more to generic programming. In the context of
C++ .it means to write programs that are evaluated at compile time.
Templates which are the C++ way of generic programming leverage this constraint by
letting you define classes where one or more parameters are unspecified at the time you define
the class. When you instance the template later you tell the compiler which type it should use
to create the class out of the template.
};
void main()
{
// Make MyContainer take just ints.
MyContainer<int> intContainer;
}
3.5 TEMPLATES:
An important feature of oops called Templates makes this benefit stronger and
provides the greater flexibility to the languages.
A template allows the construction of family of functions and classes to perform the
same operation on different types.
This provides the generic programming which allows developing the reusable software
component such as classes and function.
Terms which means use the same function or class for different purpose without
changing their basic meaning where the function or class should be defined only once.
Syntax:
template <class T>
class <class_name>
{
Member;
Member function;
};
82
http://www.francisxavier.ac.in
Example:
#include<iostream.h>
#include<conio.h>
template <class T>
class complex
{
T real, image;
public:
void getdata()
{
cout<<"\n Enter the complex values";
cin>>real>>image;
}
void putdata()
{
if(image>0)
cout<<real<<"+"<<image<<"i";PLUS.COM
else cout<<real<<image<"i";
}
}
;
void main()
{
clrscr();
complex <int> obj1;
obj1.getdata();
obj1.putdata();
complex <float> obj2;
obj2.getdata();
obj2.putdata();
getch();
}
83
http://www.francisxavier.ac.in
Syntax:
Template<typename T ,.>
Return Type Function-name(argument)
{
Function body
}
Example:
Template < class T>
void generic Bubblesort(T Temp GenericArray[]) // template function 1
{
for( int i=0;i<5; i++)
{
for( int j=i+1;j<5;j++)
{
if(TempGenericArray[i]<TempGenericArray[j])
{S.COM
int temp=TempGenericArray[i];
TempGenericArray[i]=TempGenericArray[j];
TempGenericArray[j]=temp;
}
}
Template <class T>
void Generic Display(T TempGenericArray[]) // template function2
{
cout<<\n;
for(int i=0;i<5;i++)
{
84
http://www.francisxavier.ac.in
cout<<TempGenericArray[i]<<\t;
}
}
void main()
{
int Array1[]={1,4,6,2,6};
GenericBubbleSort(Array 1);
GenericDisplay(Array2);
Char Array2[]=sdfla;
GenaricBubbleSort(Array2)
Sample output:
6
9.4
5.2
2.5
0.5
Example:
#include<iostream.h>
#include<conio.h>
template <class T1, class T2>
85
http://www.francisxavier.ac.in
void display(T1 a, T2 b)
{
cout<<a<<"\n"<<b<<"\n";
}DYARTHIPLUS.COM
void main()
{
clrscr();
void display(T1,T2);
display("sample","program");
display(10,20);
display(7.5,2);
display(10.75,10.256);
getch()
}
Containers
o A Container is an object that stores other objects (its elements), and that has
methods for accessing its elements.
Algorithms
o An algorithm is a procedure that is used to process the data contained in the
containers. The STL includes many different kinds of algorithms to provide
support to tasks such as initializing, searching, copying, and sorting and
merging
Iterators
o Iterators are a generalization of pointers: they are objects that point to other
objects.
86
http://www.francisxavier.ac.in
3.9 CONTAINERS:
A Container is an object that stores other objects (its elements), and that has methods
for accessing its elements. It is a way data is organized in memory. The STL containers are
implemented by template classes and therefore can be easily customized to hold different
types of data.
Sequence containers
Associative containers
Derived containers
Sequence Containers:
Sequence containers store elements in a linear sequence, like a line. Each element is
related to other elements by its position along the line. They all expand themselves to allow
insertion of elements and all of them support a number of operations on them.
Element0
Element1
Element2
Last Element
Associative Containers:
Associative containers are designed to support direct access to elements using keys.
87
http://www.francisxavier.ac.in
Derived Containers:
The derived containers do not support iterators and therefore we cannot use them for
data manipulation. They support two member functions pop() and push() for implementing
deleting and inserting operations.
3.10 ITERATORS:
An iterator is an object that points to an element in a container. We can use iterators to
move through the contents of containers. Iterators are handled just like pointers.
Iterators are classified into five categories depending on the functionality they
implement:
Input
Output
Forward
Bidirectional
Random Access
Random Access:
Random-access iterators implement all the functionality of bidirectional iterators, and
also have the ability to access ranges non-sequentially: distant elements can be accessed
directly by applying an offset value to an iterator without iterating through all the elements in
between. These iterators have a similar functionality to standard pointers (pointers are
iterators of this category).
The classes unary_negate and binary_negate only work with function object classes
which
have
argument
types
and
result
type
defined.
That
means,
that
89
http://www.francisxavier.ac.in
vector<int> v;
// fill v with 1 2 3 4
sort (v.begin(), v.end(), not2 (less_equal<int>()) );
Output: 4 3 2 1
Binders:
"The binders bind1st and bind2nd take a function object f of two arguments and a
value x and return a function object of one argument constructed out of f with the first or
second argument correspondingly bound to x.", Imagine that there is a container and you
want to replace all elements less than a certain bound with this bound.
vector<int> v;
// fill v with 4 6 10 3 13 2
int bound = 5;
replace_if (v.begin(), v.end(), bind2nd (less<int>(), bound), bound);
// v: 5 6 10 5 13 5
bind2nd returns a unary function object less that takes only one argument, because the
second argument has previously been bound to the value bound. When the function object is
applied to a dereferenced iterator i, the comparison *i < bound is done by the function-call
operator of less.
vector<char*> v;
char* c1 = new char[20]; strcpy (c1, "Tim");
char* c2 = new char[20]; strcpy (c2, "Charles");
char* c3 = new char[20]; strcpy (c3, "Aaron");
v.push_back (c1); v.push_back (c2); v.push_back (c3);
sort (v.begin(), v.end(), ptr_fun (strcmp) );
copy (v.begin(), v.end(), ostream_iterator<char*> (cout, " ") );
3.12 ALLOCATORS:
In C++ computer programming, allocators are an important component of the C++
Standard Library. The standard library provides several data structures, such as list and set,
commonly referred to as containers. A common trait among these containers is their ability to
change size during the execution of the program. It encapsulates a memory allocation and
deallocation strategy.
Every standard library component that may need to allocate or release storage, from
std::string, std::vector, and every container except std::array, to std::shared_ptr and
std::function, does so through an Allocator: an object of a class type that satisfies the
following requirements.
Requirements
a, an object of type A
std::allocator_traits
implementations for all optional requirements, and all standard library containers and other
allocator-aware classes access the allocator through std::allocator_traits, not directly.
};
is known as conversion. The text stream files can be opened by any editor. Text files
are more general.
Binary streams:
binary values are inserted
no conversion takes place.
The binary stream files are restricted to the application that creates the file. Binary files are
not flexible.
fstream <file name > - both input/read and output /write file.
File Modes:
When a file is opened, it must be specified how it is to be opened. This means
whether to create it from new or overwrite it and whether it's text or binary, read or write and
if the content is to be appended to it.
In order to open a file with a stream object open() member function is used.
open (filename, mode);
ios::ate
Write all output to the end of file (even if file position pointer is moved with seekp)
ios::app
Open a file for output and move to the end of the existing data (normally used to
append data to a file, but data can be written anywhere in the file
ios::in
The original file (if it exists) will not be truncated
ios::out
Open a file for output (default for ofstream objects)
93
http://www.francisxavier.ac.in
ios::trunc
Discard the file's contents if it exists (this is also the default action for ios::out, if
ios::ate, ios::app, or ios::in are not specified)
ios::binary
Opens the file in binary mode (the default is text mode)
ios::nocreate
Open fails if the file does not exist
ios::noreplace
Open files if the file already exists.
Inserting data somewhere in a sequential file would require that the entire file be
rewritten. It is possible, however, to add data to the end of a file without rewriting the file.
Adding data to the end of an existing file is called appending.
fout.open("filename.dat", ios::app) //open file for appending
getline(cin,age);
fout<<name<<endl; //send to file
fout<<age<<endl;
}
fout.close( );
//close file
assert(!fout.fail( ));
return 0;
}
Random Access:
In C++ , there are two types of file pointers to access a file in a random manner.
For a file in the read mode seekg (pointer for reading or getting)
For a file in the write mode seekp(pointer for wrioting or putting)
Using these, it is possible to search any record of any file by skipping the other records
inbetween.
Example:
#include"stdafx.h"
#include<iostream>
#include<fstream>
struct record
{
95
http://www.francisxavier.ac.in
char code[6];
char name[20];
int i;
}r;
int main()
{
std::fstream file("Temp.dat",std::ios::trunc|std::ios::in|std::ios::out|std::ios::binary);
if(!file)
{
std::cout<<"unable to open file";
exit(0);
}
std::cout<<"enter character code, name and an int\n";
std::cin.getline(r.code,6);
std::cin.getline(r.name,20);
std::cin>>r.i;
file.write((char *)&r,sizeof(r));
std::cout<<"\n\n"<<file.tellg()<<'\n'<<file.tellp();
file.seekg(3);
std::cout<<"\n\n"<<file.tellg()<<'\n'<<file.tellp();
file.seekp(5);
std::cout<<"\n\n"<<file.tellg()<<'\n'<<file.tellp();
}
96
http://www.francisxavier.ac.in
UNIT IV
ADVANCED NON-LINEAR DATA STRUCTURES
AVL trees B-Trees Red-Black trees Splay trees - Binomial Heaps Fibonacci
Heaps Disjoint Sets Amortized Analysis accounting method potential method
aggregate analysis.
TREE INTRODUCTION
Tree
A tree is a collection of nodes. The collection can be empty; otherwise a tree consists
of a specially designed node called root, and one or more non empty sub trees T1, T2, , Tk,
each of whose roots are connected by a directed edge from root.
Fig: A tree
Root
The root is the first and top most nodes in the hierarchical arrangement of data items.
A node which does not have a parent node is called root node. In the above tree, the root is
A.
Node
Each data item present in the tree is called a node. It is the basic data structures that
specifies the data information and have links to other data items. Example node A, B, ..,,
Q.
Leaf
A node which doesnt have children is called leaf or Terminal node. In the above
tree B, C, H, I, K, L, M, N, P, Q are leaf node.
97
http://www.francisxavier.ac.in
Siblings
Children of the same parents are said to be siblings. In the above tree B, C, D, E, F ,G
are siblings, I,J are siblings, K, L, M are siblings, P, Q are siblings.
Path
A path from node n1 to nk is defined as a sequence of nodes n1, n2, . . . , nk such that ni
is the parent of ni+1 for 1 <= i <= k.
There is exactly only one path from the root to each node. In the above tree the path
from A to P is A, E, J, P. Where A is the parent for E, E is the parent of J, and J is the parent
of P.
Length
The length is defined as the number of edges on the path. The length for the path A to
P is 3.
Degree
i. Degree of a node
The number of sub trees of a node is called its degree. In the above tree, degree of A
is 6, degree of F is 3, and degree of B is 0.
ii. Degree of a tree
The degree of the tree is the maximum degree of any node in the tree. In the above
tree, the degree of the tree is 6.
Level
The level of a node is defined by initially letting the root be at level one. If a node is
at level L then its children are at level L+1.
In the previous tree, the level of A is 1, level of B, C, D, E, F, G is 2, level of H, I, J,
K, L, M, N is 3, and the level of P, Q is 4.
Depth
For any node n, the depth of n is the length of the unique path from root to n. For
example, the depth of root A is 0, depth of c is 1, depth of H is 2, and the depth of P is 3
Height
For any node n, the heig ht of the node n is the length of the longest path from n to the
leaf. The height of the leaf is zero. For example, the height of node F is 1, and the height of
E is 2.
Note
The height of the tree is equal to the height of the root.
Depth of the tree is equal to the height of the tree.
98
http://www.francisxavier.ac.in
Terminal node
A node with degree zero is called a terminal node. Leaf is called the terminal node
because it has the degree 0. In the previous tree node B, C, H, I, K, L, M, N, P, Q are
terminal nodes.
Non Terminal Node
Any node whose degree is non zero is called Non Terminal node. In the previous
tree node A, D, E, F, G, J are non terminal nodes.
Edge
An edge is a condition line which connects two adjacent node of a tree. The line
drawn from one node to another node is called edge. If a tree has N nodes, then there are N1 edges. For example, the previous tree has 13 nodes and 12 edges.
Interior node
A node is said to be an interior node only if it is between root and leaves. All non
terminal nodes except the root is called interior node. For example, D, E, F, G, J are interior
nodes.
Ancestor node
A node is said to be an ancestor of another node only if it is the parent of that node or
the parent of same ancestor of that node. For example, the ancestor of node H are A and D,
and the ancestor of node F is A.
Descendent node
A node is said to be a descendent of another node if it is the child of that node or the
child of some other descendent of that node. For example, the descendent of F are K, L, M,
and the descendent of E are I, J, P, Q.
Forest
A forest is a set of disjoint trees. If the root is removed, then each node becomes a
separate tree to become a forest.
Implementation of Trees
One way to implement a tree would be to have in each node, besides its data, a pointer
to each child of the node. However, since the number of children per node can vary so greatly
and is not known in advance, it might be infeasible to make the children direct links in the
data structure, because there would be too much wasted space. The solution is simple: Keep
the children of each node in a linked list of tree nodes.
typedef struct tree_node *tree_ptr;
99
http://www.francisxavier.ac.in
struct tree_node
{
element_type element;
tree_ptr first_child;
tree_ptr next_sibling;
};
Tree Traversals
Tree traversal is a method for visiting all the nodes in the tree exactly once. There are
three types of tree traversal techniques, they are
i.
Inorder Traversal
ii.
Preorder Traversal
iii.
Postorder Traversal
Inorder Traversal
The inorder traversal of a binary tree is performed as
Traverse the left subtree in inorder
Visit the root
Traverse the right subtree in inorder.
Recursive routine for inorder traversal
void inorder(Tree T)
{
if(T!=NULL)
{
inorder(T->left);
printElement(T->Element);
100
http://www.francisxavier.ac.in
inorder(T->right);
}
}
Preorder Traversal
The preorder traversal of a binary tree is performed as
Visit the root
The left subtree in preorder
Traverse the right subtree in preorder.
Recursive routine for preorder traversal
void preorder(Tree T)
{
if(T!=NULL)
{
printElement(T->Element);
preorder(T->left);
preorder(T->right);
}
}
Postorder Traversal
The postorder traversal of a binary tree is performed as
Traverse the left subtree in postorder
Traverse the right subtree in postorder.
Visit the root
Recursive routine for postorder traversal
void postorder(Tree T)
{
if(T!=NULL)
{
postorder(T->left);
postorder(T->right);
printElement(T->Element);
}
}
101
http://www.francisxavier.ac.in
Example
Inorder :
Preorder:
Postorder:
In the above fig., the binary tree consists of a root and two sub trees T l & Tr. All nodes
to the left of the binary tree are referred as left subtrees and all nodes to the right of a binary
tree are referred to as right subtrees.
Implementation
A binary tree has at most two children; we can keep direct pointers to them. The
declaration of tree nodes is similar in structure to that for doubly linked lists, in that a node is
a structure consisting of the key information plus two pointers (left and right) to other nodes.
Binary Tree node declaration
typedef struct tree_node *tree_ptr;
struct tree_node
{
element_type element;
102
http://www.francisxavier.ac.in
tree_ptr left;
tree_ptr right;
};
typedef tree_ptr TREE;
Types of Binary Tree
i.
ii.
Skew tree
A skew tree is a binary tree in which every node except the leaf has only one
child node. There are two types of skew tree, they are left skewed binary tree and
right skewed binary tree.
Left skewed binary tree
A left skew tree has node with only the left child. It is a binary tree with only
left subtrees.
Right skewed binary tree
A right skew tree has node with only the right child. It is a binary tree with
only right subtrees.
A
iii.
iv.
A
B
v.
C
F
C
E
104
http://www.francisxavier.ac.in
2.
General Tree
Binary Tree
binary tree.
Application of trees
i.
ii.
iii.
Syntax Analysis
iv.
Grammar
v.
Expression Tree
105
http://www.francisxavier.ac.in
MakeEmpty
This operation is mainly for initialization when the programmer prefer to initialize the
first element as a one node tree.
Routine to make an empty tree
SearchTree MakeEmpty(SearchTree T)
{
if (T!=NULL)
{
MakeEmpty(T->Left);
MakeEmpty(T->Right);
free(T);
}
return NULL;
}
Find
This operation requires returning a pointer to the node in tree T that has key X or
NULL if there is no such node.
The find operation as follows.
106
http://www.francisxavier.ac.in
Example:
To find an element 4 (X = 4) in the below tree
In the first fig, the element 4 is checked with root 6. 4 is less than 6. So goto the left
subtree.
In the second fig, element 4 is checked with node 2. 4 is greater than 2. So goto the right
subtree.
In the third fig, the element 4 is checked with node 4. The element is equal. So return 4.
107
http://www.francisxavier.ac.in
FindMin
This operation returns the position of the smallest element in the tree. To find the
minimum element, start at the root and go left as long as there is a left child. The stopping
point is the smallest element.
Recursive routine for FindMin
{
if( T = = NULL )
if( T != NULL )
return NULL;
else
while(T->Left!=NULL)
return T;
T = T->Left;
else
}}
return FindMin ( T->Left ) ; }
return T; }
FindMax
This operation returns the position of the largest element in the tree. To find the
maximum element, start at the root and go right as long as there is a right child. The stopping
point is the largest element.
Recursive routine for FindMax
{
if( T = = NULL )
if( T != NULL )
return NULL;
else
while(T-
>Right!=NULL)
if( T->Right = = NULL )
return T;
T = T->Right;
else
}}
return FindMax ( T->Right ) ;
return T;
}
Insert
To insert the element X in to the tree, check with the root node T.
108
http://www.francisxavier.ac.in
If it is less than the root, traverse the left sub tree recursively until it reaches the T->Left
equals to NULL. Then X is placed in T->Left.
If it is greater than the root, traverse the right sub tree recursively until it reaches the
T->Right equals to NULL. Then X is placed in T->Right.
If X is found in the tree, do nothing.
Recursive routine to insert an element into a binary search tree
SearchTree Insert( ElementType X, SearchTree T )
{
if( T = = NULL )
{ /* Create and return a one-node tree */
T = malloc ( sizeof (struct TreeNode) );
if( T != NULL )
{
T-Element = X;
T->Left = T->Right = NULL;
}
}
else
{
if( X < T->Element )
T->Left = Insert( X, T->Left );
else
if( X > T->Element )
T->Right = Insert( X, T->Right );
/* else X is in the tree already. We'll do nothing */
return T;
}
Example: To insert 8, 4, 1, 6, 5, 7, 10
i.
ii.
iii.
Next insert element 1, 1<8 traverse towards left. 1<4 traverse towards left.
iv.
To insert element 6, 6<8 traverse towards left. 6>4, place it as right child of 4.
v.
To insert element 5, 5<8 traverse towards left. 5>4, traverse towards right. 5<6, place
8
4
it as left child of 6.
109
http://www.francisxavier.ac.in
1
5
10
6
vi.
To insert element 7, 7<8 traverse towards left. 7>4, traverse towards right. 7>6, place
it as right child of 6.
vii.
Delete
While deleting a node from a tree, the memory is to be released. Deletion operation
is the complex operation in the binary search tree. To delete a node from the tree consider the
following three possibilities.
Case 1: Node to be deleted is a leaf node
Case 2: Node with one child.
Case 3: Node with two children.
Case 1: Deleting the leaf node
Search the parent of the leaf node.
Make the parent link of the leaf node as NULL.
Release Memory.
Example: Deletion of node 6
8
4
1
4
6
Before deletion
After Deletion
8
5
4
1
else
if( X > T->Element ) /* Go right */
T->Right = Delete( X, T->Right );
else
else
{
TmpCell = T;
if( T->Left = = NULL )
T = T->Right;
if( T->Right = = NULL )
T = T->Left;
free(TmpCell );
}
return T;
}
Case 3: An insertion into the left sub tree of the right child of .
Case 4: An insertion into the right sub tree of the right child of .
These imbalances can be overcome by
i.
Single Rotation
ii.
Double Rotation
Case 1 and Case 4 imbalance (left left or right right) is fixed by a single rotation
of the tree. Case 2 and Case 3 imbalance (left right or right left) is fixed by double
rotation.
Single Rotation
Single rotation with left
The following fig. shows the single rotation that fixes case 1.
General Representation
The before picture is on the left, and the after is on the right. Node K2 violates the
AVL property because its left sub tree is two levels deeper than its right sub tree. Sub tree X
has grown to an extra level, causing it to be exactly two levels deeper than Z. To ideally
rebalance the tree, we would like to move X up a level and Z down a level. To do this we
rearrange nodes into an equivalent tree as shown in the second part of the above fig.
Eg: If we insert the value 1 in the left sub tree of the following tree it causes imblance
8
5
3
5
3
9
7
1
7
The above fig. shows that after the insertion of 1 into the original AVL tree on the
left, node 8 becomes unbalanced. Thus we do a single rotation between 5 and 8, obtaining
tree on the right.
Routine to perform single rotation with left
113
http://www.francisxavier.ac.in
child to fix the problem. The tree is shown in the following figure, before and after the
rotation:
Next, we insert the key 4, which causes no problems, but the insertion of 5 creates a
violation at node 3, which is fixed by a single rotation. Besides the local change caused by
the rotation, the programmer must remember that the rest of the tree must be informed of this
change. Here, this means that 2's right child must be reset to point to 4 instead of 3.
Next, we insert 6. This causes a balance problem for the root, since its left subtree is
of height 0, and its right subtree would be height 2. Therefore, we perform a single rotation at
the root between 2 and 4.
115
http://www.francisxavier.ac.in
The rotation is performed by making 2 a child of 4 and making 4's original left sub
tree the new right sub tree of 2. Every key in this sub tree must be positioned between 2 and
4, so this transformation makes sense. The next key we insert is 7, which causes another
rotation.
Double Rotation
Double Rotation with left
Double rotation with left is used to perform case 2. An insertion into the right sub tree
of the left child of node .
Double Rotation with left is performed by first performing single rotation with right,
and then performing single rotation with left.
Routine to perform double rotation with left
static Position DoubleRotateWithLeft( Position K3)
{
/* Rotate between K1 and K2 */
K3 -> Left = SingleRotateWithRight(K3 ->Left);
/* Rotate between K3 and K2 */
return SingleRotateWithLeft (K3);
}
Double Rotation with Right
Double rotation with right is used to perform case 4. An insertion into the left sub tree
of the right child of node .
Double Rotation with right is performed by first performing single rotation with left,
and then performing single rotation with right.
Routine to perform double rotation with right
static Position DoubleRotateWithRight( Position K1)
{
116
http://www.francisxavier.ac.in
Next we insert 13, which require a double rotation. Here the double rotation is again a rightleft double rotation that will involve 6, 14, and 7 and will restore the tree. In this case, k3 is
the node with key 6, k1 is the node with key 14, and k2 is the node with key 7.
If 12 is now inserted, there is an imbalance at the root. Since 12 is not between 4 and
7, we know that the single rotation will work.
117
http://www.francisxavier.ac.in
To insert 10, a single rotation needs to be performed, and the same is true for the subsequent
insertion of 9. We insert 8 without a rotation, creating the almost perfectly balanced tree that
follows.
4.2 B-TREES:
A B-tree is a tree data structure that keeps data sorted and allows searches, insertions,
and deletions in logarithmic amortized time. Unlike self-balancing binary search trees, it is
optimized for systems that read and write large blocks of data. It is most commonly used in
database and file systems.
The set formulation of the B-tree rules: Every B-tree depends on a positive constant integer
called MINIMUM, which is used to determine how many elements are held in a single node.
120
http://www.francisxavier.ac.in
Rule 1: The root can have as few as one element (or even no elements if it also has no
children); every other node has at least MINIMUM elements.
Rule 3: The elements of each B-tree node are stored in a partially filled array, sorted
from the smallest element (at index 0) to the largest element (at the final used position
of the array).
Rule 4: The number of subtrees below a nonleaf node is always one more than the
number of elements in the node.
o
1 of the node.
Rule 6: Every leaf in a B-tree has the same depth. Thus it ensures that a B-tree avoids
the problem of a unbalanced tree.
private int firstGE(int target), which returns the first location in the root such that
data[x] >= target. If there's no such location, then return value is dataCount.
122
http://www.francisxavier.ac.in
The above result is an illegal B-tree. Our plan is to perform a loose addition first, and then fix
the root's problem.
To fix a child with MAXIMIM + 1 elements, the child node is split into two nodes
that each contain MINIMUM elements. This leaves one extra element, which is
passed up to the parent.
It is always the middle element of the split node that moves upward.
The parent of the split node gains one additional child and one additional element.
The children of the split node have been equally distributed between the two smaller
nodes.
fixExcess(0).
return answer;
}
private boolean looseRemove(int target)
{
1. i = firstGE(target)
2. Deal with one of these four possibilities:
2a. if (root has no children and target not found) return false.
2b. if( root has no children but target found) {
remove the target
return true
}
2c. if (root has children and target not found) {
answer = subset[i].looseRemove(target)
if (subset[i].dataCount < MINIMUM)
fixShortage(i)
return true
}
2d. if (root has children and target found) {
data[i] = subset[i].removeBiggest()
if (subset[i].dataCount < MINIMUM)
fixShortage(i)
return true
}
}
125
http://www.francisxavier.ac.in
Case 1: Transfer an extra element from subset[i-1]. Suppose subset[i-1] has more than the
MINIMUM number of elements.
a. Transfer data[i-1] down to the front of subset[i].data.
b. Transfer the final element of subset[i-1].data up to replace data[i-1].
c. If subset[i-1] has children, transfer the final child of subset[i-1] over to the front of
subset[i].
Case 2: Transfer an extra element from subset[i+1]. Suppose subset[i+1] has more than the
MINIMUM number of elements.
Case 3: Combine subset[i] with subset[i-1]. Suppose subset[i-1] has only MINIMUM
elements
a. Transfer data[i-1] down to the end of subset[i-1].data.
126
http://www.francisxavier.ac.in
b. Transfer all the elements and children from subset[i] to the end of subset[i-1].
c. Disconnect the node subset[i] from the B-tree by shifting subset[i+1], subset[i+2] and
so on leftward.
Case 4: Combine subset[i] with subset[i+1]. Suppose subset[i+1] has only MINIMUM
elements. We may need to continue activating fixShortage() until the B-Tree rules are
satisfied.
127
http://www.francisxavier.ac.in
128
http://www.francisxavier.ac.in
structure
Balance is preserved by painting each node of the tree with one of two colors (typically called
'red' and 'black') in a way that satisfies certain properties, which collectively constrain how
unbalanced the tree can become in the worst case. When the tree is modified, the new tree is
subsequently rearranged and repainted to restore the coloring properties. The properties are
designed in such a way that this rearranging and recoloring can be performed efficiently.
A binary search tree is a red-black tree if:
1. Every node is either red or black
2. Every leaf (nil) is black
3. If a node is red, then both its children are black
4. Every simple path from a node to a descendant leaf contains the same number of black
nodes
Black-height of a node x, bh(x), is the number of black nodes on any path from x to a
leaf, not counting x
Case 1
129
http://www.francisxavier.ac.in
Case 2
Transform to case 3
Case 3
Example:
131
http://www.francisxavier.ac.in
Case 1
Case 2a
Case 2b
132
http://www.francisxavier.ac.in
Case 3
Transforms to case 4
Case 4
133
http://www.francisxavier.ac.in
Does not require any accounting information (color, level, height, etc.)
O(N) worst case for any single operation, but O(log N) average case
Frequently-accessed keys are moved up so that they are near the root.
Splay Operation:
Fundamental operation is the splay: this operation re-arranges the tree to make a
specified node the root. A side-effect of the splay is that imbalances in the tree are corrected.
The simplest approach to splaying is the bottom-up approach, which is similar to the
rebalancing operations we have seen for AVL, Red-Black, and AA-trees. On some path from
a selected node back to the root, rotations are performed.
There are three cases for splaying. (Note that only the "left" cases are shown: there are
symmetric "right" cases.)
Zig
Zig-Zig
Zig-Zag
Zig Step:
This step is done when p is the root. The tree is rotated on the edge between x and p.
Zig steps exist to deal with the parity issue and will be done only as the last step in a splay
operation and only when x has odd depth at the beginning of the operation.
134
http://www.francisxavier.ac.in
Zig-zig Step:
This step is done when p is not the root and x and p is either both right children or are
both left children. The picture below shows the case where x and p are both left children. The
tree is rotated on the edge joining p with its parent g, then rotated on the edge
joining x with p.
Zig-zag Step:
This step is done when p is not the root and x is a right child and p is a left child or
vice versa. The tree is rotated on the edge between p and x, and then rotated on the resulting
edge between x and g.
Overall splaying algorithm (bottom-up): splaying continues until the node X is the
root of the overall tree.
A splay operation is performed after each access. We recursively apply the splaying
strategy until the node of our interest reaches the root.
135
http://www.francisxavier.ac.in
Insertion: X is the newly inserted node (and will become the root after splaying).
Search:
o
If X is in the tree, X is the node found (and will become the root after
splaying).
If X is not in the tree, the last node accessed prior to reaching the NULL
pointer is splayed.
Deletion:
o
First, X is the node to be deleted. Splay it to the root (and delete it).
Then for the two subtrees L and R (left and right), we find the largest element
in L. Splay it to the root of L. At this point, L's (new) root has no right child.
Example: Delete 6
136
http://www.francisxavier.ac.in
Binomial trees
The binomial tree Bk is an ordered tree defined recursively. As shown in figure(a), the
binomial tree B0 consists of a single node. The binomial tree Bk consists of two binomial
trees Bk-1 that are linked together: the root of one is the leftmost child of the root of the other.
Figure (b) shows the binomial trees B0 through B4.
Figure (a) The recursive definition of the binomial tree Bk. Triangles represent rooted
subtrees. (b) The binomial trees Bo through B4. Node depths in B4 are shown. (c)
Another way of looking at the binomial tree Bk.
Some properties of binomial trees are as follows.
For the binomial tree Bk,
1. There are 2k nodes,
137
http://www.francisxavier.ac.in
4. The root has degree k, which is greater than that of any other node; moreover if the
children of the root are numbered from left to right by k - 1, k - 2, . . . , 0, childi is the root of
a subtree Bi.
Definition:
A binomial heap H is a set of binomial trees that satisfies the following binomial-heap
properties.
Each binomial tree in H is heap-ordered: the key of a node is greater than or equal to
the key of its parent.
There is at most one binomial tree in H whose root has a given degree.
Figure. A binomial heap H with n = 13 nodes. (a) The heap consists of binomial trees B 0,
B2, and B3, which have 1, 4, and 8 nodes respectively, totaling n = 13 nodes. Since each
binomial tree is heap-ordered, the key of any node is no less than the key of its parent.
Also shown is the root list, which is a linked list of roots in order of increasing degree.
138
http://www.francisxavier.ac.in
(b) A more detailed representation of binomial heap H. Each binomial tree is stored in
the left-child, right-sibling representation, and each node stores its degree.
Figure (a) shows a binomial heap H with 13 nodes. The binary representation of 13 is 1101
, and H consists of heap-ordered binomial trees B3, B2, and B0, having 8, 4, and 1 nodes
respectively, for a total of 13 nodes.
Figure: The binomial tree B4 with nodes labeled in binary by a postorder walk.
Above figure also shows, the roots of the binomial trees within a binomial heap are
organized in a linked list, which we refer to as the root list. The degrees of the roots strictly
increase as we traverse the root list. By the second binomial-heap property, in an n-node
binomial heap the degrees of the roots are a subset of {0, 1, . . . , 1g n }. The sibling field
has a different meaning for roots than for nonroots. If x is a root, then sibling[x] points to the
next root in the root list. (As usual,sibling[x] = NIL if x is the last root in the root list.)
A given binomial heap H is accessed by the field head[H], which is simply a pointer
to the first root in the root list of H. If binomial heap H has no elements, thenhead[H] = NIL.
139
http://www.francisxavier.ac.in
(1).
BINOMIAL-HEAP-MINIMUM(H)
1 y
NIL
2 x
head[H]
3 min
4 while x
NIL
then min
7
8
y
x
key[x]
sibling[x]
9 return y
Since a binomial heap is heap-ordered, the minimum key must reside in a root node.
The BINOMIAL-HEAP-MINIMUM procedure checks all roots, which number at most lg n
+ 1, saving the current minimum in min and a pointer to the current minimum in y. When
called on the binomial heap of Figure 20.3, BINOMIAL-HEAP-MINIMUM returns a pointer
to the node with key 1.
Because there are at most lg n + 1 roots to check, the running time of BINOMIALHEAP-MINIMUM is O(lg n).
140
http://www.francisxavier.ac.in
node y to the Bk-1 tree rooted at node z; that is, it makes zthe parent of y. Node z thus
becomes the root of a Bk tree.
BINOMIAL-LINK(y,z)
1 p[y]
2 sibling[y]
3 child[z]
4 degree[z]
child[z]
y
degree[z]+1
The BINOMIAL-LINK procedure makes node y the new head of the linked list of
node z's children in O(1) time. It works because the left-child, right-sibling representation of
each binomial tree matches the ordering property of the tree: in a Bk tree, the leftmost child of
the root is the root of a Bk-1 tree.
The following procedure unites binomial heaps H1 and H2, returning the resulting
heap. It destroys the representations of H1 and H2 in the process. Besides BINOMIAL-LINK,
the procedure uses an auxiliary procedure BINOMIAL-HEAP-MERGE that merges the root
lists of H1 and H2 into a single linked list that is sorted by degree into monotonically
increasing order.
141
http://www.francisxavier.ac.in
Case 1:
Case 1, shown in Figure (a), occurs when degree[x]
when x is the root of a Bk-tree and next-x is the root of a Bl-tree for some l >k.
Lines 11-12 handle this case. We don't link x and next-x, so we simply march the
pointers one position further down the list. Updating next-x to point to the node following the
new node x is handled in line 21, which is common to every case.
Case 2:
Case 2, shown in Figure (b), occurs when x is the first of three roots of equal degree,
that is, when degree[x] = degree[next-x] = degree[sibling[next-x]].
We handle this case in the same manner as case 1: we just march the pointers one
position further down the list. Line 10 tests for both cases 1 and 2, and lines 11-12 handle
both cases.
Cases 3 and 4:
Cases 3 and 4 occur when x is the first of two roots of equal degree, that is, when
degree[x] = degree[next-x]
degree[sibling[next-x]].
These cases may occur on the next iteration after any case, but one of them always
occurs immediately following case 2. In cases 3 and 4, we link x and next-x. The two cases
are distinguished by whether x or next-x has the smaller key, which determines the node that
will be the root after the two are linked.
In case 3, shown in Figure (c), key[x]
removes next-x from the root list, and line 15 makes next-x the leftmost child of x.
142
http://www.francisxavier.ac.in
143
http://www.francisxavier.ac.in
In case 4, shown in Figure (d), next-x has the smaller key, so x is linked to nextx. Lines 16-18 remove x from the root list, which has two cases depending on whether x is the
first root on the list (line 17) or is not (line 18). Line 19 then makes x the leftmost child
of next-x, and line 20 updates x for the next iteration.
Following either case 3 or case 4, the setup for the next iteration of the while loop is
the same. We have just linked two Bk-trees to form a Bk+l-tree, which x now points to. There
were already zero, one, or two other Bk+1-trees on the root list from the output of
BINOMIAL-HEAP-MERGE, so x is now the first of either one, two, or three Bk+l-trees on
the root list. If x is the only one, then we enter case 1 in the next iteration: degree [x]
degree[next-x]. If x is the first of two, then we enter either case 3 or case 4 in the next
iteration. It is when x is the first of three that we enter case 2 in the next iteration.
144
http://www.francisxavier.ac.in
remove next-x from the root list and link it to x, creating a Bk+1-tree. (d) Case 4:
degree[x] = degree[next-x]
remove x from the root list and link it to next-x, again creating a B k+1-tree.
Inserting a node
The following procedure inserts node x into binomial heap H, assuming of course that
node x has already been allocated and key[x] has already been filled in.
BINOMIAL-HEAP-INSERT(H,x)
1 H'
2 p[x]
MAKE-BINOMIAL-HEAP()
NIL
3 child[x]
NIL
4 sibling[x]
NIL
5 degree[x]
6 head[H']
7 H
BINOMIAL-HEAP-UNION(H,H')
1 find the root x with the minimum key in the root list of H,
and remove x from the root list of H
2 H'
MAKE-BINOMIAL-HEAP()
BINOMIAL-HEAP-UNION(H,H')
5 return x
146
http://www.francisxavier.ac.in
Since each of lines 1-4 takes O(lg n) time if H has n nodes, BINOMIAL-HEAPEXTRACT-MIN runs in O(lg n) time.
Decreasing a key
The following procedure decreases the key of a node x in a binomial heap H to a new
value k. It signals an error if k is greater than x's current key.
BINOMIAL-HEAP-DECREASE-KEY (H,x,k)
1 if k > key[x]
2
3 key[x]
4 y
5 z
p[y]
6 while z
7
do exchange key[y]
key[z]
10
p[y]
Deleting a key
It is easy to delete a node x's key and satellite information from binomial heap H in O(lg n)
time. The following implementation assumes that no node currently in the binomial heap has
a key of -
As shown in below figure, this procedure decreases a key in the same manner as in a
binary heap: by "bubbling up" the key in the heap. After ensuring that the new key is in fact
no greater than the current key and then assigning the new key to x, the procedure goes up the
tree, with y initially pointing to node x. In each iteration of the while loop of lines 610, key[y] is checked against the key of y's parent z. If y is the root or key[y]
key[z], the
binomial tree is now heap-ordered. Otherwise, node y violates heap ordering, so its key is
exchanged with the key of its parent z, along with any other satellite information. The
procedure then sets y toz, going up one level in the tree, and continues with the next iteration.
The BINOMIAL-HEAP-DECREASE-KEY procedure
takes O(lg n)
time.
By
property 2 of Lemma 20.1, the maximum depth of x is lg n , so the while loop of lines 6-10
iterates at most lg n times.
147
http://www.francisxavier.ac.in
BINOMIAL-HEAP-DELETE(H,x)
1 BINOMIAL-HEAP-DECREASE-KEY(H,x,-
2 BINOMIAL-HEAP-EXTRACT-MIN(H)
associated satellite information up to a root by calling BINOMIAL-HEAP-DECREASEKEY. This root is then removed from H by a call of BINOMIAL-HEAP-EXTRACT-MIN.
The BINOMIAL-HEAP-DELETE procedure takes O(lg n) time.
148
http://www.francisxavier.ac.in
Data Structures:
Children of a node are linked together in a Circular, doubly linked List, E.g node 52
Min [H]
3
23
17
18
52
38
39
41
30
24
26
46
35
149
http://www.francisxavier.ac.in
Node Pointers
- left [x]
- right [x]
- degree [x]
- number of children in the child list of x
- mark [x]
Properties:
Support Mergeable heap operations such as Insert, Minimum, Extract Min, and Union
in constant time O(1)
Desirable when the number of Extract Min and Delete operations is small relative to
the number of other operations.
Most asymptotically fastest algorithms for computing minimum spanning trees and
finding single source shortest paths make use of the Fibonacci heaps.
Find Min
Insert
150
http://www.francisxavier.ac.in
Example:
Min [H]
3
23
17
18
52
38
39
24
30
26
41
46
35
Red Nodes are marked nodes they will become relevant only in the delete operation.
Min [H]
3
23
21
17
18
52
39
38
30
41
24
26
46
35
Remove the minimum node from the root list, min ptr to right(x)
Consolidate the root list by linking roots of equal degree and key[x] <= key[y], until
every root in the root list has a distinct degree value. (uses auxiliary array)
151
http://www.francisxavier.ac.in
21
24
17
18
38
52
39
23
41
26
46
30
35
Root Nodes are stored in the auxiliary array in the index corresponding to their
degree.
21
26
24
17
46
30
23
w,x
18
39
52
38
41
35
152
http://www.francisxavier.ac.in
There are no root nodes of index 2, so that array entry remains empty
Now the algorithm iterates through the auxiliary array and links roots of equal degree
such that key[x] <= key[y],
0
A
w,x
21
26
24
17
46
30
18
38
52
39
23
41
35
Nodes 21 and 52 are linked so that node 21 has degree 1, and it is then linked to node
18
0
A
w,x
18
26
24
17
46
30
23
21
38
39
41
52
35
153
http://www.francisxavier.ac.in
But there are now no more remaining root nodes of duplicate degrees.
18
26
24
17
46
30
23
38
21
w,x
41
39
52
35
min [H]
18
26
24
17
46
30
23
21
38
39
41
52
35
154
http://www.francisxavier.ac.in
At some point it was a root then it was linked to anther node and 1 child of it has been
cut.
Decrease-Key
Check that new key is not greater than current key, then assign new key to x
Else
o Cut x: make x a root, remove link from parent y, clear marked field of x
o Perform a Cascading cut on xs parent y (relevant if parent is marked):
if y is a root, return
The Cascading cut procedure recurses its way up the tree until a root, or an unmarked
node is found.
Example:
18
26
24
17
46
30
23
21
38
39
41
52
35
155
http://www.francisxavier.ac.in
Since the new key of node 46 is 15 it violates the min-heap property, so it is cut
and put on the root.
15
24
17
18
23
30
21
38
39
41
52
26
35
So node 5 is cut and place on the root list because again the heap property is violated.
But the parent of node 35, which is node 26, is marked, so we have to perform a
cascading cut.
min [H]
15
24
17
26
30
18
23
21
38
39
41
52
The cascading cut involves cutting the marked node, 26, unmarking it, and putting it
on the root list.
156
http://www.francisxavier.ac.in
We now repeat this procedure (recurse) on the marked nodes parent until we hit a
node on the root list.
min [H]
15
26
24
18
17
23
21
30
38
39
41
52
Since the next node encountered in the cascading cut procedure is a root node node
7 the procedure terminates.
min [H]
15
26
24
17
18
23
21
30
38
39
41
52
Delete-Key
Call Extract-Min on H.
157
http://www.francisxavier.ac.in
The Disjoint Set data structure solves a specific problem that is interesting both
theoretically and practically. The problem is as follows:
You have a collection of n items, which you number from 0 to n-1. These items will
be partitioned into some number of sets. The sets are "disjoint" which means that no item
belongs to more than one set. All items belong to some set though (hence the use of the word
"partition.").
int Find(i): This takes the number of an item and returns its "set id." This is a number
between zero and n-1. You don't really care what the number is; however,
if i and j belong to the same disjoint set, then Find(i) will equal Find(j).
int Union(id1, id2): This takes the set id's of two different sets, and performs the
union operation on them, coalescing them into one set. It returns the set id of this new
set. Now, when you call Find() on any item that was previously in either of these sets,
it will return this new set id.
int Disjoint::Union(int s1, int s2)
{
int p, c;
if (links[s1] != -1 || links[s2] != -1) {
cerr << "Must call union on a set, and not just an element.\n";
158
http://www.francisxavier.ac.in
exit(1);
}
if (ranks[s1] > ranks[s2])
{
p = s1;
c = s2;
}
else
{
p = s2;
c = s1;
}
links[c] = p;
ranks[p] += ranks[c]; /* HERE */
return p;
}
Abstract implementation
In the abstract, we implement disjoint sets with circles and arrows, which we'll call
"nodes" and links. Each element will be in its own node, and each element has one link. That
link is either to another node, or to NULL. In the beginning, when you first set up an instance
of disjoint sets, all nodes will have NULL links.
When a node has a NULL link, we call it the "root" of a set. If you call Find() on a
node with a NULL link, it will return the node's item number, and that is the set id of the
node. Therefore, when you first start, every node is the root of its own set, and when you
call Find(i), it will return i.
When you call Union(i, j), remember that i and j must be set id's. Therefore, they must
be nodes with NULL links. What you do is have one of those nodes set its link to the other
node.
Example:
We initialize an instance of disjoint sets with 10 items. Each item is a node with a
number from 0 to 9. Each node has a NULL link, which we depict by not drawing any arrows
from the node:
159
http://www.francisxavier.ac.in
Again, each node is in its own set, and each node's set id is its number. Suppose we
call Union(0, 1), Union(2, 3) and Union(4, 5). These will each set one of the node's link to the
other node. We'll talk about how that gets done later. However, suppose this is the result:
Node 0's link has been set to 1. Both of these nodes' set ids are now 1, which
means Find(0) equals Find(1) equals one. Similarly, Find(2) equalsFind(3) equals three. This
gives you a clue about implementing Find(). When you call Find(n), what you do is keep
setting n to n's link, until n's link is NULL. At that point, you are at the root of the set, and
you return n.
Union is pretty simple, too, but you have some choices about how to determine which
node sets its link field to the other. We use three methods to do this:
1. Union by size: Make the node whose set size is smaller point to the node whose set
size is bigger. Break ties arbitrarily.
2. Union by height: The height of a set is the longest number of links that it takes to get
from one of its items to the root. With union by height, you make the node whose
height is smaller point to the node whose height is bigger.
3. Union by rank with path compression: This works exactly like union by height,
except you don't really know the height of the set. Instead, you use the set's rank,
which is what its height would be, were you using union by height. The reason that
you don't know the height is that when you call Find(i) on an element, you perform
"path compression."
160
http://www.francisxavier.ac.in
There are two sets, with set id's 5 and 9. Now, suppose you call Find(0). It will return
five, but along the way to the root node of its set, it encounters nodes 1 and 3. Before
returning five, it sets the links to 0, 1 and 3 to five:
Running Time:
We assume the number of items in the instance of disjoint sets is n:
Not just consider one operation, but a sequence of operations on a given data
structure.
Probabilistic analysis:
161
http://www.francisxavier.ac.in
Average case running time: average over all possible inputs for one algorithm
(operation).
Amortized analysis:
No involvement of probability
Aggregate analysis:
Accounting method:
Potential method:
Stack operations:
PUSH(S,x), O(1)
POP(S), O(1)
MULTIPOP(S,k), min(s,k)
while not STACK-EMPTY(S) and k>0
do POP(S)
k=k-1
The worst case cost for MULTIPOP in the sequence is O(n), since the stack
size is at most n.
Thus the cost of the sequence is O(n2). Correct, but not tight.
162
http://www.francisxavier.ac.in
Idea:
When amortized cost > actual cost, the difference is saved in specific objects
as credits.
The credits can be used by later operations whose amortized cost < actual cost.
Conditions:
Suppose actual cost is ci for the ith operation in the sequence, and amortized
cost is ci',
Since we want to show the average cost per operation is small using
amortized cost, we need the total amortized cost is an upper bound of
total actual cost.
Actual costs:
When pushing a plate, use one dollar to pay the actual cost of the push and
leave one dollar on the plate as credit.
Whenever POPing a plate, the one dollar on the plate is used to pay the actual
cost of the POP. (Same for MULTIPOP).
163
http://www.francisxavier.ac.in
The total amortized cost for n PUSH, POP, MULTIPOP is O(n), thus O(1) for
average amortized cost for each operation.
Conditions hold: total amortized cost total actual cost, and amount of credits never
becomes negative.
Let $1 represent each unit of cost (i.e., the flip of one bit).
Whenever a bit is set, use $1 to pay the actual cost, and store another $1 on the bit as
credit.
At any point, a 1 in the counter stores $1, the number of 1s is never negative, and so
is the total credit.
At most one bit is set in each operation, so the amortized cost of an operation is at
most $2.
The potential is associated with the data structure as a whole rather than with specific
objects within the data structure.
Initial data structure D0, n operations, resulting in D0, D1,, Dn with costs c1, c2,,
cn.
If (Dn) (D0), then total amortized cost is an upper bound of total actual cost.
But we do not know how many operations, so (Di) (D0) is required for any i.
164
http://www.francisxavier.ac.in
If the potential change is positive (i.e., (Di) - (Di-1)>0), then ci' is an overcharge
(so store the increase as potential), otherwise, undercharge (discharge the potential to
pay the actual cost).
PUSH:
POP:
MULTIPOP(S,k): k'=min(s,k)
So amortized cost of each operation is O(1), and total amortized cost of n operations
is O(n).
Since total amortized cost is an upper bound of actual cost, the worse case cost of n
operations is O(n).
Define the potential of the counter after the ith INCREMENT is (Di) =bi, the
number of 1s. clearly, (Di)0.
Each object can be POP only once (including in MULTIPOP) for each time it is
PUSHed. #POPs is at most #PUSHs, which is at most n.
do A[i]0
4.
(flip, reset)
ii+1
5. if i<k
6. then A[i]1
(flip, set)
166
http://www.francisxavier.ac.in
Thus the worst case running time is O(n) for a sequence of n INCREMENTs.
167
http://www.francisxavier.ac.in
UNIT V
GRAPHS
Representation of Graphs Breadth-first search Depth-first search Topological sort
Minimum Spanning Trees Kruskal and Prim algorithm Shortest path algorithm
Dijkstras algorithm Bellman-Ford algorithm Floyd - Warshall algorithm.
V2
V4
V3
In the above graph V1, V2, V3, V4 are the vertices and (V1, V2), (V2, V3), (V3, V4),
(V4, V1), (V1, V3), (V2, V4) are the edges.
V2
Undirected Graph
An undirected graph is a graph, which consists of undirected edges. In undirected
graph, the edges are undirected or two way. If (v,w) is a undirected edge, then (v,w) = (w,v).
V1
V2
V3
168
http://www.francisxavier.ac.in
Weighted Graph
A graph is said to be weighted graph if every edge in the graph is assigned a weight or
value. It can be directed or undirected.
2
V1
5
V2
V1
6
V3
V2
7
V3
Subgraph
A subgraph of a graph G = (V,E) is a graph G` = (V`, E) such that V` V and E E.
Symmetric digraph
A symmetric digraph is a directed graph such that for every edge vw there is also a
reverse edge wv.
Complete Graph
A complete graph is a graph in which there is an edge between every pair of vertices.
A complete graph with n vertices will have n(n-1)/2.
V1
V2
Number of vertices is 4
Number of edges is 6
V4
V3
Path
A path in a graph is defined as a sequence of verices w1, w2, w3, . . . , wn such that
169
http://www.francisxavier.ac.in
Length
The length of a path in a graph is the number of edges on the path, which is equal to
N1. Where N is the number of vertices.
Length of the path from A to B is {A, B} = 1
Length of the path from A to C is {A, C} = 1 & {A, B, C} = 2.
If there is a path from a vertex to itself with no edges then the path length is 0.
Length of the path from A->A & B -> B is 0.
Loop
A loop in a graph is defined as the path from a vertex to itself. If the graph contains an
edge (v,v) from a vertex to itself, then the path v, v is sometimes referred to as a loop.
Simple Path
A simple path is a path such that all vertices are distinct (different), except that the
first and last vertexes are same.
Simple path for the above graph {A, B, C, D, A}. First and Last vertex are the same ie. A
Cycle
A cycle in a graph is a path in which the first and the last vertex are the same.
Cyclic Graph
A graph which has cycles is referred to as cyclic graph. A graph is said to be cyclic, if
the edges in the graph should form a cycle.
Acyclic Graph
A graph is said to be acyclic, if the edges in the graph does not form a cycle.
Degree
The number of edges incident on a vertex determines its degree. The degree of the
vertex V is written as degree (V).
Indegree : The indegree of the vertex V, is the number of edges entering into the vertex V.
Outdegree: The outdegree of the vertex V, is the number of edges exiting from the vertex V.
Indegree of vertex V1 = 2
V1
V2
Outdegree of vertex V1 = 1
Indegree of vertex V2 = 1
V4
V3
Outdegree of vertex V2 = 2
Representation of Graph
A Graph can be represented in two ways.
i.
Adjacency Matrix
ii.
Adjacency List
ii.
iii.
V3
V2
V4
V4
V2
V3
171
http://www.francisxavier.ac.in
V2
V3
V4
8
Here Aij = Cij if there exists an edge from Vi to Vj. (Cij is the weight or cost).
Aij = 0, if there is no edge.
If there is no arc from i to j, C[i,j] = , where i j.
Advantage
Simple to implement.
Disadvantage
Takes O(n2) space to represents the graph.
Takes O(n2) time to solve most of the problem.
172
http://www.francisxavier.ac.in
The BFS(G, s) algorithm develops a breadth-first search tree with the source vertex, s,
as its root. The parent or predecessor of any other vertex in the tree is the vertex from which
it was first discovered. For each vertex, v, the parent of v is placed in the variable [v].
Another variable, d[v], computed by BFS contains the number of tree edges on the path from
s to v. The breadth-first search uses a FIFO queue, Q, to store gray vertices.
do color[u] WHITE
173
http://www.francisxavier.ac.in
3.
d[u] infinity
4.
[u] NIL
5.
color[s] GRAY
6.
d[s] 0
initialize
7.
[s] NIL
initialize
8.
Q {}
9.
ENQUEUE(Q, s)
10
while Q is non-empty
Clear queue Q
do u DEQUEUE(Q)
11.
12.
13.
14.
15.
d[v] d[u] + 1
16.
[v] u
17.
ENQUEUE(Q, v)
18.
DEQUEUE(Q)
19.
color[u] BLACK
Example:
The following figure illustrates the progress of breadth-first search on the undirected
sample graph.
a. After initialization (paint every vertex white, set d[u] to infinity for each vertex u, and set
the parent of every vertex to be NIL), the source vertex is discovered in line 5. Lines 8-9
initialize Q to contain just the source vertex s.
b. The algorithm discovers all vertices 1 edge from s i.e., discovered all vertices (w and r) at
level 1.
174
http://www.francisxavier.ac.in
c.
d. The algorithm discovers all vertices 2 edges from s i.e., discovered all vertices (t, x, and v)
at level 2.
e.
f.
g. The algorithm discovers all vertices 3 edges from s i.e., discovered all vertices (u and y) at
level 3.
h.
i. The algorithm terminates when every vertex has been fully explored.
175
http://www.francisxavier.ac.in
Vertices
from
which
exploration
FIFO order.
177
http://www.francisxavier.ac.in
while( !IsEmpty( Q ) )
{
V = Dequeue( Q );
TopNum[V] = ++Counter; /* assign next number */
for each W adjacent to V
if( --Indegree[W] = 0 )
Enqueue( W, Q );
}
/
Adjacency Matrix
178
http://www.francisxavier.ac.in
Solution
1. Number of 1s present in each column of adjacency matrix represents the indegree of
the corresponding vertex.
Indegree [V1] = 0
Indegree [V5] = 1
Indegree [V2] = 1
Indegree [V6] = 3
Indegree [V3] = 2
Indegree [V7] = 2
Indegree [V4] = 3
2. Enqueue the vertex, whose indegree is 0 and place it on the queue. Indegree of V1 is
0. So place it in the queue.
3. Dequeue the vertex V1 from the queue and decrement the indegrees of its adjacent
vertex V2 & V3. Indegree [V2] = 0, Indegree [V3] = 1. Now enqueue vertex V2
because its indegree is 0.
4. Dequeue the vertex V2 from the queue and decrement the indegrees of its adjacent
vertex V2 & V3. Indegree [V2] = 0, Indegree [V3] = 1. Now enqueue vertex V2
because its indegree is 0.
Vertex
-------------------------------------------------------------------------------v1
v2
v3
v4
v5
v6
v7
------------------------------------------------------------------------------enqueue v1
v2
v5
v4
v3
v7
v6
--------------------------------------------------------------------------------------------------------------------
dequeue v1
v2
v5
v4
v3
v7
v6
Algorithm Analysis
The running time of this algorithm is O(|E| + |V|) where E represents the Edges and
v represents the vertices of the graph.
179
http://www.francisxavier.ac.in
A Spanning tree of an undirected graph, G is a tree formed from graph edges that
connects all vertices of G.
A minimum spanning tree exists if and only if G is connected. The number of edges
in the minimum spanning tree is |V| -1.
The minimum spanning tree can be created using two algorithms, that is prims
algorithm and kruskals algorithm.
Procedure
i.
Initially there are |V| single node trees. Each vertex is initially in its own set.
ii.
Select the edges (u,v) in the order of smallest weight and accepted if it does not cause
the cycle.
iii.
iv.
The strategy
i.
The edges are built into a minheap structure and each vertex is considered as a sigle
node tree.
ii.
The deletemin operation is used to find the minimum cost edge (u,v).
iii.
The vertices u and v are searched in the spanning tree set S and if the returned sets are
not same then (u,v) is added to the set s with the constraint that adding (u,v) will not
create a cycle in spanning tree set S.
iv.
Repeat step (ii) and (iii) until a spanning tree is constructed with |V| - 1 edges.
Example: Find the minimum spanning tree for the following graph.
180
http://www.francisxavier.ac.in
i.
ii.
Select the smallest edge v1 to v4, both the nodes are different sets, it does not form
cycle.
iii.
Select the next smallest edge v6 to v7. These two vertices are different sets; it does
not form a cycle, so it is included in the MST.
iv.
Select the next smallest edge v1 to v2. These two vertices are different sets; it does
not form a cycle, so it is included in the MST.
v.
Select the next smallest edge v3 to v4. These two vertices are different sets; it does
not form a cycle, so it is included in the MST.
vi.
Select the next smallest edge v2 to v4 both v2 and v4 are same set, it forms cycle so
v2 v4 edge is rejected.
vii.
Select the next smallest edge v1 to v3, it forms cycle so v1 v3 edge is rejected.
viii.
Select the next smallest edge v4 to v7, it does not form a cycle so it is included in the
tree.
ix.
Select the next smallest edge v3 to v6, it forms a cycle so v3 v6 edge is rejected.
x.
Select the next smallest edge v5 to v7, it does not form a cycle so it is included in the
tree.
Edge
Weight
Action
---------------------------(v1,v4)
Accepted
(v6,v7)
Accepted
(v1,v2)
Accepted
(v3,v4)
Accepted
(v2,v4)
Rejected
(v1,v3)
Rejected
(v4,v7)
Accepted
181
http://www.francisxavier.ac.in
(v3,v6)
Rejected
(v5,v7)
Accepted
ReadGraphIntoHeapArray( G, H );
BuildHeap( H );
182
http://www.francisxavier.ac.in
EdgesAccepted = 0;
while( EdgesAccepted < NumVertex-1 )
{
e = DeleteMin( H );
uset = Find( u, S );
vset = Find( v, S );
if( uset != vset )
{
/* accept the edge */
EdgesAccepted++;
SetUnion( S, uset, vset );
}
}
}
5.6.2 PRIMS ALGORITHM
In this method, minimum spanning tree is constructed in successive stages. One node
is picked as a root and an edge is added (i.e) an associated vertex is added to the tree, until
all the vertices are present in the tree with |V| - 1 edges.
The Strategy
i.
One node is picked as a root node (u) from the given connected graph.
ii.
At each stage choose a new vertex v from u, by considering an edge (u,v) with
minimum cost among all edges from u, where u is already in the tree ad v is not in
the tree.
iii.
The prims algorithm table is constructed with three parameters. They are
known known vertex i.e., processed vertex is indicated by 1. Unknown
vertex is indicated by zero.
dv - Weight of the shortest arc connecting v to the known vertex.
pv - It contains last vertex (i.e.,) current vertex to cause a change in dv.
iv.
After selecting the vertex v, the update rule is applied for each unknown w adjacent
to v. The rule is dw = min (dw , Cw,v), that is more than one path exist between v to w
then dw is updated with minimum cost.
183
http://www.francisxavier.ac.in
Example:
i.
v1 is selected as initial node in the spanning tree and construct initial configuration of the
table.
ii.
Known
dv
pv
V1
V2
V3
V4
V5
V6
V7
v1 is declared as known vertex. Then its adjacent vertices v2, v3, v4 are updated.
T[v2].dist = min(T[v2].dist, Cv1,v2) = min ( ,2) = 2
T[v3].dist = min(T[v3].dist, Cv1,v3) = min ( ,4) = 2
T[v4].dist = min(T[v4].dist, Cv1,v4) = min ( ,1) = 2
Known
dv
pv
V1
V2
V1
V3
V1
V4
V1
V5
V6
V7
184
http://www.francisxavier.ac.in
iii.
Among all adjacent vertices V2, V3, V4. V1 -> V4 distance is small. So V4 is selected and
declared as known vertex. Its adjacent vertices distance are updated.
V1 is not examined because it is known vertex.
No change in V2 , because it has dv = 2 and the edge cost from V4 -> V2 = 3.
T[v3].dist = min(T[v3].dist, Cv4,v3) = min (4 ,2) = 2
T[v5].dist = min(T[v5].dist, Cv4,v5) = min ( ,7) = 7
T[v6].dist = min(T[v6].dist, Cv4,v6 ) = min ( ,8) = 8
T[v7].dist = min(T[v7].dist, Cv4,v7 ) = min ( ,4) = 4
iv.
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V4
V7
V4
V7
V4
Among all either we can select v2, or v3 whose dv = 2, smallest among v5, v6 and v7.
v2 is declared as known vertex.
Its adjacent vertices are v1, v4 and v5. v1, v4 are known vertex, no change in their
dv value.
T[v5].dist = min(T[v5].dist, Cv2,v5) = min (7 ,10) = 7
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V4
V7
V4
185
http://www.francisxavier.ac.in
v.
Among all vertices v3s dv value is lower so v3 is selected. v3s adjacent vertices are v1,
v4 and v6. No changes in v1 and v4.
vi.
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V3
V7
V4
Among v5, v6, v7, v7s dv value is lesser, so v7 is selected. Its adjacent vertices are v4,
v4, and v6. No change in v4.
T[v5].dist = min(T[v5].dist, Cv7,v5) = min (7,6) = 6
T[v6].dist = min(T[v6].dist, Cv7,v6) = min (5,1) = 1
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V7
V6
V7
V7
V4
186
http://www.francisxavier.ac.in
vii.
Among v5 and v6, v6 is declared as known vertex. v6s adjacent vertices are v3, v4, and
v7, no change in dv value, all are known vertices.
viii.
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V7
V6
V7
V7
V4
Finally v5 is declared as known vertex. Its adjacent vertices are v2, v4, and v7, no change in
dv value, all are known vertices.
v
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V7
V6
V7
V7
V4
Page 187
Algorithm Analysis
The runnig time is O(|V|2) in case of adjacency list and O(|E| log |V|) in case of binary
heap.
Prims Algorithm
i.
ii.
iii.
Find the smallest edge from the graph connecting the edge of the vertex in the tree,
such that it does not form a cycle.
iv.
v.
Repeat step 3 until the tree contains all the vertices in the graph.
http://www.francisxavier.ac.in
Page 188
ii.
dv it specifies distance form source to vertex. Initially all vertices are unreachable
except for S whose path length is zero.
iii.
pv It is book keeping variable, which allow us to print te actual path. i.e., the vertex
which makes the changes in dv.
ii.
Dequeue the vertex S from queue and assign the value of that vertex to be known and
then find its adjacency vertices.
iii.
If the distance of the adjacent vertices is equal to infinity then change the distance of
that vertex as the distance of its source vertex. Increment by 1 and enqueue the vertex.
iv.
Page 189
MakeEmpty( Q );
Enqueue( S, Q );
while( !IsEmpty( Q ) )
{
v = Dequeue( Q );
T[v].known = True;
for each w adjacent to v
if( T[w].Dist = = INFINITY )
{
T[w].Dist = T[v].Dist + 1;
T[w].path = v;
Enqueue( w, Q );
}
i.
v3 is taken as source node and its path length is initialized to 0. v3 is inserted into Q.
http://www.francisxavier.ac.in
Known
dv
pv
V1
V2
V3
V4
V5
V6
V7
V3
Page 190
ii.
v3 find its adjacent node whose path length is 1. v1, v6 are adjacent nodes to v3 and
inserted in to queue.
V3 Dequeued
iii.
Known
dv
pv
V1
V3
V2
V3
V4
V5
V6
V3
V7
V1 , V6
Find the adjacent node for v1. v2 and v4 are adjacent node for v1, v2 and v4 inserted
into the queue.
V1 Dequeued
iv.
Known
dv
pv
V1
V3
V2
V1
V3
V4
V1
V5
V6
V3
V7
V6 , V2 , V4
No adjacent vertices for v6. No change in path value for all vertices.
http://www.francisxavier.ac.in
Page 191
V6 Dequeued
v.
Known
dv
pv
V1
V3
V2
V1
V3
V4
V1
V5
V6
V3
V7
V2 , V4
Find the adjacent vertices for v2. v4 and v5 are adjacent nodes to v2 and inserted into
queue.
V2 Dequeued
vi.
Known
dv
pv
V1
V3
V2
V1
V3
V4
V1
V5
V2
V6
V3
V7
V4 , V5
Find the adjacent vertices for v4. v5, v6 and v7 are adjacent vertices. Minimum path
length for v7 is 3. v7 is inserted into queue.
http://www.francisxavier.ac.in
Page 192
V4 Dequeued
vii.
Known
dv
pv
V1
V3
V2
V1
V3
V4
V1
V5
V2
V6
V3
V7
V4
V5 , V7
An adjacent vertex for v5 is v7. Already found the minimum path length from v3 to
v7 is 3. So no change in dv and pv.
V5 Dequeued
viii.
Known
dv
pv
V1
V3
V2
V1
V3
V4
V1
V5
V2
V6
V3
V7
V4
V7
An adjacent vertex for v7 is v6. Already found the minimum path length from v3 to
v6 is 1. So no change in dv and pv.
http://www.francisxavier.ac.in
Page 193
V7 Dequeued
v
Known
dv
pv
V1
V3
V2
V1
V3
V4
V1
V5
V2
V6
V3
V7
V4
Empty
Algorithm Analysis
Running time is O(|E| + |V|)
Weighted Graph
The general method to solve the single source shortest path problem is known as
Dijkstras algorithm. It applied to weighted graph.
Procedure
It uses greedy technique.
It proceeds in stages.
It selects a vertex v, which has the smallest dv among all the unknown vertices and
declares the shortest path from s to v is known.
The remainder consists of updating the value of dw.
We should set dw = dv + Cv, w, if the new value for dw would an improvement.
http://www.francisxavier.ac.in
Page 194
1. v1 is taken as source.
v
Known
dv
pv
V1
V2
V3
V4
V5
V6
V7
2. Now v1 is known vertex, marked as 1. Its adjacent vertices are v2, v4, pv and dv values
are updated
T[v2]. dist = Min (T[v2].dist, T[v1].dist + Cv1, v2) = Min ( , 0+2) = 2
T[v4]. dist = Min (T[v4].dist, T[v1].dist + Cv1, v4) = Min ( , 0+1) = 1
v
Known
dv
pv
V1
V2
V1
V3
V4
V1
V5
V6
V7
3. Select the vertex with minimum distance away v2 and v4 . v4 is marked as known
vertex. Its adjacent vertices are v3, v5, v6 and v7 .
T[v3]. dist = Min (T[v3].dist, T[v4].dist + Cv4, v3) = Min ( , 1+2) = 3
T[v5]. dist = Min (T[v5].dist, T[v4].dist + Cv4, v5) = Min ( , 1+2) = 3
T[v6]. dist = Min (T[v6].dist, T[v4].dist + Cv4, v6) = Min ( , 1+8) = 9
T[v7]. dist = Min (T[v7].dist, T[v4].dist + Cv4, v7) = Min ( , 1+4) = 5
http://www.francisxavier.ac.in
Page 195
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V4
V7
V4
4. Select the vertex which is shortest distance from source v1. v2 is smallest one. v2 is
marked as known vertex. Its adjacent vertices are v4 ad v5. The distance from v1 to v4
and v5 through v2 is more comparing with previous value of dv. No change in dv and pv
value.
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V4
V7
V4
5. Select the next smallest vertex from source. v3 and v5 are smallest one. Adjacent
vertices for v3 is v1 and v6. v1 is source there is no change in dv and pv
T[v6]. dist = Min (T[v6].dist, T[v3].dist + Cv3, v6) = Min (9 , 3+5) = 8
dv and pv values are updated. Adjacent vertices for v5 is v7. No change in dv and pv
value.
http://www.francisxavier.ac.in
Page 196
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V3
V7
V4
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V7
V7
V4
7. The last vertex v6 is declared as known. No adjacent vertices for v6. No updation in the
table.
http://www.francisxavier.ac.in
Known
dv
pv
V1
V2
V1
V3
V4
V4
V1
V5
V4
V6
V7
V7
V4
Page 197
/* Adjacency List*/
int known;
DistType Dist;
Vertex Path
};
/* Vertices are numbered from 0*/
#define NotAVertex (-1)
typedef struct TableEntry Table[NumVertex];
Table Initialization routine
void InitTable(Vertex Start, Graph G, Table T)
{
int i;
ReadGraph(G,T);
for (i=0; i<NumVertex; i++)
{
T[i].known = False;
T[i]. Dist = Infinity;
T[i]. Path = NotAVertex;
}
http://www.francisxavier.ac.in
Page 198
T[Start]. Dist = 0;
}
Routine to print the actual shortest path
void Printpath(Vertex V, Table T)
{
if(T[V]. Path != NotAVertex)
{
PrintPath(T[V]. Path, T);
printf(to);
}
printf(%v, V);
/* %v is pseudocode*/
}
Pseudocode for Dijkstras algorithm
void Dijkstra(Table T)
{
Vertex v, w;
for( ; ;)
{
v = smallest unknown distance vertex;
if( v = = NotAVertex)
break;
T[v]. kown = True;
for each w adjacent to v
if(!T[w].known)
if(T[v].Dist + Cvw < T[w]. Dist)
{
/* update w*/
Decrease(T[w]. Dist to T[v].Dist + Cvw);
T[w]. path = v;
}
}
}
http://www.francisxavier.ac.in
Page 199
For a given source vertex s, find the minimum weight paths to every vertex reachable
from s denoted
The graph cannot contain any negative weight cycles (otherwise there would
be no minimum path since we could simply continue to follow the negative
weight cycle producing a path weight of -).
The solution cannot have any positive weight cycles (since the cycle could
simply be removed giving a lower weight path).
The solution can be assumed to have no zero weight cycles (since they would
not affect the minimum value).
Therefore given these caveats, we know the shortest paths must be acyclic (with |V|
distinct vertices) |V| - 1 edges in each path.
Generic Algorithm
The single source shortest path algorithms use the same notation as BFS with
predecessor and distance d fields for each vertex. The optimal solution will have v.d =
(s,v) for all v V.
The solutions utilize the concept of edge relaxation which is a test to determine
whether going through edge (u,v) reduces the distance to v and if so update v. and v.d. This
is accomplished using the condition
http://www.francisxavier.ac.in
Page 200
Bellman-Ford Algorithm
The Bellman-Ford algorithm uses relaxation to find single source shortest paths on
directed graphs that may contain negative weight edges. The algorithm will also detect if
there are any negative weight cycles (such that there is no solution).
BELLMAN-FORD(G,w,s)
1. INITIALIZE-SINGLE-SOURCE(G,s)
2. for i = 1 to |G.V|-1
3.
4.
8. return TRUE
INITIALIZE-SINGLE-SOURCE(G,s)
1. for each vertex v G.V
2.
v.d =
3.
v.pi = NIL
4. s.d = 0
RELAX(u,v,w)
1. if v.d > u.d + w(u,v)
2.
3.
v.pi = u
http://www.francisxavier.ac.in
Page 201
Note that if the graph is a DAG (and thus is known to not have any cycles), we can
make Bellman-Ford more efficient by first topologically sorting G (O(V+E)), performing the
same initialization (O(V)), and then simply looping through each vertex u in topological
order relaxing only the edges in Adj[u] (O(E)). This method only takes O(V + E) time. This
procedure (with a few slight modifications) is useful for finding critical paths for PERT
charts.
Example:
Given the following directed graph
Using vertex 5 as the source (setting its distance to 0), we initialize all the other distances to
.
Iteration 1: Edges (u5,u2) and (u5,u4) relax updating the distances to 2 and 4
http://www.francisxavier.ac.in
Page 202
Iteration 2: Edges (u2,u1), (u4,u2) and (u4,u3) relax updating the distances to 1, 2, and 4
respectively. Note edge (u4,u2) finds a shorter path to vertex 2 by going through vertex 4
Iteration 3: Edge (u2,u1) relaxes (since a shorter path to vertex 2 was found in the previous
iteration) updating the distance to 1
http://www.francisxavier.ac.in
Page 203
Negative cycle checks: We now check the relaxation condition one additional time for each
edge. If any of the checks pass then there exists a negative weight cycle in the graph.
v3.d > u1.d + w(1,3) 4 6 + 6 = 12
v4.d > u1.d + w(1,4) 2 6 + 3 = 9
v1.d > u2.d + w(2,1) 6 3 + 3 = 6
v4.d > u3.d + w(3,4) 2 3 + 2 = 5
v2.d > u4.d + w(4,2) 3 2 + 1 = 3
v3.d > u4.d + w(4,3) 3 2 + 1 = 3
v2.d > u5.d + w(5,2) 3 0 + 4 = 4
v4.d > u5.d + w(5,4) 2 0 + 2 = 2
Note that for the edges on the shortest paths the relaxation criteria gives equalities.
Additionally, the path to any reachable vertex can be found by starting at the vertex and
following the 's back to the source. For example, starting at vertex 1, u1. = 2, u2. = 4, u4.
= 5 the shortest path to vertex 1 is {5,4,2,1}.
k is not an intermediate vertex of p (i.e. is not used in the minimum weight path)
All intermediate vertices are in {1, 2, ..., k-1}
http://www.francisxavier.ac.in
Page 204
Thus if we define a quantity d(k)ij as the minimum weight of the path from vertex i to
vertex j with intermediate vertices drawn from the set {1, 2, ..., k} the above properties give
the following recursive solution
Algorithm
FLOYD-WARSHALL(W)
1. n = W.rows
2. D(0) = W
3. (0) = (0)ij = NIL if i=j or wij =
= i if ij and wij <
4. for k = 1 to n
5.
6.
for i = 1 to n
7.
for j = 1 to n
8.
9.
10.
11.
else
(k)ij = (k-1)kj
12.
http://www.francisxavier.ac.in
Page 205
Initialization: (k = 0)
http://www.francisxavier.ac.in
Page 206
Transitive Closure
Floyd-Warshall can be used to determine whether or not a graph has transitive closure, i.e.
whether or not there are paths between all vertices.
Run Floyd-Warshall
http://www.francisxavier.ac.in
Page 207