0% found this document useful (0 votes)
58 views

R23_DS_Unit_1-1

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
58 views

R23_DS_Unit_1-1

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

Unit I [R23]

Introduction to Linear Data Structures: Definition and importance of linear data structures,
Abstract data types (ADTs) and their implementation, Overview of time and space complexity
analysis for linear data structures. Searching Techniques: Linear & Binary Search, Sorting
Techniques: Bubble sort, Selection sort, Insertion Sort, Quick Sort, Merge Sort

INTRODUCTION:
• A data structure is a particular way of storing and organizing data in a computer so that it can be
used efficiently. So a data structure is a specialized format for organizing, processing, retrieving
and storing data.
• Some common examples of data structures are arrays, linked lists, queues, stacks, binary trees,
and hash tables.
• It is considered as not only the storing of data elements but also the maintaining of the logical
relationship existing between individual data elements.
• The way of organizing of the data & performing the operations is called as data structure.
Data structure = organized data + operations
Algorithm + Data Structure=Program
Definition of Data Structure:
A data structure is a way of organizing and storing data in a computer so that it can be accessed
and manipulated efficiently. It defines the relationship between the data and the operations that
can be performed on that data. Data structures are essential for solving various computational
problems efficiently and effectively.

Classification of Data Structures:

Data structures are generally categorized into two classes: primitive and non-primitive data structures

Primitive Data Structures:


 Primitive data structures are the fundamental data types which are supported by a
programminglanguage.Somebasicdatatypesareinteger,real,character,andboolean.The terms
‘data type’, ‘basic data type’, and ‘primitive data type’ are often used interchangeably.
 Primitive data structures are directly operated upon by the machine instructions.
 The primitive data structures have different representation on different computers.
Non-primitive Data Structures:
 Non-primitive data structures are those data structures which are created using primitive data
structures. Examples of such data structures include linked lists, stacks, trees, and graphs.

Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 1


 The Non-primitive data structures are highly developed complex data structures.
 Basically these are developed from primitive data structures.
 Non-primitive data structures can further be classified into two categories: linear and non-
linear data structures.
Linear and Non-linear Structures:
 If the elements of a data structure are stored in a linear or sequential order, then it is a linear
data structure. Examples include arrays, linked lists, stacks, and queues.
 Linear data structures can be represented in memory in two different ways. One way is to
have to a linear relationship between elements by means of sequential memory locations. The
other way is to have a linear relationship between elements by means of links.
 The adjacency relationship is maintained between the elements of linear data structure.
 If the elements of a data structure are not stored in a sequential order, then it is a non-linear
data structure.
 The relationship of adjacency is not maintained between elements of a non-linear data
structure. Examples include trees and graphs.
Importance of Linear Data Structures:
Linear data structures are those in which data elements are arranged in a sequential manner,
which means that each element is connected to its predecessor and successor. Some common
examples of linear data structures include arrays, linked lists, stacks, and queues.

Therefore, linear data structures can organize elements in a sequential manner, one after the
other, just like items in a line. The importance of linear data structures lies in their simplicity
and versatility. The following are some key reasons why linear data structures are significant:

1. Simplicity: They are easier to understand and implement compared to more complex, non-
linear structures. This makes them ideal for beginners and straightforward applications.
2. Efficient access: Linear data structures allow for efficient access to elements based on their
position. Accessing specific elements is often very efficient, especially for the first and last
elements.
3. Fast insertions and deletions: Adding or removing elements at the beginning or end of the
structure can be quite efficient, just like adding a new element to the end of the data structure or
removing the first one of the data structure.
4. Memory locality: Linear data structures typically require contiguous or sequential memory
allocation, which can be more memory-efficient compared to other data structures like trees or
graphs, especially when dealing with large datasets
5. Wide applicability: Linear data structures are the building blocks for numerous applications like
lists, queues, stacks, and binary trees. They play a vital role in algorithms like sorting and
searching, making them as more important.
6. Application in Problem Solving: Many real-world problems can be efficiently solved using
linear data structures. For instance, a stack can be used to implement functions and manage
function calls in programming languages, while a queue can be employed in tasks like task
scheduling or managing resources.
ADT:

An Abstract Data Type (ADT) is a mathematical model for a certain class of data structures that
specifies a set of operations and the semantics (meaning) of those operations, without specifying the
implementation details. In other words, an ADT defines what operations can be performed on the
data, but not how those operations are implemented. For example, a stack is an abstract data type that
specifies a linear data structure with LIFO (last in, first out) behavior.
Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 2
The implementation of an Abstract Data Type (ADT) involves translating its abstract interface,
which defines the operations and their semantics, into concrete code that represents the data structure
and implements those operations. The following are the steps required to implement an ADT,

