You are on page 1of 31

DATA STRUCTURES THROUGH C++

CHAPTER - 1
1.1. C++ CLASS OVERVIEW- BASIC OOP CONCEPTS:
In this tutorial you will learn about Objects, Classes, Inheritance, Data Abstraction, Data
Encapsulation, Polymorphism, Overloading, and Reusability.
Before starting to learn C++ it is essential to have a basic knowledge of the concepts of
Object oriented programming. Some of the important object oriented features are namely:

Objects

Classes

Inheritance

Data Abstraction

Data Encapsulation

Polymorphism

Overloading

Reusability
In order to understand the basic concepts in C++, a programmer must have a good knowledge
of the basic terminology in object-oriented programming. Below is a brief outline of the
concepts of object-oriented programming languages :

Objects:
Object is the basic unit of object-oriented programming. Objects are identified by its unique
name. An object represents a particular instance of a class. There can be more than one
instance of a class. Each instance of a class can hold its own relevant data.

An Object is a collection of data members and associated member functions also known as
methods.

Classes:
Classes are data types based on which objects are created. Objects with similar properties and
methods are grouped together to form a Class. Thus a Class represents a set of individual
objects. Characteristics of an object are represented in a class as Properties. The actions that
can be performed by objects become functions of the class and are referred to as Methods.
For example consider we have a Class of Cars under which Santro Xing, Alto and WaganR
represents individual Objects. In this context each Car Object will have its own, Model, Year
of Manufacture , Color, Top Speed, Engine Power etc., which form Properties of the Car
class and the associated actions i.e., object functions like Start, Move, and Stop form the
Methods of Car Class.
No memory is allocated when a class is created. Memory is allocated only when an object is
created, i.e., when an instance of a class is created.

Inheritance:
Inheritance is the process of forming a new class from an existing class or base class. The
base class is also known as parent class or super class. The new class that is formed is
called derived class. Derived class is also known as a child class or sub class. Inheritance
helps in reducing the overall code size of the program, which is an important concept in
object-oriented programming.

Data Abstraction:
Data Abstraction increases the power of programming language by creating user defined data
types. Data Abstraction also represents the needed information in the program without
presenting the details.

Data Encapsulation:
Data Encapsulation combines data and functions into a single unit called Class. When using
Data Encapsulation, data is not accessed directly; it is only accessible through the functions
present inside the class. Data Encapsulation enables the important concept of data hiding
possible.

Polymorphism:
Polymorphism allows routines to use variables of different types at different times. An
operator or function can be given different meanings or functions. Polymorphism refers to a
single function or multi-functioning operator performing in different ways.

Overloading:
Overloading is one type of Polymorphism. It allows an object to have different meanings,
depending on its context. When an existing operator or function begins to operate on new
data type, or class, it is understood to be overloaded.

Reusability:
This term refers to the ability for multiple programmers to use the same written and debugged
existing class of data. This is a time saving device and adds code efficiency to the language.
Additionally, the programmer can incorporate new features to the existing class, further
developing the application and allowing users to achieve increased performance . This time
saving feature optimizes code, helps in gaining secured applications and facilitates easier
maintenance on the application.
The implementation of each of the above object-oriented programming features for C++ will
be highlighted in later sections.

A sample program to understand the basic structure of C++


1. //program to read employee details and to output the data
2.

3. ////////// code begins here /////////////////////////////


4. #include < iostream >
// Preprocessor directive
5. using namespace std;
6. class employee
// Class Declaration
7. {
8. private:
9.
char empname[50];
10.
int empno;
11.
12. public:
13.
void getvalue()
14.
{
15.
cout<<"INPUT Employee Name:";
16.
cin>>empname;
//
waiting input from the Keyboard for the name
17.
cout<<"INPUT Employee Number:";
18.
cin>>empno;
// waiting input from the Keyboard for the number
19.
}
20.
void displayvalue(){
21.
cout<<"Employee Name:"<< empname << endl; // displays the employee
name
22.
cout<<"Employee Number:"<< empno << endl; // displays the emloyee
number
23.
24.
}
25. };
26. void main()
27. {
28.
employee e1;
// Creation of Object
29.
e1.getvalue();
// the getvalue method is being
called
30.
e1.displayvalue(); // the displayvalue method is being called
}
31.
32. ///// code ends here //////////////

