Searching and Sorting
Searching and Sorting
What is Sorting?
Sorting in C++ refers to arranging elements in a specific order, typically in ascending or descending
order, within a data structure like an array or a list. Sorting is a fundamental operation in computer
science and has numerous applications, from organizing data to optimizing search algorithms. Sorting is
required to ensure that the data which we use is in a particular order so that we can easily retrieve the
required piece of information from the pile of data.
i. Internal sorting
We know that all the programs are stored inside the hard disk, and whenever we run a program in C++,
it comes to the RAM.
In internal sorting, the data to be sorted is present in the main memory or RAM, and sorting process will
also occur in the main memory itself. Examples of internal sorting are bubble sort, insertion
sort, selection sort.
While in the case of external sorting, the data is not always present in the RAM because the data is
large. So data is filled in the RAM or main memory in small portions. An example of external sorting is
the Merge sort.
i. Bubble sort: this is the technique in which we compare every element with its adjacent
element and swap the elements if they are not in order. That is, we begin by comparing the
first two elements of the array and checking if the first element is greater than the second
element; if it is, we will swap those elements and move forward to the next element. This
way, at the end of every pass, the heaviest element gets bubbled up at the end of the list.
If the first element is not greater than the second, then we don’t need to swap it. And this process will
keep on repeating till the end of the array.
This is the technique in which we find the smallest in the list and put it in its proper place. At each pass,
the next smallest element is selected and placed in its proper position. The complete array is divided
into two halves, the sorted subarray on the left and the unsorted subarray on the right. Once the first
element is sorted, the search for the second minimum element begins from the rest of the array and is
positioned at second place.
All the elements are positioned on the sorted side of the subarray one after the other, and the complete
array becomes a sorted array.
This is the technique in which we start from the second element of the list. We compare the second
element with its previous (1st) element and place it in its proper place. In the next pass, for each
element, we compare it to all its previous elements and insert that element at its proper place. This
technique performs well when the elements in the list is smaller
Then, we will compare the third element with all the elements that are before it. Similarly, it goes for
the fourth element and so on. Once all the comparisons are made, the elements become sorted.
using pivot element, this technique uses the ‘divide and conquer’ strategy in which the problem is
divided into several subproblems and after solving these subproblems individual are merged together
for a complete sorted list.
v. Merge sort:
This technique also makes use of ‘divide and conquer’ strategy. Here, we divide the list first into equal
halves. Then we perform merge sort technique on these lists independently so that both lists are sorted.
Finally, we merge both list to get a complete sorted list.
vi. Shell sort:
Shell sort is a generalization of insertion sort that allows the exchange of items that are far apart,
making it more efficient for larger lists. It works by initially sorting elements far apart from each other,
then progressively reducing the gap between the elements to be compared. This helps move elements
into their correct positions more quickly than a simple insertion sort.
This is also known as a comparison-based sorting algorithm. It arranges the elements in the format of a
sorted binary tree or a full binary tree.
SEARCHING IN C++
Searching refers to a technique that helps locate a data element out of the given string or array. It is
procedure used to find a specific item or element within a collection of data. These algorithms are
widely used in computer science and are crucial for tasks like searching for a particular record in a
database, finding an element in a sorted list, or locating a file on a computer.
b. Binary search
Linear searching: this is a sequential technique in which we begin a search at one end of the list and
continue checking each element until the required element appears.
Example:
12 32 8 23 54 10 29
0 1 2 3 4 5 6
If our search key = 23, then we start the search from the 0th element, the value will be compared to each
element. Once the key element matches with the element in the array, then, that particular location will
be returned. In this case however, location 4 is returned.
Linear search can be performed on any linear data structure having sorted or unsorted elements. But it
takes a longer time if there are too many elements, and if the key element is towards the end as each
element is compared with the key value.
3. It begins at one end of the list and continues checking each element until the required
element appears
Binary search
This is a technique that uses “divide and conquer” rule to search for a key element. It involve the
iterative division of the search range in half and the key element is searched in both the halves of the list
until the key is found.
Example:
5 8 10 13 21 23 25 43 54 75
0 1 2 3 4 5 6 7 8 9
Search key = 21
5 8 10 13 21 23 25 43 54 75
0 1 2 3 4 5 6 7 8 9
We then compare the key element with the [mid] element. We find that the element value of the mid =
21. Hence, we find the key.
If the key = 25
We compare the mid element with25, we see that 21<25 , we directly search for the key in the upper
half of the array.
23 25 43 54 75
If the key = 10
We find that 10 is in the lower range, then we search for the key element in the lower range: low +
(high-mid)/2
0 + (9 - 4)/2
Searching technique helps us to search for information stored on a computer so that user can proceed
with the other task of information processing. Binary search is much faster and efficient, hence it is used
extensively.
A string is an ordered sequence of characters, enclosed in double quotation marks. It is part of the
Standard Library.
You need to include the <string> library to use the string data type. Alternatively, you can use a library
that includes the string library.
The <string> library is included in the <iostream> library, so you don't need to include <string>
separately, if you already use <iostream>.
A string is an object that represents a group or a sequence of characters. The string is represented as
a one-dimensional array of characters and ends with a \0 (null character). Strings in C++ can be defined
either using the std::string class or the C-style character arrays.
Strcmp (s1, s2) Returns 0 if s1 and s2 are the same; less than 0 if
s1<s2; greater than 0 if s1>s2
Index 0 1 2 3 4 5
H E L L O \0
ARRAY IN C++
An array can be defined as a group or collection of similar kinds of elements or data items that are
stored together in contiguous memory spaces. Arrays in C++ are a collection of similar data type
elements. An array is a data structure that allows you to store a collection of elements of the same type
in contiguous memory locations. It provides a way to handle multiple values efficiently using a single
variable name. Arrays are used to store multiple values in a single variable, instead of declaring separate
variables for each value. Each element in the array can be accessed using an index.
To declare an array, define the variable type, specify the name of the array followed by square
brackets and specify the number of elements it should store.
Syntax:
int club[10];
Here int is the data type of the array elements, the club is the name of the array, and ten is the size of
the array, which means ten blocks of memory space are created for the array club.
For examples:
string cars[4]; // we can declared a variable that holds an array of four strings
You can initialize an array while declaring it, by assigning elements to the array during the declaration
using curly braces around the elements separated by commas.
If you initialize an array with fewer values than its size, the remaining elements are automatically set to
zero:
After the declaration of the array, you can assign elements to the array at each index.
Examples:
i. int numbers[3];
array[0]={1};
array[1]={2};
array[2]={3};
array[0]={"Volvo"};
array[0]={"BMW"};
array[0]={"Ford"};
array[0]={"Mazda"};
In C++, each element in an array is associated with a number. The number is known as an array index.
We can access elements of an array by using those indices. You access an array element by referring to
the index number inside square brackets [], where indexing starts from 0. Array indexes start with 0: [0]
is the first element. [1] is the second element, etc.
One-dimensional array
Two-dimensional array
Multidimensional array
One-Dimensional Array:
In this type of array, it stores elements in a single dimension. And, In this array, a single specification is
required to describe elements of the array.
Fig: One-dimensional array
The diagram above shows that it arranged all the elements row-wise in a single dimension, one after the
other.
Two-Dimensional Array:
In this type of array, two indexes describe each element, the first index represents a row, and the
second index represents a column.
As you can see, the elements are arranged row-wise and column-wise; in a two-dimensional array, there
are i number of rows and j number of columns. The above figure is a representation of a 3 x 3 matrix,
which means there are three rows and three columns in the array.
Multidimensional Array:
The simplest example of a multidimensional array is a 2-d array; a two-dimensional array also falls under
the category of a multidimensional array. This array can have any number of dimensions.
Syntax:
Here size1 size2 up to so on size n describes the number of dimensions; in the case of a 2-d array, there
are only two dimensions, a multidimensional array can have any number of dimensions.
Example:
int array[5][10][4];
Efficient memory usage: Arrays in C++ use contiguous memory locations to store the elements,
thus making it efficient to allocate and access data.
Easy access to elements: Elements in an array can be accessed using their index, making it easy
to retrieve specific data from the array.
Better performance: Accessing elements in an array using an index is faster than using other
data structures like linked lists or trees. This is because the index provides direct access to the
memory location where the element is stored.
Flexibility: Arrays in C+
+ can be used to store different types of data, including integers, characters, and strings.
Easy to implement algorithms: Many algorithms in computer science use arrays, making it easy
to implement these algorithms in C++.
Compatible with other data structures: Arrays can be used in conjunction with other data
structures in C++, such as stacks and queues, to implement complex data structures and
algorithms.
Easy to pass to functions: Arrays can be easily passed as arguments to functions in C++, making
it easy to manipulate large amounts of data efficiently.
Fixed-size: Arrays in C++ have a fixed size determined at the time of declaration. There is no
dynamic increase in the size of an array.
Memory allocation: Arrays in C++ are allocated in contiguous memory blocks. If the array is very
large, there may not be enough contiguous memory available for allocation.
No bounds checking: C++ does not perform any bounds checking on arrays, so it is possible to
access memory outside of the bounds of the array. This can lead to segmentation faults or other
memory-related errors.
Limited flexibility: Arrays in C++ have limited flexibility due to fixed size and dimensions. This
makes it difficult to implement certain algorithms and data structures that require dynamic
resizing and manipulation.
Inefficient for insertion and deletion: Inserting or deleting elements from an array in C++ can be
inefficient, as it requires shifting all the elements after the insertion or deletion point to make room or
fill in the gap. This can be time-consuming, especially for large arrays.
Pointers are variables that store the memory addresses of other variables or any data structures in C++.
A pointer is the symbolic representation of addresses. It stores the address of variables or memory
location. Pointers enable programmers to create and manipulate dynamic data structures. Variables can
be of type int, array, char, function, or any other pointer.
data_type *pointer_name ;
How a Pointer works:
datatype *var_name;
int *ptr; // ptr can point to an address which holds int data
Assigning the address of a variable to a pointer using the unary operator (&) which returns the
address of that variable.
Accessing the value stored in the address using unary operator (*) which returns the value of the
variable located at the address specified by its operand.
The reason we associate data type with a pointer is that it knows how many bytes the data is stored in.
When we increment a pointer, we increase the pointer by the size of the data type to which it points.
incremented ( ++ )
decremented ( — )
Pointers reduce the code and improve performance. They are used to retrieve strings, trees,
arrays, structures, and functions.
In addition to this, pointers allow us to access a memory location in the computer’s memory
Pointers to an array is the pointer that points to the array. It is the pointer to the first element of the
pointer instead of the whole array but we can access the whole array using pointer arithmetic.
As you can see, this declaration is similar to the pointer to a variable. It is again because this pointer
points to the first element of the array.
Note: The array name is also a pointer to its first element so we can also declare a pointer to the array
as: type *ptr_name = arr_name;
The following table list the points that distinguish the arrays and pointers from each other:
S.
No. Array Pointer
2. Collection of elements of similar data type. Store the address of another variable.
The array can be initialized at the time of Pointers can also be initialized at
3.
definition. definition.
The size of the array decides the number of The pointer can store the address of
4.
elements it can store. only one variable.
S.
No. Array Pointer
Arrays are static in nature i.e. they cannot be Pointers are dynamic in nature i.e.
7.
resized according to the user requirements. memory allocated can be resized later.
Polymorphism refers to the ability of a C++ function or object to perform in different ways, depending
on how the function or object is used. Polymorphism is the ability of a message to be displayed in more
than one form. Polymorphism refers to the ability of different classes to be treated as instances of the
same class through a common interface. It allows objects of different derived classes to be manipulated
through a pointer or reference of the base class type, enabling methods to be used interchangeably
while maintaining specific behaviors for each derived class. This is typically achieved through virtual
functions and dynamic binding, making the code more flexible and easier to maintain. Polymorphism in
C++ allows us to reuse code by creating one function that’s usable for multiple uses. We can also make
operators polymorphic and use them to add not only numbers but also combine strings. This saves time
and allows for a more streamlined program.
Types of Polymorphism
i. Compile-time Polymorphism
Compile-time Polymorphism
Compile time polymorphism takes place when a program is being compiled. C++ polymorphism occurs in
this phase when either a function or an operator is overloaded. This type of polymorphism is also known
as static or early binding. In this, the compiler at the compilation stage knows which functions to
execute during the program execution. It matches the overloaded functions or operators with the
number and type of parameters at the compile time.
Compile time polymorphism comes in two forms in C++: function overloading and operator overloading.
a. Function Overloading:
Function overloading allows us to use the same function multiple times, with the action or output of
that function dependent on its assigned data type.
b. Operator Overloading:
C++ allows us to add additional tasks to operators by using the operator keyword followed by the
operator itself. Operators cover a wide range of uses in C++, from arithmetic to comparisons and logical
statements. We can give a special meaning to an operator for a particular class without changing its
original meaning for the rest of the program.
Runtime Polymorphism
Runtime polymorphism in C++ uses function overriding and takes place while a program is in the run
state. In this, the compiler at the compilation stage does not know the functions to execute during the
program execution. The function call is not resolved during compilation, but it is resolved in the run time
based on the object type. It means the function is invoked by seeing which object is calling it; the parent
class object or the derived class object. This is also known as dynamic binding or late binding. In this
form of polymorphism, the program has to discover which definition of a function it needs to use based
on the information in the main() function. Since this happens during runtime, the process is slower
compared to compile time polymorphism.
Runtime Polymorphism in C++ is achieved in two ways:
a. Function Overriding:
In this, the method with the same name, type, and number of parameters is defined in both the base
and derived classes. When the object of the derived class calls this method, the method in the derived
class gets executed instead of the base class. Therefore, we can say that the method of the derived class
has overridden the method of the base class
b. Virtual Functions:
When we use the pointer of the base class to override the function of the base class in the derived class,
it is not done. Therefore virtual functions in C++ come into play. If you declare a method in the base
class as virtual and use a pointer of the base class to call that method, it calls the method of the derived
class and not the base class. Thus, the virtual function in the base class helps the derived class method
to override that function.
Function calls are statically binded. Function calls are dynamically binded.
Inheritance in C++
Inheritance is a mechanism that allows one class to inherit properties and methods from another class.
It establishes a hierarchical relationship between classes, where a subclass inherits the characteristics
(data and methods) of its superclass.
i. Base Class (Super Class): The class whose properties and methods are inherited.
ii. Derived Class (Sub Class): The class that inherits from the base class and may have
additional features or override existing ones.
1. Single Inheritance
Single Level Inheritance involves deriving only one class from the base class. It establishes a
straightforward parent-child relationship between two classes.
2. Multi-Level Inheritance
Multi-Level Inheritance signifies a chain of inheritance where one derived class becomes the base class
for another derived class. It allows for multiple levels of derived classes.
3. Hierarchical Inheritance
Hierarchical Inheritance occurs when multiple classes are derived from a single base class. It forms a
hierarchical structure, branching out from a common base.
4. Multiple Inheritance
Multiple Inheritance occurs when a single class inherits properties and methods from multiple base
classes. It provides a way to combine functionalities from different sources.
5. Hybrid Inheritance
In C++, inheritance syntax involves creating a derived class that inherits attributes and behaviors from a
base class. Here's a basic representation:
derived_class_name: name of the new class, which will inherit the base class
access-specifier: Specifies the access mode which can be either of private, public or protected. If
neither is specified, private is taken as default.
Example:
// Base Class
class Base {
public:
void display() {
};
public:
void show() {
};
Mode of inheritance controls the access level of the inherited members of the base class in the derived
class. In C++, there are 3 modes of inheritance:
a. Public Mode
b. Protected Mode
3. Private Mode
If we derive a subclass from a public base class. Then the public member of the base class will become
public in the derived class and protected members of the base class will become protected in the
derived class.
Example:
If we derive a subclass from a Protected base class. Then both public members and protected members
of the base class will become protected in the derived class.
Example:
If we derive a subclass from a Private base class. Then both public members and protected members of
the base class will become private in the derived class. They can only be accessed by the member
functions of the derived class.
Private mode is the default mode that is applied when we don’t specify any mode.
Example:
Note: The private members in the base class cannot be directly accessed in the derived class, while
protected and public members can be directly accessed. To access or update the private members of the
base class in derived class, we have to use the corresponding getter and setter functions of the base
class or declare the derived class as friend class.
The below table summarizes the above three modes and shows the access specifier of the members of
the base class in the subclass when derived in public, protected and private modes:
The following table illustrates the control of the derived classes over the members of the base class in
different visibility modes:
Table 13.1: Control of the derived classes over members of the base class:
Benefits of Inheritance:
ii. Hierarchical Classification: Creates a logical structure and hierarchy in your code.
Ease of Maintenance: Changes in the base class can automatically propagate to derived classes