1. Define the Interface: This step defines the interface of the ADT. This includes specifying the
operations that can be performed on the data structure and their signatures (method names,
parameters, and return types).

2. Choose a Data Structure: Select an appropriate data structure to represent the ADT. The choice
of data structure depends on factors such as the efficiency of operations, memory usage, and the
requirements of the application. Common data structures for implementing ADTs include arrays,
linked lists, and hash tables.

3. Implement Operations: Implement each operation defined by the ADT interface using the chosen
data structure.

Space and Time Complexity:

Efficiency is one of the important characteristics of algorithms. This efficiency is also referred as
complexity of an algorithm. This complexity can be determined as the amount of resources such as
time and memory needed to execute an algorithm. So the efficiency or complexity of an algorithm is
stated in terms of time and space complexity. Hence, the complexity of an algorithm is divided into
two types:

1. Space Complexity
2. Time Complexity

Space Complexity:

 Space Complexity can be defined as amount of memory (or) space required by an algorithm to run.
 To compute the space complexity we use 2 factors i. Constant ii. Instance characteristics.
 The space requirement denoted by S(p) can be given as S(p)=C+Sp
Where C- Constant, it denotes the space taken for input and output. Sp–Amount of space taken
by an instruction, variable and identifiers.

Time complexity:

 The time complexity of an algorithm is the amount of computing time required by an algorithm
to run its completion.
 There are 2 types of computing time 1.Compile time 2.Runtime
 The time complexity generally computed at runtime (or) execution time.
 The time complexity of an algorithm is basically the running time of a program as a function of
the input size.
Worst-case running time:
This is the maximum time required to solve the problem of size ‘n’. so this worst-case running time
of an algorithm is an upper bound on the running time for any input. Therefore, the running time of
algorithm will never go beyond this time limit. Normally this is represented by Big Oh (O) notation.
Average-case running time:
The average-case running time of an algorithm is an estimate of the running time for an ‘average’
input. It specifies the expected behavior of the algorithm when the input is randomly drawn from a
given distribution. Sometimes it is difficult to find, because we have to check all possible inputs.
Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 3
Normally it is represented by Big Theta (θ) notation.

Best-case running time:


It will be the minimum time that an algorithm requires to solve a problem of size ‘n’. This best-case
running time of an algorithm is an lower bound on the running time for any input. Normally it is
represented by Big Omega (Ω) notation.