1.2. CLASS DEFINITION:


A class is used in object-oriented programming to describe one or more objects. It
serves as a template for creating, or instantiating, specific objects within aprogram. While
each object is created from a single class, one class can be used to instantiate multiple
objects.
Several programming languages support classes, including Java, C++, Objective C,
and PHP 5 and later. While the syntax of a class definition varies between programming
languages, classes serve the same purpose in each language. All classes may contain variable
definitions and methods, or subroutines that can be run by the corresponding object.
Below is an example of a basic Java class definition:
class Sample
{
public static void main(String[] args)
{
String sampleText = "Hello world!";
System.out.println(sampleText);
}
}
The above class, named Sample, includes a single method named main. Within main, the
variablesampleText is defined as "Hello world!" The main method invokes the System class
from Java's built-in core library, which contains the out.println method. This method is used
to print the sample text to the text output window.
Classes are a fundamental part of object-oriented programming. They allow variables and
methods to be isolated to specific objects instead of being accessible by all parts of the
program. This encapsulation of data protects each class from changes in other parts of the
program. By using classes, developers can create structured programs with source code that
can be easily modified.
NOTE: While classes are foundational in object-oriented programming, they serve as
blueprints, rather than the building blocks of each program. This is because classes must be
instantiated as objects in order to be used within a program. Constructors are typically used to
create objects from classes, while destructors are used to free up resources used by objects
that are no longer needed.

1.3.OBJECTS:
A class is the collection of related data and function under a single name. A C++
program can have any number of classes. When related data and functions are kept
under a class, it helps to visualize the complex problem efficiently and effectively.

A Class is a blueprint for objects:


When a class is defined, no memory is allocated. You can imagine like a datatype.
int var;

The above code specifies var is a variable of type integer; int is used for specifying
variable var is of integer type. Similarly, class are also just the specification for
objects and object bears the property of that class.

1.4. Defining the Class:


Class is defined in C++ programming using keyword class followed by
identifier(name of class). Body of class is defined inside curly brackets an terminated
by semicolon at the end in similar way as structure.
class class_name
{
// some data
// some functions
};

Example of Class:
class temp
{
private:
int data1;
float data2;
public:
void func1()
{ data1=2; }
float func2(){
data2=3.5;
retrun data;
}

};

Explanation:
As mentioned, definition of class starts with keyword class followed by name of
class(temp) in this case. The body of that class is inside the curly brackets and
terminated by semicolon at the end. There are two keywords: private and public
mentioned inside the body of class.

Keywords: private and public:


Keyword private makes data and functions private and keyword public makes data
and functions public. Private data and functions are accessible inside that class only
whereas, public data and functions are accessible both inside and outside the class.
This feature in OOP is known as data hiding. If programmer mistakenly tries to
access private data outside the class, compiler shows error which prevents the misuse
of data. Generally, data are private and functions are public.

C++ Objects:
When class is defined, only specification for the object is defined. Object has same
relationship to class as variable has with the data type. Objects can be defined in
similary way as structure is defined.

Syntax to Define Object in C++:


Class name variable name;
For the above defined class temp, objects for that class can be defined as:
temp obj1,obj2;
Here, two objects (obj1 and obj2) of temp class are defined.

1.5. Data member and Member functions:


The data within the class is known as data member. The function defined within the
class is known as member function. These two technical terms are frequently used in
explaining OOP. In the above class temp, data1 and data2 are data members
and func1() and func2() are member functions.

Accessing Data Members and Member functions:


Data members and member functions can be accessed in similar way the member of
structure is accessed using member operator(.). For the class and object defined
above, func1() for object obj2 can be called using code:
obj2.func1();
Similary, the data member can be accessed as:
object_name.data_memeber;
Note: You cannot access the data member of the above class temp because both data
members are private so it cannot be accessed outside that class.

Example to Explain Working of Object and Class in C++ Programming


/* Program to illustrate working of Objects and Class in C++ Programming
*/
#include <iostream>
using namespace std;
class temp
{
private:
int data1;
float data2;
public:
void int_data(int d){
data1=d;
cout<<"Number: "<<data1;
}
float float_data(){
cout<<"\nEnter data: ";
cin>>data2;
return data2;
}
};
int main(){
temp obj1, obj2;
obj1.int_data(12);
cout<<"You entered "<<obj2.float_data();
return 0;
}

Output:
Number: 12
Enter data: 12.43
You entered: 12.43

Explanation of Program:
In this program, two data members data1 and data2 and two member
function int_data() and float_data() are defined under temp class. Two
objects obj1 and obj2 of that class are declared. Function int_data() for the obj1 is
executed using code obj1.int_data(12);, which sets 12 to the data1 of object obj1.
Then, function float_data() for the object obj2 is executed which takes data from
user; stores it in data2 of obj2 and returns it to the calling function.
Note: In this program, data2 for object obj1 and data1 for object obj2 is not used and
contains garbage value.

CHAPTER-2
2.1. FUNCTION OVERLOADING:
You can have multiple definitions for the same function name in the same scope.
The definition of the function must differ from each other by the types and/or the number of
arguments in the argument list. You can not overload function declarations that differ only by
return type.
Following is the example where same function print() is being used to print different data
types:
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "Printing int: " << i << endl;
}
void print(double f) {
cout << "Printing float: " << f << endl;
}
void print(char* c) {
cout << "Printing character: " << c << endl;
}
};
int main(void)
{
printData pd;
// Call print to print integer
pd.print(5);
// Call print to print float
pd.print(500.263);
// Call print to print character
pd.print("Hello C++");
return 0;
}
When the above code is compiled and executed, it produces the following result:
Printing int: 5

Printing float: 500.263


Printing character: Hello C++

2.2. OPERATOR OVERLOADING:


