Ds Unit1
Ds Unit1
Jadhav
Assistant Professor,
Department of Artificial Intelligence and Data Science,
K. K. Wagh Institute of Engineering Education and Research, Nashik
A data structure is a storage that is used to store and organize data. It is a way of arranging
data on a computer so that it can be accessed and updated efficiently.
A data structure organizes, processes, retrieves, and stores data, making it essential for
nearly every program or software system. To help you master them, we've compiled a
comprehensive guide covering types, classifications, and applications of data structures.
Data is the raw material that data structures store, organize, and manage, and
upon which algorithms operate to perform computations and solve problems.
•Representation:
•Data within a data structure can be numbers, text, images, objects, or any other
type of information that a program needs to process.
•Organization:
•Data structures provide specific ways to arrange and store this data. For
example, an array stores data in contiguous memory locations, while a linked list
links data elements together in a chain.
•Access and Manipulation:
•Data structures allow for efficient access, insertion, deletion, and modification of
the stored data based on the structure's characteristics.
Data in Algorithms:
•Input:
•Algorithms take data as input, which can be in various forms depending on
the algorithm's purpose. For example, a sorting algorithm might take an
unsorted list of numbers as input.
•Processing:
•Algorithms process the input data according to their predefined steps or
instructions. This processing can involve calculations, comparisons,
transformations, or other operations.
•Output:
•Algorithms produce output based on the processed data. This output can
be a modified version of the input data, a new set of data, or a result that
indicates the solution to a problem.
Data structures are defined as special classes implemented to hold data only,
i.e. Pure models, e.g. Car, Kid, Animal, Event, Employee, Company,
Customer ...etc. Those data are generally declared or considered as instance
variables in other classes beginnings.
The methods of this class should not perform any real significant work,
otherwise the data structure class is not a data structure anymore
C++ is an object-oriented programming language.
Everything in C++ is associated with classes and objects, along with its
attributes and methods. For example: in real life, a car is an object. The car
has attributes, such as weight and color, and methods, such as drive and
brake.
Attributes and methods are basically variables and functions that belongs to
the class. These are often referred to as "class members".
Create a Class
class MyClass { // The class
public: // Access specifier
int myNum; // Attribute (int variable)
string myString; // Attribute (string variable)
};
int main() {
MyClass myObj; // Create an object of MyClass
// Print values
cout << myObj.myNum << "\n";
cout << myObj.myString;
return 0; 15
Some text
}
Define a Method Inside the Class
#include <iostream>
using namespace std;
int main() {
MyClass myObj; // Create an object of
MyClass
myObj.myMethod(); // Call the method
return 0;
} Hello World!
Access Specifiers
Access specifiers control how the members (attributes and methods) of a class can be accessed.
They help protect data and organize code so that only the right parts can be seen or changed.
#include <iostream>
using namespace std;
class MyClass {
public: // Public access specifier
int x; // Public attribute
private: // Private access specifier
int y; // Private attribute
};
int main() {
MyClass myObj;
myObj.x = 25; // Allowed (x is public)
myObj.y = 50; // Not allowed (y is private) In function 'int main()':
return 0; Line 8: error: 'int MyClass::y' is private
Line 14: error: within this context
}
Create a Function
C++ provides some pre-defined functions, such as main(), which is used to
execute code. But you can also create your own functions to perform certain
actions.
Syntax
void myFunction()
{
// code to be
executed
}
#include <iostream>
using namespace std;
void myFunction() {
cout << "WELCOME TO KKWAGH";
}
int main() {
myFunction();
return 0;
WELCOME TO KKWAGH
}
C++ Function Parameters
Syntax
void functionName(parameter1, parameter2, parameter3)
{
// code to be executed
}
#include <iostream>
#include <string>
using namespace std;
int main() {
myFunction("ABC");
myFunction("PQR");
myFunction("XYZ");
return 0;
}
ABC KKW
PQR KKW
XYZ KKW
Return Values
#include <iostream>
using namespace std; #include <iostream>
using namespace std;
int myFunction(int x) {
return 5 + x; int myFunction(int x, int y) {
} return x + y;
}
int main() {
cout << myFunction(3); int main() {
return 0; cout << myFunction(5, 3);
} return 0;
}
8
Pass By Reference
#include <iostream>
using namespace std;
void swapNums(int &x, int &y) {
int z = x;
x = y;
y = z;
}
int main() {
int firstNum = 10;
int secondNum = 20;
swapNums(firstNum, secondNum);
int main() {
int num1, num2;
cout << "Sum = " << sum << endl; Enter first number: 7
Enter second number: 13
return 0; Sum = 20
}
•Linear data structure: Data structure in which data elements are arranged sequentially or
linearly, where each element is attached to its previous and next adjacent elements, is called a
linear data structure.
Examples: array, stack, queue, linked list, etc.
• Static data structure: Static data structure has a fixed memory size. It is easier to access
the elements in a static data structure.
Example: array data structure.
• Dynamic data structure: In the dynamic data structure, the size is not fixed. It can be
randomly updated during the runtime which may be considered efficient concerning the
memory (space) complexity of the code.
Examples: stack and queue data structures.
•Non-linear data structure: Data structures where data elements are not placed sequentially or
linearly are called non-linear data structures. In a non-linear data structure, we can't traverse all
the elements in a single run.
Examples: tree and graph data structures
Different applications of an array are as follows:
Arrays efficiently manage and store database records.
•It helps in implementing sorting algorithm.
•It is also used to implement other data structures like Stacks, Queues, Heaps, Hash
tables, etc.
•An array can be used for CPU scheduling.
•Linked lists are used to implement other data structures like stacks, queues, etc.
•It is used for the representation of sparse matrices.
•It is used in the linked allocation of files.
•Linked lists are used to display image containers. Users can visit past, current, and next
images.
•They are used to perform undo operations.
Before returning the top element from the stack, we check if the stack is empty.
If the stack is empty (top == -1), we simply print “Stack is empty”.
Otherwise, we return the element stored at index = top .
isEmpty Operation in Stack:
Returns true if the stack is empty, else false.=
1. Push
2. Pop
3. Display
4. Exit
Enter your choice: 1
Enter value to push: 20
20 pushed into queue
...
Applications of Queue:
Different applications of Queue are as follows:
•Queue is used for handling website traffic.
•It helps to maintain the playlist in media players.
•It helps in serving requests on a single shared resource, like a printer, CPU task
scheduling, etc.
•Queues are used for job scheduling in the operating system.
Tree Data Structure
A tree is a non-linear and hierarchical data structure where the elements are arranged in
a tree-like structure. In a tree, the topmost node is called the root node. Each node
contains some data, and data can be of any type. It consists of a central node, structural
nodes, and sub-nodes which are connected via edges. Different tree data structures
allow quicker and easier access to the data as it is a non-linear data structure.
Applications of Tree:
•Heap is a tree data structure that is implemented using arrays and used to implement
priority queues.
•B-Tree and B+ Tree are used to implement indexing in databases.
•Syntax Tree helps in scanning, parsing, generation of code, and evaluation of arithmetic
expressions in Compiler design.
•Spanning trees are used in routers in computer networks.
•Domain Name Server also uses a tree data structure.
Binary Search Tree Data Structure
A Binary Search Tree (or BST) is a data structure used for organizing and storing data in a
sorted manner. Each node in a Binary Search Tree has at most two children, a left child and
a right child, with the left child containing values less than the parent node and the right child
containing values greater than the parent node. This hierarchical structure allows for
efficient searching, insertion, and deletion operations on the data stored in the tree.
What is an Algorithm
The word Algorithm means "A set of finite rules or instructions to be followed
in calculations or other problem-solving operations" Or "A procedure for
solving a mathematical problem in a finite number of steps that frequently
involves recursive operations".
Use of the Algorithms:
2.They help to automate processes and make them more reliable, faster, and
easier to perform.
The addition of two scalar numbers requires one extra memory location to hold
the result. Thus the space complexity of this algorithm is constant,
hence S(n) = O(1).
int freq[n];
int a[n];
for(int i = 0; i<n;
i++)
{
cin>>a[i];
freq[a[i]]++;
}
Output
51
20 4
10 3
// C++ program for the above approach // Driver Code
#include <bits/stdc++.h> int main()
using namespace std; {
// Given array
// Function to count frequencies of array int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 };
items int n = sizeof(arr) / sizeof(arr[0]);
void countFreq(int arr[], int n)
{ // Function Call
unordered_map<int, int> freq; countFreq(arr, n);
return 0;
// Traverse through array elements and }
// count frequencies
for (int i = 0; i < n; i++)
freq[arr[i]]++; Here two arrays of length N, and variable i are
// Traverse through map and print used in the algorithm so, the total space used
frequencies is N * c + N * c + 1 * c = 2N * c + c, where c is
for (auto x : freq)
cout << x.first << " " << x.second << a unit space taken. For many inputs,
endl;
}
constant c is insignificant, and it can be said
that the space complexity is O(N).
Big O notation
is a powerful tool used in computer science to describe the time complexity or
space complexity of algorithms. Big-O is a way to express the upper bound of
an algorithm’s time or space complexity.
•Helps in understanding the scalability of algorithms and predicting how they will
perform as the input size grows.
•In the best-case analysis, we calculate the lower bound on the running
time of an algorithm. We must know the case that causes a minimum
number of operations to be executed.
•For linear search, the best case occurs when x is present at the first
location. The number of operations in the best case is constant (not
dependent on n). So the order of growth of time taken in terms of input
size is constant.
3. Average Case Analysis (Rarely used)
•In average case analysis, we take all possible inputs and calculate
the computing time for all of the inputs. Sum all the calculated
values and divide the sum by the total number of inputs.
•We must know (or predict) the distribution of cases. For the linear
search problem, let us assume that all cases are uniformly
distributed (including the case of x not being present in the array).
So we sum all the cases and divide the sum by (n+1). We take
(n+1) to consider the case when the element is not present.
An array, when considered as an abstract data type (ADT), is a collection of
elements, typically of the same data type, that are accessed using an integer
index, and it is defined by its operations rather than its specific implementation.
Implementation Does not specify how operations are Specifies how to create and organize data
Details implemented or how data is structured. types to implement the structure.
Used to design and conceptualize data Used to implement data structures that realize
Usage
structures. the abstract concepts defined by ADTs.
Example List ADT, Stack ADT, Queue ADT. Structures, classes, enumerations, records.
Examples of ADTs
List ADT
The List ADT (Abstract Data Type) is a sequential collection of elements that supports a
set of operations without specifying the internal implementation. It provides an
ordered way to store, access, and modify data.
Operations:
The List ADT need to store the required data in the sequence and should
have the following operations:
•get(): Return an element from the list at any given position.
•insert(): Insert an element at any position in the list.
•remove(): Remove the first occurrence of any element from a non-empty
list.
•removeAt(): Remove the element at a specified location from a non-
empty list.
•replace(): Replace an element at any position with another element.
•size(): Return the number of elements in the list.
•isEmpty(): Return true if the list is empty; otherwise, return false.
•isFull(): Return true if the list is full, otherwise, return false. Only
applicable in fixed-size implementations (e.g., array-based lists).
Stack ADT
The Stack ADT is a linear data structure that follows the LIFO (Last In, First Out) principle.
It allows elements to be added and removed only from one end, called the top of the stack.
Operations:
In Stack ADT, the order of insertion and deletion should be according to
the FILO or LIFO Principle. Elements are inserted and removed from the
same end, called the top of the stack. It should also support the following
operations:
•push(): Insert an element at one end of the stack called the top.
•pop(): Remove and return the element at the top of the stack, if it is not
empty.
•peek(): Return the element at the top of the stack without removing it, if
the stack is not empty.
•size(): Return the number of elements in the stack.
•isEmpty(): Return true if the stack is empty; otherwise, return false.
•isFull(): Return true if the stack is full; otherwise, return false. Only
relevant for fixed-capacity stacks (e.g., array-based).
3. Queue ADT
The Queue ADT is a linear data structure that follows the FIFO (First In, First Out) principle.
It allows elements to be inserted at one end (rear) and removed from the other end (front).
Operations:
The Queue ADT follows a design similar to the Stack ADT,
but the order of insertion and deletion changes to FIFO.
Elements are inserted at one end (called the rear) and
removed from the other end (called the front). It should
support the following operations:
•enqueue(): Insert an element at the end of the queue.
•dequeue(): Remove and return the first element of the
queue, if the queue is not empty.
•peek(): Return the element of the queue without removing
it, if the queue is not empty.
•size(): Return the number of elements in the queue.
•isEmpty(): Return true if the queue is empty; otherwise,
return false.
Advantage:
The advantages are listed below:
•Encapsulation: ADTs provide a way to encapsulate data and operations into a single unit, making it easier to
manage and modify the data structure.
•Abstraction: ADTs allow users to work with data structures without having to know the implementation details,
which can simplify programming and reduce errors.
•Data Structure Independence: ADTs can be implemented using different data structures, which can make it easier
to adapt to changing needs and requirements.
•Information Hiding: ADTs can protect the integrity of data by controlling access and preventing unauthorized
modifications.
•Modularity: ADTs can be combined with other ADTs to form more complex data structures, which can increase
flexibility and modularity in programming.
Disadvantages:
The disadvantages are listed below:
•Overhead: Implementing ADTs can add overhead in terms of memory and processing, which can affect
performance.
•Complexity: ADTs can be complex to implement, especially for large and complex data structures.
•Learning Curve: Using ADTs requires knowledge of their implementation and usage, which can take time and effort
to learn.
•Limited Flexibility: Some ADTs may be limited in their functionality or may not be suitable for all types of data
structures.
•Cost: Implementing ADTs may require additional resources and investment, which can increase the cost of
development.
Array Data Structure
Array is a collection of items of the same variable type that are stored at contiguous
memory locations.
Initialization of Array
int arr[] = { 1, 2, 3, 4, 5 };
char arr[5] = { 'a', 'b', 'c', 'd', 'e' };
float arr[10] = { 1.4, 2.0, 24, 5.0, 0.0 };
Array Representation
Basic Operations in Arrays
•Traverse − print all the array elements one by one.
•Insertion − Adds an element at the given index.
•Deletion − Deletes an element at the given index.
•Search − Searches an element using the given index or by the value.
•Update − Updates an element at the given index.
•Display − Displays the contents of the array.
Array - Insertion Operation
#include <iostream>
using namespace std;
int main(){
int LA[3] = {}, i;
cout << "Array Before Insertion:" << endl;
for(i = 0; i < 3; i++)
cout << "LA[" << i <<"] = " << LA[i] << endl;
Output
//prints garbage values
Array Before Insertion:
cout << "Inserting elements.." <<endl; LA[0] = 0
cout << "Array After Insertion:" << endl; // prints array LA[1] = 0
values LA[2] = 0
for(i = 0; i < 5; i++) { Inserting elements..
LA[i] = i + 2; Array After Insertion:
cout << "LA[" << i <<"] = " << LA[i] << endl; LA[0] = 2
} LA[1] = 3
return 0; LA[2] = 4
LA[3] = 5
}
LA[4] = 6
Array - Deletion Operation
#include <iostream>
using namespace std;
int main(){
int LA[] = {1,3,5};
int i, n = 3;
cout << "The original array elements are :"<<endl;
for(i = 0; i<n; i++) {
cout << "LA[" << i << "] = " << LA[i] << endl;
}
for(i = 1; i<n; i++) { Output
LA[i] = LA[i+1]; The original array elements are :
n = n - 1; LA[0] = 1
} LA[1] = 3
cout << "The array elements after deletion :"<<endl; LA[2] = 5
for(i = 0; i<n; i++) { The array elements after deletion :
cout << "LA[" << i << "] = " << LA[i] <<endl; LA[0] = 1
} LA[1] = 5
}
Stability in Sorting Algorithms
A sorting algorithm is considered stable if it preserves the relative order of
elements with equal keys in the sorted output. This means that if two elements
have the same value, their original order in the input sequence is maintained
in the sorted sequence.
Example: If you have a list of students sorted by their names, and then you
sort them by their grades, a stable sort would ensure that students with the
same grade remain in alphabetical order by name. An unstable sort might
reorder students with the same grade, losing the original alphabetical order.
Stable Algorithms: Insertion Sort, Merge Sort, Bubble Sort, Counting Sort,
Radix Sort (when implemented with a stable counting sort).
Unstable Algorithms: Quick Sort, Heap Sort, Selection Sort.i
A sorting algorithm is said to be stable if two objects with equal keys appear in the
same order in sorted output as they appear in the input data set
Common internal sorting methods include:
•Bubble Sort:
•Compares adjacent elements and swaps them if they are in the wrong order, repeatedly passing through
the list until no more swaps are needed.
•Insertion Sort:
•Builds the final sorted array one item at a time. It iterates through the input elements and inserts each
element into its correct position in the already sorted part of the array.
•Selection Sort:
•Divides the input list into two parts: a sorted sublist and an unsorted sublist. It repeatedly finds the
minimum element from the unsorted part and puts it at the end of the sorted sublist.
•Merge Sort:
•A divide-and-conquer algorithm that recursively divides the unsorted list into sublists until each sublist
contains only one element (which is considered sorted). Then, it repeatedly merges sublists to produce
new sorted sublists until there is only one sorted list remaining.
•Quick Sort:
•Another divide-and-conquer algorithm that picks an element as a pivot and partitions the array around
the picked pivot.
•Heap Sort:
•Uses a binary heap data structure. It builds a max-heap (or min-heap) from the input data and
repeatedly extracts the maximum (or minimum) element from the heap.
Where stable sorting algorithms are useful? So we might have to sort again to obtain the list of
Consider the following dataset of Student Names students section-wise too. But in doing so, if the
and their respective class sections. sorting algorithm is not stable, we might get a result
(Dava,A) like this:
(Alia,B) (Chinmay,A)
(Ketrina,A) (Dava,A)
(Emran,B) (Ketrina,A)
(Chinmay,A) (Emran,B)
(Alia,B)
If we sort this data according to
name only, then it is highly unlikely The dataset is now sorted according to sections, but not
that the resulting dataset will be according to names.
grouped according to sections as
well. (Chinmay,A)
(Alia,B) (Dava,A)
(Chinmay,A) (Ketrina,A)
(Dava,A) (Alia,B)
(Emran,B) (Emran,B)
(Ketrina,A)
Bubble Sort Algorithm
Bubble Sort is an elementary sorting algorithm, which works by repeatedly exchanging
adjacent elements, if necessary. When no exchanges are required, the file is sorted.
Step 1 − Check if the first element in the input array is greater than
the next element in the array.
Step 2 − If it is greater, swap the two elements; otherwise move the
pointer forward in the array.
Step 3 − Repeat Step 2 until we reach the end of the array.
Step 4 − Check if the elements are sorted; if not, repeat the same
process (Step 1 to Step 3) from the last element of the array to the
first.
Step 5 − The final output achieved is the sorted array.
voidbubbleSort(int numbers[], #include <stdio.h>
intarray_size)
int main() {
{ int my_array[] = {64, 34, 25, 12, 22, 11, 90, 5};
inti, j, temp; int n = sizeof(my_array) / sizeof(my_array[0]);
for (i = (array_size - 1); i>= 0; i--)
for (int i = 0; i < n-1; i++) {
for (j = 1; j <= i; j++) for (int j = 0; j < n-i-1; j++) {
if (numbers[j-1] > numbers[j]) if (my_array[j] > my_array[j+1]) {
{ int temp = my_array[j];
my_array[j] = my_array[j+1];
temp = numbers[j-1]; my_array[j+1] = temp;
numbers[j-1] = numbers[j]; }
numbers[j] = temp; }
}
}
} printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", my_array[i]);
}
printf("\n");
return 0;
}
int main(){
#include<iostream> int n;
using namespace std; n = 5;
void bubbleSort(int *array, int size){ int arr[5] = {67, 44, 82, 17, 20}; //initialize an array
for(int i = 0; i<size; i++) { cout << "Array before Sorting: ";
int swaps = 0; //flag to detect any swap is for(int i = 0; i<n; i++)
there or not cout << arr[i] << " ";
for(int j = 0; j<size-i-1; j++) { cout << endl;
if(array[j] > array[j+1]) { //when the bubbleSort(arr, n);
current item is bigger than next cout << "Array after Sorting: ";
int temp; for(int i = 0; i<n; i++)
temp = array[j]; cout << arr[i] << " ";
array[j] = array[j+1]; cout << endl;
array[j+1] = temp; }
swaps = 1; //set swap flag
}
}
if(!swaps)
break; // No swap in this pass, so array is
sorted
} Output
} Array before Sorting: 67 44 82 17 20
Array after Sorting: 17 20 44 67 82
Insertion Sort Algorithm
Now we have a bigger picture of how this sorting technique works, so we can derive
simple steps by which we can achieve insertion sort.
Step 4 − Shift all the elements in the sorted sub-list that is greater than the value to be
sorted
1.First we find the smallest element and swap it with the first element. This
way we get the smallest element at its correct position.
3.We keep doing this until we get all elements moved to correct position.
Pseudocode
Algorithm: Selection-Sort (A)
fori← 1 to n-1 do
min j ←i;
min x ← A[i]
for j ←i + 1 to n do
if A[j] < min x then
min j ← j
min x ← A[j]
A[min j] ← A [i]
A[i] ← min x
QuickSort is a sorting algorithm based on the Divide and Conquer that picks an
element as a pivot and partitions the given array around the picked pivot by placing
the pivot in its correct position in the sorted array.
Output Array: [1 2 3 4 6 7 9 ]
void quicksort(int array[], int low, int high) {
#include <stdio.h> if (low < high) {
int pivotIndex = partition(array, low, high);
void quicksort(int array[], int low, int high); quicksort(array, low, pivotIndex - 1);
int partition(int array[], int low, int high); quicksort(array, pivotIndex + 1, high);
}
int main() { }
int myArray[] = {64, 34, 25, 12, 22, 11, 90, 5}; int partition(int array[], int low, int high) {
int n = sizeof(myArray) / int pivot = array[high];
sizeof(myArray[0]); int i = low - 1;
Divide: Divide the list or array recursively into two halves until it can no more be divided.
Conquer: Each subarray is sorted individually using the merge sort algorithm.
Merge: The sorted subarrays are merged back together in sorted order. The process continues until all elements from both subarrays have
been merged.
Conquer:
•[38] is already sorted.
•[27] is already sorted.
•[43] is already sorted.
•[10] is already sorted.
Merge:
•Merge [38] and [27] to get [27, 38] .
•Merge [43] and [10] to get [10,43] .
•Merge [27, 38] and [10,43] to get the final sorted list [10, 27, 38, 43]
Step 2
Construct a
table to Step 3
store the
values Based on
based on the least
their significant
indexing. digit of all
Since the the
inputs given numbers,
are decimal place the
numbers, numbers
the on their
indexing is respective
done based indices.
on the
possible
values of The elements sorted after this step would be 001, 042,
these digits, 482, 143, 003, 765, 236, 026, 056, 099.
i.e., 0-9
Step 4
Step 5
001, 003, 026, 236, 042, 143, 056, 765, 482, 099
The order
of input for
this step
would be
the order
of the
output in
the
previous
step. Now,
we
perform
sorting
using the
second
least
significant
digit.
The final sorted output is −
The order of the output achieved is 001, 003, 026, 236,
042, 143, 056, 765, 482, 099. 1, 3, 26, 42, 56, 99, 143, 236, 482, 765
void radixsort(int a[], int n){
int max = a[0];
include <iostream>
for (int i = 1; i < n; i++)
using namespace std;
if (a[i] > max)
void countsort(int a[], int n, int pos){
max = a[i];
int output[n + 1];
for (int pos = 1; max / pos > 0; pos *= 10)
int max = (a[0] / pos) % 10;
countsort(a, n, pos);
for (int i = 1; i < n; i++) {
}
if (((a[i] / pos) % 10) > max)
int main(){
max = a[i];
int a[] = {236, 15, 333, 27, 9, 108, 76, 498};
}
int n = sizeof(a) / sizeof(a[0]);
int count[max + 1];
cout <<"Before sorting array elements are: ";
for (int i = 0; i < max; ++i)
for (int i = 0; i < n; ++i) {
count[i] = 0;
cout <<a[i] << " ";
for (int i = 0; i < n; i++)
}
count[(a[i] / pos) % 10]++;
radixsort(a, n);
for (int i = 1; i < 10; i++)
cout <<"\nAfter sorting array elements are: ";
count[i] += count[i - 1];
for (int i = 0; i < n; ++i) {
for (int i = n - 1; i >= 0; i--) {
cout << a[i] << " ";
output[count[(a[i] / pos) % 10] - 1] = a[i];
}
count[(a[i] / pos) % 10]--;
cout << "\n";
}
}
for (int i = 0; i < n; i++) Output
a[i] = output[i]; Before sorting array elements are: 236 15 333 27 9 108 76 498
} After sorting array elements are: 9 15 27 76 108 236 333 498