Big Oh(O):
If f(n) describes the running time of an algorithm, f(n) is O(g(n)) if there
exist a positive constant c and n 0 such that, 0 ≤ f(n) ≤ cg(n) for all n ≥ n 0. It
gives the worst-case complexity of an algorithm.
Example : If f(n)= 3n+2 then prove that f(n)=O(n)
Solution: By the definition of Big Oh,
f(n)= O(g(n)) where f(n) ⩽ c. g(n) for all n ≥ n0
let g(n)=n and c=4, then
consider 0 ⩽ 3n+2 ⩽ 4n
When n=1, 0⩽5⩽4
When n=2, 0⩽8⩽8
When n=3, 0 ⩽ 11⩽ 12
Therefore, 3n+2 ⩽ 4n can satisfied for all n where n0 >= 2, So by the definition of Big Oh, we can say that
f(n)= O(n)
Big Omega(Ω):
This notation is defined as, a function f is said to be Ω(g(n)), if there is a constant c > 0 and a natural
number n0 such that c*g(n) ≤ f(n) for all n ≥ n 0. However, it provides the best case complexity of an
algorithm.
Example : If f(n)= 3n+2 then prove that f(n)= Ω(n)
Solution: By the definition of Big Ω, f(n) = Ω(g(n)
where c*g(n) ≤ f(n) for all n ≥ n0
Let c=3 and g(n)=n, then
Consider 3n+2 ≥ 3n
When n= 1, 5≥ 3
When n = 2, 8 ≥ 6
Therefore 3n + 2 ≥ 3n can be satisfied for n where n ≥ 1,
hence by the definition of Big omega, we can say that f(n)= Ω(n)
Big Theta(θ):
The function f is said to be θ(g(n)), if there are constants c1, c2 > 0 and a natural number n 0 such that
c1* g(n) ≤ f(n) ≤ c2 * g(n) for all n ≥ n 0. It is used for analyzing the average-case complexity of an
algorithm.
Example: If f(n)= 3n+2 then prove that f(n)= θ(n)
Solution: By the definition of Big θ, f(n) = θ(n)
where c1* g(n) ≤ f(n) ≤ c2 * g(n) for all n ≥ n 0
Let c1 = 3, c2 = 4 and g(n) = n, then
Consider 3n ≤ 3n+2 ≤ 4n
When n = 1, 3 ≤ 5 ≤ 4
When n = 2, 6 ≤ 8 ≤ 8
When n = 3, 9≤ 11≤ 12
Therefore 3n ≤ 3n+2 ≤ 4n can be satisfied for all n, where n ≥ 2.
Hence we say that f(n)= θ(n).

The time and space complexities of linear data structures, such as arrays, linked lists, stacks, and
queues, vary depending on the specific operations being performed and the underlying
implementation details. Here's a general overview:

Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 4


1. Arrays:

- Time Complexity:
- Accessing an element by index (random access): O(1)
- Searching an element: O(n)
- Insertion/Deletion at the beginning: O(1)
- Insertion/Deletion at location : O(n)
- Insertion/Deletion at the end (if resizing is not required): O(1)
- Space Complexity: O(n)

2. Linked Lists:

- Time Complexity:
- Accessing an element by index (sequential access): O(n)
- Searching an element: O(n)
- Insertion/Deletion at the beginning: O(1)
- Insertion/Deletion at the end (with tail pointer): O(1)
- Insertion/Deletion at a given position: O(n)
- Space Complexity: O(n)
3. Stacks (implemented using arrays or linked lists):

- Time Complexity:
- Push (Insertion) operation: O(1)
- Pop (Deletion) operation: O(1)
- Peek (Accessing the top element) operation: O(1)
- Space Complexity: O(n)
4. Queues (implemented using arrays or linked lists):

- Time Complexity:
- Enqueue (Insertion) operation: O(1)
- Dequeue (Deletion) operation: O(1)
- Peek (Accessing the front element) operation: O(1)
- Space Complexity: O(n)
Searching:
 Searching means that to find whether a particular element in the given list of elements.
 The searching element is referred as key element.
 If the element is found then search is said to be successful otherwise it is unsuccessful.
 We have different Searching techniques and most frequently used search technique are linear
search, binary search.

Linear Search:
 This is simplest search technique and also called as sequential search.
 In this search we can traverse the group of elements sequentially or linearly to search an element.
 Hence, in this search we access every element of the group one by one sequentially and check
whether it is required element or not.
 If the element found then this linear search becomes successful and it gives the location of the
element found, otherwise it becomes unsuccessful.
 This linear search is mostly used to search an element from unordered list i.e the items are not
sorted.

The Linear search is implemented by using the following C function:


int linearSearch(int arr[], int size, int key)
{

Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 5


for (int i = 0; i < size; i++)
if (arr[i] == key)
return i;
return -1;
}

Time Complexity of Linear Search:


Best Case: This case occurs when the search element is found at first comparison. In this case the
search ends with single successful comparison. Thus, O(1) is the best case time complexity for linear
search.
Worst Case: This case occurs when the search element may be at last position or not at all. So in
this case ‘n’ comparisons are required. Therefore O(n) is the worst case time complexity for linear
search.
Average Case: When the search element is in the middle of the array then average case occurs. So
the average case also requires the time complexity of O(n).

Binary Search:
 Binary search is another search technique which works efficiently on the sorted lists.
 This technique uses divide and conquer principle to search an element. According to this
principle, the list is divided into two halves and key element is compared with middly element.
 If match found, then this binary search becomes successful and gives the location of the element
found. Otherwise it becomes unsuccessful.
 However, inorder to search an element by using binary search technique, we must ensure that the
list is sorted.
The Binary search is implemented by using the following C function:
void BinarySearch(int arr[], int key, int low, int high)
{
int mid;
mid = (low + high)/2;
if(low>high)
printf("Element is not found” );
else
{
if(arr[mid]==num){
printf("Element Is At The Index: %d ",mid);
exit(0); }
else if(arr[mid] > num)
BinarySearch(arr, num, first, mid-1);
else
BinarySearch(arr, num, mid+1, last);
}
}
Time Complexity for Binary Search:
The time complexity for binary search technique is generally based on the height of binary tree.
If the number of elements are ‘n’ which is greater than 2 then the time computation is maximum
and it is a log arithmetic function. i.e log(n).So in successful searches the computing time for
binary search is
Best Case: It takes O(1) as time complexity.
Worst Case: It takes O(log n) as time complexity. and
Average Case: It takes O(log n) as time complexity.
However in unsuccessful search the best, average and worst case time complexity is O(logn)
time.
Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 6
Sorting:
 Sorting is a technique to arrange the list of elements either in a specific order.
 The specific order may be an ascending order (increasing order) or descending order
(decreasing order)
We have number of sorting techniques and most frequently used techniques are given below
 Bubble sort
 Insertion sort
 Selection sort
 Quick sort
 Merge sort

Bubble Sort:

 Bubble Sort is also called as Exchange Sort.


 The mechanism used in this Bubble Sort is that, each element is compared with its adjacent
element and if they are not in order then only they are interchanged, otherwise left them as it
easily.
 The same procedure is repeated until no more elements are left for comparison.
 After completion of this 1stpass the largest element is moved to the last position.
 Repeat the same process for further passes.
 For a given list of n elements, the bubble sort requires n – 1 passes to sort the elements.
The Bubble sort is implemented by using the following C function:
void bubbleSort(int arr[], int n)
{
int i, j, temp;
for (i = 0; i < n-1; i++)
for (j = 0; j < n-i-1; j++)
if (arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
Time Complexity of Bubble Sort:
Best Case: This case occurs when an array is already in sorted order. So in this case it performs only
O(n) comparisons and even single swap is also not required. As a result, the time complexity for this
scenario is O(n)
Worst Case: This case occurs when an array is in reverse order. So an outer loop performs ‘n’
iterations. For the first iteration of outer loop, n-1 comparisons required for inner loop to swap the
elements. And for the second iteration, it required n-2 comparisons and so on. Therefore the time
complexity is given as
T(n) = (n - 1) + (n – 2)+ (n – 3)+ ................. + 3 + 2 +1
T(n) = n(n – 1)/2
T(n)= O(n2)
This is same as average case also.

Insertion sort:
 This is another sorting technique and in this sorting technique the list can be divided into two
Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 7
parts, one is sorted list and other is unsorted list.
 Initially the sorted list is empty and unsorted list contains all the elements to be sorted.
 Finally the sorted list contains all the elements in sorted order and unsorted list becomes empty.
 The mechanism used for this insertion sort is given as follows:
i. For a given list of n elements, this technique requires n – 1 passes to sort the elements.
ii. In each pass the first element of unsorted list is moved to sorted list by inserting it in its
proper place.
iii. In each pass the first element of unsorted list is compared with its predecessor elements of
sorted list and if they are not in order then they are swapped, so that the element is inserted
in its appropriate position of the sorted list.
The Insert sort technique is implemented by using the following C function:
void InsertionSort(int a[], int n)
{
for(i=1; i<n;i++)
for(j = i; j >0; j--)
if(a[j]<a[j-1])
swap(a[j], a[j-1])
}
Time Complexity of Insertion Sort:
Best Case: This case occurs when an array is already in sorted order. So in this case it performs
only O(n) comparisons and even single swap is also not required. As a result, the time
complexity for this scenario is O(n).
Worst Case: This case occurs when an array is in reverse order. For the first iteration of outer
loop, 1 comparison required for inner loop to swap the elements. And for the second iteration, it
required 2 comparisons and so on. Therefore the time complexity is given as
T(n) = 1 + 2 + 3+..................... +(n - 3) + (n – 2)+ (n – 1)
T(n) = n(n – 1)/2. Hence, T(n)= O(n2). This is same as average case also.

Selection Sort:
 A selection sort technique also divides the list into two parts, the sorted part on the left
side and the unsorted part on the right side.
 Initially, the sorted part is empty, and the unsorted part contains the entire list.
 Finally, we get the sorted part contains the entire list in sorted order and unsorted part
becomes empty.
 The mechanism used for this selection sort is given as follows:
i) For a given list of n elements, this technique requires n – 1 passes to sort the elements.
ii) In each pass the minimum element from unsorted list is selected and moved to sorted
list in its proper place.
 Usually this sorting technique is used when the list is small in size.
This selection sort is implemented by using following C function:
void SelectionSort(int a[], int n)
{
int i, j, min,temp;
for(i = 0; i < n - 1; i++)
{
min = i;
for(j = i + 1; j < n; j++)
if(a[j] < a[min])
min = j;
temp = a[i];
a[i] = a[min];
a[min] = temp;
}
Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 8
}
Time Complexity of Selection Sort:
Best Case: This case occurs when an array is already in sorted order. So in this case it performs only
O(n) comparisons and even single swap is also not required. As a result, the time complexity for this
scenario is O(n).
Worst Case: This case occurs when an array is in reverse order. For the first iteration of outer loop,
n-1 comparisons required for inner loop to find the location of minimum element to swap. And for
the second iteration, it required n-2 comparisons and so on. Therefore the time complexity is given
as
T(n) = (n - 1) + (n – 2)+ (n – 3)+ ................. + 3 + 2 +1
T(n) = n(n – 1)/2
T(n)= O(n2)
This is same as average case also.

Quick Sort:
 Quick sort is a highly efficient sorting algorithm and is based on partitioning of array of elements into
smaller arrays.
 A large array is partitioned into two arrays one of which holds values smaller than the specified value,
say pivot, based on which the partition is made and another array holds values greater than the pivot
value.
 Quicksort partitions an array and then calls itself recursively twice to sort the two resulting sub-arrays.
 This technique is quite efficient for large-sized set of elements.
Steps for Quick sort:
1. First select an element which is to be called as pivot element.
2. Next, compare all array elements with the selected pivot element and arrange them in such a way
that, an element less than the pivot element are to its left and greater than pivot is to it's right.
3. Perform the same operations on left and right side elements to the pivot element.
4. Finally, we will get the sorted array of elements.

This quick sort is implemented by using following C functions:

void quicksort(int a[],int p,int q)


{
int j;
if(p<q)
{
j=partion(a,p,q+1);
quicksort(a,p,j-1);
quicksort(a,j+1,q);
}
}
int partion(int a[],int m,int p)
{
int v,i,j,t;
v=a[m], i=m; j=p;
do
{
do{ i++;}while(a[i]<v);
do{j--;}while(a[j]>v);
if(i<j)
{

Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 9


t=a[i];
a[i]=a[j];
a[j]=t;
}
}while(i<=j);
a[m]=a[j];
a[j]=v;
return j;
}

Time Complexity for Quicksort:


Best Case: The best case will occur if each partitioning stage divides the array exactly in half. The
procedure partition always split the array into two equal sized arrays. The total partitioning on each
level will take O(n) time. As we know that there are log n levels, thus the total time for the best case is
O(n log n).
Worst Case: This case occurs when the pivot element splits an array into unbalanced. This case will
happen only one element in one array and all other elements in another array. Usually this case occurs
when the given array is already sorted. So the successive part of partition will split an arrays of length
n,n-1,n-2,n-3,…..,3,2.
Therefore the running time is proportional to the following
n+(n-1)+(n –3)+(n–4)+………+2=(n+2)(n–1)/2
Therefore it is given as T(n) =O(n2).
Average Case: This is most difficult part of the algorithm. This case occurs when we select pivot
randomly. With this assumption, the time complexity is also same as best case i.e O(n log n).

Merge Sort:
 This sorting technique uses divide and conquers strategy. According to it, the array is initially divided
into two equal halves and then they are combined in a sorted manner.
 We can think of it as a recursive process that continuously splits the array in to half until it cannot be
divided further. This means that if the array becomes empty or has only one element left, i.e it is the
case to stop the recursion.
 Then, both halves are sorted by merge operation. This merge operation is the process of taking two
smaller sorted arrays and combines them into a larger sorted list.
 Finally, we will get the sorted array of elements.

This Merge sort is implemented by using following C functions:


void mergeSort(int low,int high)
{
int mid;
if (low<high)
{
int mid = (low+high)/2;
mergeSort (low, mid);
mergeSort(mid+1, high);
merge(low, mid, high);
}

void merge(int low, int mid, int high)


{
int h,i,j,k;
Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 10
h=low; i=low; j=mid+1;

while(h<=mid && j<=high)


{
if(a[h]<=a[j])
{
b[i]=a[h];
h = h+1;
}
else
{
b[i]= a[j];
j=j+1;
}
i=i+1;
}
if (h>mid)
for (k=j;k<=high;k++)
{
b[i]=a[k];
i=i+1;
}
else
for (k=h;k<=mid;k++)
{
b[i]=a[k];
i=i+1;
}
for (k=low;k<=high;k++)
a[k] = b[k];
}

TimeComplexity Merge Sort:


The computing time for merge sort is described by the recurrence relation as,
T(n)=a when n=1, and ’a’ is constant
=2T(n/2)+cn n>1,’c’aconstant.
Therefore it is taken as,
T(n) = 2T(n/2) + O(n)
The array of size n is divided into maximum of log n sub-arrays and merging of all sub-arrays into a single
sorted array takes O(n) time. Therefore the time complexity for all worst, best and average cases is
O(n log n) for merge sort.

Dr. M. Purnachandra Rao, Asso.Prof, Dept. of IT, KITS Page 11

You might also like