You can redefine or overload most of the built-in operators available in C++. Thus a
programmer can use operators with user-defined types as well.
Overloaded operators are functions with special names the keyword operator followed by the
symbol for the operator being defined. Like any other function, an overloaded operator has a
return type and a parameter list.
Box operator+(const Box&);
declares the addition operator that can be used to add two Box objects and returns final Box
object. Most overloaded operators may be defined as ordinary non-member functions or as
class member functions. In case we define above function as non-member function of a class
then we would have to pass two arguments for each operand as follows:
Box operator+(const Box&, const Box&);
Following is the example to show the concept of operator over loading using a member
function. Here an object is passed as an argument whose properties will be accessed using
this object, the object which will call this operator can be accessed using this operator as
explained below:
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{

breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// Overload + operator to add two Box objects.
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
// Main function for the program
int main( )
{
Box Box1;
// Declare Box1 of type Box
Box Box2;
// Declare Box2 of type Box
Box Box3;
// Declare Box3 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 specification
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// volume of box 1
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// volume of box 2
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// Add two object as follows:
Box3 = Box1 + Box2;

// volume of box 3
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400

2.3. GENERIC PROGRAMMING:


Generic programming means that you are not writing source code that is compiled as-is but
that you write "templates" of source codes that the compiler in the process of compilation
transforms into source codes. The simplest example for generic programming are container
classes like arrays, lists or maps that contain a collection of other objects. But there's much
more to generic programming. In the context of C++ (and called meta programming) it
means to write programs that are evaluated atcompile time.
A basic example of generic programming are templates of containers: In a statically typed
language like C++ you would have to declare separate containers that hold integers, floats,
and other types or deal with pointers to void and therefore losing all type information..
Example:
template<typename T>
class MyContainer
{
// Container that deals with an arbitrary type T
};
void main()
{
// Make MyContainer take just ints.
MyContainer<int> intContainer;
}
Templates are generic because the compiler translates the template into actual code. Note that
in the case you don't instantiate your template no code will be generated for it at all. On the
other hand, if you declare a MyContainer<int>, a MyContainer<float>, and
a MyContainer<String> the compiler will create three versions of your code each of them
having a different type. There will be some optimizations involved but basically your
template code will be instantianted to three new types.

Iterators are a design pattern that were popularized in the seminal book "Design Patterns"
by Gamma et al. It's a pattern to iterate over the content of a container class. Unlike using
a for-loop an iterator is an instance of a class that points to a member of the container and
gives you an unified interface to traverse the container as well as accessing the members.
Take look at this example:
// Instanciate template QList with type int
QList<int> myList;
// put some ints into myList
// Copyconstruct iterator that points to the
// first member of the list.
QList<int>::iterator i = myList.begin();
// Iterate through the list
while (i != myList.end()) {
std::cout << *i << std::endl;
i++;
}
In this C++ example I'm instantating a template QList with type int. QList a container class
that stores a list of objects. In this example we will use it to store integers.
Then I create an iterator i to traverse through the list. myList.begin() returns an iterator
that points to the first element of the list. We can compare the iterator with another
iterator myList.end()that points after the last element of the list. If both iterators are the
same we know that we have passed the last elment. In the loop we're printing the element by
accessing it with *i and go to the next element with i++.
Note that in this example * and ++ are overloaded operators and reimplemented by the
iterator class. In a programming language without operator overloading there could be
methods like i.element() or i.next() that do the same task. It's important to see that i is
not a pointer but a whole class that just mimics the behaviour of a pointer.
What's the benefit of iterators? They provide a unified way to access the members of a
container class, completely indepented on how the container class is implemented internally.
No matter if your want to traverse a list, map or tree, the iterator classes (should) always
work the same way.

2.4. CLASS TEMPLATES:


Just as we can define function templates, we can also define class templates. The general
form of a generic class declaration is shown here:
template <class type> class class-name {
.

.
.
}
Here, type is the placeholder type name, which will be specified when a class is instantiated.
You can define more than one generic data type by using a comma-separated list.
Following is the example to define class Stack<> and implement generic methods to push
and pop the elements from the stack:
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems;

// elements

public:
void push(T const&); // push element
void pop();
// pop element
T top() const;
// return top element
bool empty() const{
// return true if empty.
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// append copy of passed element
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// remove last element
elems.pop_back();
}

template <class T>


T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// return copy of last element
return elems.back();
}
int main()
{
try {
Stack<int>
intStack; // stack of ints
Stack<string> stringStack; // stack of strings
// manipulate int stack
intStack.push(7);
cout << intStack.top() <<endl;
// manipulate string stack
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
If we compile and run above code, this would produce the following result:
7
hello
Exception: Stack<>::pop(): empty stack

2.5. INHERITANCE BASICS:


A class can be derived from more than one classes, which means it can inherit data and
functions from multiple base classes. To define a derived class, we use a class derivation list
to specify the base class(es). A class derivation list names one or more base classes and has
the form:

class derived-class: access-specifier base-class


Where access-specifier is one of public, protected, or private, and base-class is the name of
a previously defined class. If the access-specifier is not used, then it is private by default.
Consider a base class Shape and its derived class Rectangle as follows:
#include <iostream>
using namespace std;
// Base class
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;

}
When the above code is compiled and executed, it produces the following result:
Total area: 35
Access Control and Inheritance:
A derived class can access all the non-private members of its base class. Thus base-class
members that should not be accessible to the member functions of derived classes should be
declared private in the base class.
We can summarize the different access types according to who can access them in the
following way:
Access

Public

protected

Private

Same class

Yes

Yes

Yes

Derived classes

Yes

Yes

No

Outside classes

Yes

No

No

A derived class inherits all base class methods with the following exceptions:

Constructors, destructors and copy constructors of the base class.

Overloaded operators of the base class.

The friend functions of the base class.


Type of Inheritance:
When deriving a class from a base class, the base class may be inherited through public,
protected orprivate inheritance. The type of inheritance is specified by the access-specifier
as explained above.
We hardly use protected or private inheritance, but public inheritance is commonly used.
While using different type of inheritance, following rules are applied:

Public Inheritance: When deriving a class from a public base class, public members
of the base class become public members of the derived class and protected members of the
base class become protected members of the derived class. A base class's private members

are never accessible directly from a derived class, but can be accessed through calls to
the public andprotected members of the base class.

Protected
Inheritance: When
deriving
from
a protected base
class, public and protectedmembers of the base class become protected members of the
derived class.

Private
Inheritance: When
deriving
from
a private base
class, public and protected members of the base class become private members of the
derived class.
Multiple Inheritances:
A C++ class can inherit members from more than one class and here is the extended syntax:
class derived-class: access baseA, access baseB....
Where access is one of public, protected, or private and would be given for every base class
and they will be separated by comma as shown above. Let us try the following example:
#include <iostream>
using namespace std;
// Base class Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// Base class PaintCost
class PaintCost
{
public:
int getCost(int area)
{

return area * 70;


}
};
// Derived class
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Total area: 35
Total paint cost: $2450

CHAPTER 3

3.1. Algorithm Efficiency:


Efficient algorithm, so it would be nice to have metrics for comparing algorithm efficiency.
The complexity of an algorithm is a function describing the efficiency of the algorithm in
terms of the amount of data the algorithm must process. Usually there are natural units Some
algorithms are more efficient than others. We would prefer to chose an for the domain and
range of this function. There are two main complexity measures of the efficiency of an
algorithm:

Time complexity is a function describing the amount of time an algorithm takes in


terms of the amount of input to the algorithm. "Time" can mean the number of
memory accesses performed, the number of comparisons between integers, the
number of times some inner loop is executed, or some other natural unit related to the
amount of real time the algorithm will take. We try to keep this idea of time separate
from "wall clock" time, since many factors unrelated to the algorithm itself can affect
the real time (like the language used, type of computing hardware, proficiency of the
programmer, optimization in the compiler, etc.). It turns out that, if we chose the units
wisely, all of the other stuff doesn't matter and we can get an independent measure of
the efficiency of the algorithm.

Space complexity is a function describing the amount of memory (space) an algorithm


takes in terms of the amount of input to the algorithm. We often speak of "extra"
memory needed, not counting the memory needed to store the input itself. Again, we
use natural (but fixed-length) units to measure this. We can use bytes, but it's easier to
use, say, number of integers used, number of fixed-sized structures, etc. In the end, the
function we come up with will be independent of the actual number of bytes needed
to represent the unit. Space complexity is sometimes ignored because the space used
is minimal and/or obvious, but sometimes it becomes as important an issue as time.

For example, we might say "this algorithm takes n2 time," where n is the number of items in
the input. Or we might say "this algorithm takes constant extra space," because the amount of
extra memory needed doesn't vary with the number of items processed.
For both time and space, we are interested in the asymptotic complexity of the algorithm:
When n (the number of items of input) goes to infinity, what happens to the performance of
the algorithm?

An example: Selection Sort


Suppose we want to put an array of n floating point numbers into ascending numerical order.
This task is called sorting and should be somewhat familiar. One simple algorithm for sorting
is selection sort. You let an index i go from 0 to n-1, exchanging the ith element of the array
with the minimum element from i up to n. Here are the iterations of selection sort carried out
on the sequence {4 3 9 6 1 7 0}:
index
0
1
2
3
4
--------------------------------------------------------- -------|
4
3
9
6
1

comments

initial

i=0 |
0
3
9
6
i=1 |
0
1
9
6
i=2 |
0
1
3
6
i=3 |
0
1
3
4
i=4 |
0
1
3
4
i=5 |
0
1
3
4
Here is a simple implementation in C:

1
3
9
9
6
6

7
7
7
7
7
7

4
4
4
6
9
9

swap 0,4
swap 1,3
swap 3, 9
swap 6, 4
swap 9, 6
(done)

int find_min_index (float [], int, int);


void swap (float [], int, int);
/* selection sort on array v of n floats */
void selection_sort (float v[], int n) {
int
i;
/* for i from 0 to n-1, swap v[i] with the minimum
* of the i'th to the n'th array elements
*/
for (i=0; i<n-1; i++)
swap (v, i, find_min_index (v, i, n));
}
/* find the index of the minimum element of float array v from
* indices start to end
*/
int find_min_index (float v[], int start, int end) {
int
i, mini;
mini = start;
for (i=start+1; i<end; i++)
if (v[i] < v[mini]) mini = i;
return mini;
}
/* swap i'th with j'th elements of float array v */
void swap (float v[], int i, int j) {
float
t;
t = v[i];
v[i] = v[j];
v[j] = t;
}
Now we want to quantify the performance of the algorithm, i.e., the amount of time and space
taken in terms of n. We are mainly interested in how the time and space requirements change
as n grows large; sorting 10 items is trivial for almost any reasonable algorithm you can think
of, but what about 1,000, 10,000, 1,000,000 or more items?

For this example, the amount of space needed is clearly dominated by the memory consumed
by the array, so we don't have to worry about it; if we can store the array, we can sort it. That
is, it takes constant extra space.
So we are mainly interested in the amount of time the algorithm takes. One approach is to
count the number of array accesses made during the execution of the algorithm; since each
array access takes a certain (small) amount of time related to the hardware, this count is
proportional to the time the algorithm takes.
We will end up with a function in terms of n that gives us the number of array accesses for
the algorithm. We'll call this function T(n), for Time.
T(n) is the total number of accesses made from the beginning of selection_sort until the
end. selection_sort itself simply calls swap and find_min_index as i goes from 0 to n-1, so

T(n) =

[ time for swap + time for find_min_index (v, i, n)] .

(n-2 because the for loop goes from 0 up to but not including n-1). (Note: for those not
familiar with Sigma notation, that nasty looking formula above just means "the sum, as we
let i go from 0 to n-2, of the time for swap plus the time for find_min_index (v, i, n).)
The swap function makes four accesses to the array, so the function is now

T(n) =

[ 4 + time for find_min_index (v, i, n)] .

If we look at find_min_index, we see it does two array accesses for each iteration through
the for loop, and it does the for loop n - i - 1 times:

T(n) =

[ 4 + 2 (n - i - 1)] .

With some mathematical manipulation, we can break this up into:

T(n) = 4(n-1) + 2n(n-1) - 2(n-1) - 2

i.

(everything times n-1 because we go from 0 to n-2, i.e., n-1 times). Remembering that the
sum of i as i goes from 0 to n is (n(n+1))/2, then substituting in n-2 and cancelling out the 2's:
T(n) = 4(n-1) + 2n(n-1) - 2(n-1) - ((n-2)(n-1)).
and to make a long story short,
T(n) = n2 + 3n - 4 .
So this function gives us the number of array accesses selection_sort makes for a given array
size, and thus an idea of the amount of time it takes. There are other factors affecting the
performance, for instance the loop overhead, other processes running on the system, and the
fact that access time to memory is not really a constant. But this kind of analysis gives you a
good idea of the amount of time you'll spend waiting, and allows you to compare this
algorithms to other algorithms that have been analyzed in a similar way.
Another algorithm used for sorting is called merge sort. The details are somewhat more
complicated and will be covered later in the course, but for now it's sufficient to state that a
certain C implementation takes Tm(n) = 8n log n memory accesses to sort n elements. Let's
look at a table of T(n) vs. Tm(n):
n
T(n)
Tm(n)
---------2
6
11
3
14
26
4
24
44
5
36
64
6
50
86
7
66
108
8
84
133
9
104
158
10
126
184
11
150
211
12
176
238
13
204
266
14
234
295
15
266
324
16
300
354
17
336
385
18
374
416
19
414
447
20
456
479
T(n) seems to outperform Tm(n) here, so at first glance one might think selection sort is better
than merge sort. But if we extend the table:
n
--20
21

T(n)
---456
500

Tm(n)
----479
511

22
546
544
23
594
576
24
644
610
25
696
643
26
750
677
27
806
711
28
864
746
29
924
781
30
986
816
we see that merge sort starts to take a little less time than selection sort for larger values of n.
If we extend the table to large values:
n
T(n)
Tm(n)
---------100
10,296
3,684
1,000
1,002,996
55,262
10,000 100,029,996
736,827
100,000 10,000,299,996 9,210,340
1,000,000
1,000,002,999,996
110,524,084
10,000,000
100,000,029,999,996 1,289,447,652
we see that merge sort does much better than selection sort. To put this in perspective, recall
that a typical memory access is done on the order of nanoseconds, or billionths of a second.
Selection sort on ten million items takes roughly 100 trillion accesses; if each one takes ten
nanoseconds (an optimistic assumption based on 1998 hardware) it will take 1,000,000
seconds, or about 11 and a half days to complete. Merge sort, with a "mere" 1.2 billion
accesses, will be done in 12 seconds. For a billion elements, selection sort takes almost
32,000 years, while merge sort takes about 37 minutes. And, assuming a large enough RAM
size, a trillion elements will take selection sort 300 million years, while merge sort will take
32 days. Since computer hardware is not resilient to the large asteroids that hit our planet
roughly once every 100 million years causing mass extinctions, selection sort is not feasible
for this task. (Note: you will notice as you study CS that computer scientists like to put things
in astronomical and geological terms when trying to show an approach is the wrong one. Just
humor them.)

3.2. Abstract data type Lists C++:

Abstract Data Type Lists C

ADT in lists in C++ or sequence is an abstract data type that implements a finite ordered
collection of values, where the same value may occur more than once. An instance of a list is
a computer representation of the mathematical concept of a finite sequence; the (potentially)
infinite analog of a list is a stream. Lists are a basic example of containers, as they contain
other values. Each instance of a value in the list is usually called an item, entry, or element of
the list; if the same value occurs multiple times, each occurrence is considered a distinct item.
Lists are distinguished from arrays in that lists only allow sequential access, while arrays
allow random access.

ADT List Operations


1.
2.
3.
4.
5.
6.
7.

Create an empty list


Destroy a list
Determine whether a list is empty
Determine the number of items in a list
Insert an item at a given position in the list
Delete the item at a given position in the list
Retrieve th item at a given position in the list

C++ Classes
Encapsulation (used to refer to one of two related but distinct notions, and sometimes to
the combination) combines an ADTs data with its operations to form an object
1.
2.
3.
4.
5.
6.

An object is an instance of a class


A class defines a new data type
A class contains data members and
methods (member functions)
By default all members in a class are private.
Encapsulation hides implementation details.

Each class definition is placed in a header file Classname.h


The implementation of a classs methods are placed in an implementation file Classname.cpp
Constructors- Create and initialize new instances of a class. Invoked when you declare an
instance of the class. Have the same name as the class. Have no return type, not even void
Destructor- Destroys an instance of an object when the objects lifetime ends

C++ Namespaces- AS mechanism for logically grouping declarations and definitions into a
common.

3.3. ADT QUEUE:


The ADT Queue Definition The ADT Queue is a linear sequence of an arbitrary number of
items, together with access procedures. The access procedures permit addition only at the
back of the queue and deletion of items only at the front of the queue. A queue is either
empty, or it consists of a sequence of items. Manipulations or accesses to these items are only
permitted at the two ends of the queue. The queue is a list structure sometimes called a
first-in-first-out (or FIFO) list. (Bobby, Joe, Sue) (Bobby, Joe, Sue, Ellen) (Joe, Sue)
Add(Ellen) Delete( ) front back A queue is like a line of people. The first person to join a line
is the first person served and is thus the first to leave the line. Queues are appropriate for
many real-world situations. For instance, we often wait in a queue to buy a movie ticket, or to
use an automatic ticket machine. The person at the front of the queue is served, while new
people join the queue at its back. A queue is then a form of list. The difference from the
previous forms of list is in its access procedures. Whereas a stack can be seen as a list with
only one end, because all the operations are performed at the top of the stack, the queue can
be seen, in contrast, as a list with two ends, the front and the back. Queues have the main
property of allowing new items to be added only at the back of the list, and the item first
inserted in the list (i.e. the item at the front of the list) is the first to be removed. This
behaviour is commonly referred to as first-in-first-out or simply FIFO. So the first item in a
queue is the front item, whereas the last item in a queue is the backitem. The access
procedures for a queue include operations such as examining whether the queue is empty,
inspecting the item at the front of the queue but not others, placing an item at the back of the
queue, but at no other position, and removing an item from the front of the queue, but from
no other position. The next slides will describe these procedures in more detail. A typical
application of queues in computer science is: resource allocation by operating systems on
multi-user machines. For instance, if you share a printer with other users, a request to print
enters a queue to wait its turn.

3.4. STACK ADT:


Stacks
The conceptual picture of a Stack ADT is something like this:

_______

_________
\
/
\
values in
\/
/
\/
| ----- |
values out
|
|
| ----- |
|
|
| ----- |
|
|
|
|
---------Think of a stack of newspapers or trays in a cafeteria. The only item that can be taken out (or
even seen) is the most recently added (or top) item; a Stack is a Last-In-First-Out (LIFO)
abstract data type.
/

Here are the Stack ADT operations:


Operation
boolean
isEmpty()

Description
return true iff the Stack is empty

void push(E ob) add ob to the top of the Stack


E pop()

remove and return the item from the top of the Stack (error
if the Stack is empty)

E peek()

return the item that is on the top of the Stack, but do not
remove it (error if the Stack is empty)

In Java we create the StackADT interface as:


public interface StackADT<E> {
boolean isEmpty();
void push(E ob);
E pop() throws EmptyStackException;
E peek() throws EmptyStackException;
}

Queues:
The conceptual picture of a Queue ADT is something like this:
-----------------items in the queue
----> values out
-----------------^
^
|
|
this is the rear of
this is the front of
the queue
the queue

values in ---->

Think of people standing in line. A Queue is a First-In-First-Out (FIFO) abstract data type.
Items can only be added at the rear of the queue and the only item that can be removed is the
one at the front of the queue.
Here are the Queue ADT operations:
Operation

Description

boolean isEmpty() return true iff the Queue is empty


void enqueue(E
ob)

add ob to the rear of the Queue

E dequeue()

remove and return the item from the front of the Queue (error if
the Queue is empty)

In Java we create the QueueADT interface as:


public interface QueueADT<E> {
boolean isEmpty();
void enqueue(E ob);
E dequeue() throws EmptyQueueException;
}

Implementing Stacks:
The Stack ADT is very similar to the List ADT; therefore, their implementations are also
quite similar.

Array Implementation:
Below is the definition of the ArrayStack class, using an array to store the items in the stack;
note that we include a static final variable INITSIZE, to be used by
the ArrayStack constructor as the initial size of the array (the same thing was done for
the ArrayList class).
public class ArrayStack<E> implements StackADT<E> {
// *** fields ***
private static final int INITSIZE = 10; // initial array size
private E[] items; // the items in the stack
private int numItems; // the number of items in the stack
// *** constructor ***
public ArrayStack() { ... }
// *** required StackADT methods ***
// add items

public void push(E ob) { ... }


// remove items
public E pop() throws EmptyStackException { ... }
// other methods
public E peek() throws EmptyStackException { ... }
public boolean isEmpty() { ... }
}

You might also like