Introduction
Introduction
by
Farhan Sufyan
Contents
1
1.2 Abstract Data Types (ADT)
• ADT is a mathematical model or concept that defines a data type logically.
• ADT specifies a set of data items and collection of operations that can be performed on that data.
• ADT only specify what operations are to be performed on the data but not how these operations will be
implemented.
• ADT does not specify how data will be organized in memory.
• It is called “abstract” because it gives an implementation-independent view. The process of providing only
the essentials and hiding the details is known as abstraction.
• The user of data type does not need to know how that data type is implemented, for example, int, float,
char data types only with the knowledge that these data type can operate without any idea of how they are
implemented. So a user only needs to know what a data type can do, but not how it will be implemented.
2
1.2.3 Queue ADT
A Queue contains elements of the same type arranged in sequential order. Operations take place at both ends,
insertion is done at the end and deletion is done at the front. Following operations can be performed:
• 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.
• isFull() – Return true if the queue is full, otherwise return false.
3
1.4 Advantages of the Data Structures
• Efficiency
– DS can be implemented and complied into the libraries to be used again and again.
• Abstraction
– The client program use the DS through an interface without getting into the implementation details.
– For example, in case stack, the user can simply use push() and pop() operations. Implementation of the
stack can be changed from array to linked list, the client program work in the same way.
• Deletion: The process of removing an element from the data structure is called deletion. We can delete an
element from the data structure at any location. If we try to delete an element from an empty data structure
then underflow occurs.
• Searching: The process of finding the location of an element within the data structure is called Searching.
• Sorting: The process of arranging the elements in the data structure in some logical order is known as
Sorting.
• Merging: Combining the records in two different sorted files into single sorted file.
4
1.6 Classification of Data Structures
5
(b) Linear and Non-Linear DS
• Linear DS
– A DS is said to be linear if its elements form a sequence or linear order.
– One way to have a linear relationship between the elements represented by means of sequential
memory locations (eg- Array).
– Another way is to have the linear relationship between the elements represented by the means of
pointer or links (eg- Linked List).
– In linear DS, each element has only one successor and one predecessor, except first and last
element.
– In linear DS, single-level is involved, therefore all the elements can be traversed in a single run.
– Easy to implement.
– eg- String, Array, Stack, Queues.
• Non-Linear DS
– In Non-Linear DS there is no linear order in the arrangements of the elements.
– Elements in non-linear DS are attached in hierarchical manner.
– Non-Linear DS is the multi-level and are not easy to implement.
– Cannot traverse all the elements of DS in a single run.
– Utilize computer memory efficiency in comparison to linear DS.
– eg- Trees, Graph, Files.
(c) Static and Dynamic DS
• Static DS
– In static DS, the size of the structure is fixed, the memory is allocated at compilation time only
and can’t be changed at run time.
– The content of the DS can be modified but without changing the memory space allocated to it.
– Static DS allows faster access to elements but insertion and deletion is expensive.
– eg- Array.
• Dynamic DS
– In Dynamic DS, the memory is located at run-time. Therefore, these DS are flexible in size and
can be modified during the operation performed on it in the run-time.
– Efficient with respect to the memory complexity of the code.
– Dynamic DS allows fast insertion and deletion of the elements but access to elements is slow.
– eg- Linked-List.
6
1.7 Algorithm
An algorithm is defined as a well-defined list of steps for solving a problem. Each step of an algorithm will have a
clear meaning and can be performed with a finite amount of effort and finite length of time. Every algorithm must
satisfy the following criteria,
• Inputs: Zero or more quantities which are externally supplied to the algorithm.
• Output: At least one quantity is produced as output.
• Definiteness: Each step must be clear and unambiguous.
• Finiteness: All steps for all cases of an algorithm will terminate after a finite amount of time.
• An algorithm’s time complexity specifies how long it will take to execute an algorithm as a function of its
input size.
• Similarly, an algorithm’s space complexity specifies the total amount of space or memory required to execute
an algorithm as a function of the size of the input.
• Suppose we have an array of 100 elements, and we want to insert a new element at the beginning of the list.
A List can be implemented using the Array or Linked List
– This becomes a very tedious task in case of array, as we first need to shift the elements towards the
right, and we will add new element at the starting of the array.
– Hence, for inserting a new element at the beginning of requires shifting 100 elements in an array, then
it takes 100 units of time to shift. It concludes that time complexity depends upon the input size.
Therefore, if the input size is n, then f(n) is a function of n that denotes the time complexity.
7
– Suppose we consider the linked list as a data structure to add the element at the beginning. The linked
list contains two parts, i.e., data and address of the next node. We simply add the address of the first
node in the new node, and head pointer will now point to the newly added node.
– Therefore, we conclude that adding the data at the beginning of the linked list is faster than the arrays.
In this way, we can compare the data structures and select the best possible data structure for performing
the operations.
8
1.7.5 Time Complexities or Asymptotic Notations
• In complexity theory, we find complexity f(n) for three major cases as follows:
• Big–O notation considers asymptotic algorithm behavior. The notation tells clearly how the algorithm scales
when input size increases. This is often called the order of growth.
• In theoretical terms, Big –O notation is used to examine the performance/complexity of an algorithm.
• Big –O notation examines an algorithm’s upper bound of performance. It measures the worst case time
complexity or the longest amount of time an algorithm can possibly take to complete.
• This implies that f (n) does not grow faster than g(n), or g(n) is an upper bound on the function f (n). In this
case, we are calculating the growth rate of the function which eventually calculates the worst time complexity
of a function, i.e., how worst an algorithm can perform.
9
10
1.7.6 Various Time Complexities
1. Constant time complexity - O(1)
• When your algorithm is not dependent on the input size n, it is said to have a constant time complexity
with order O(1). This means that the run time will always be the same regardless of the input size.
• For example, if an algorithm is to return the first element of an array. Even if the array has 1 million
elements, the time complexity will be constant if you use this approach:
2. Logarithmic time complexity - O(logn)
• A method with a logarithmic complexity (where n is really big) divides the issue into little bits for each
iteration (log n). It takes log(N) steps to execute a particular operation on N items.
• This method is the second best because your program runs for half the input size rather than the full
size. After all, the input size decreases with each iteration.
• For N = 1, 000, 000, an algorithm that has a complexity of O(log(N)) would undergo 20 steps (with a
constant precision). Here, the logarithmic base does not hold a necessary consequence for the operation
count order, so it is usually omitted.
• For example, Binary Search
3. Linear time complexity - O(n)
• When an algorithm executes n steps for input size n (where n is very large) and the time consumed by
the process changes linearly as the input size rises, the algorithm is said to have complexity O(n).
• To execute an operation on N items it takes about the same number of steps as the number of elements.
• Linear complexity denotes a relationship between the number of components and the number of steps.
•
• For example, Linear Search
4. Log-Linear time complexity - O(nlogn)
• An algorithm with an O(nlogn) complexity (where n is really big) divides the issue into little chunks
for each invocation, then takes each of the smallest bits and stitches them back together (n log n).
Executing a given operation on N items takes N ∗ log(N) steps.
• For a given 1000 elements, the Log-linear time complexity will execute 10, 000 steps for solving a
given problem.
11
7. Factorial time complexity - O(N!)
• The exponential function N! grows even faster; for example, if N = 5 will result in 120. Likewise, if
N = 10, it will result in 3, 628, 800 and so on.
n
8. Double exponential time complexity - O(22 ),
12
1.8 Sample Questions
1. Explain the concepts of data, information, and data structure.
6. Define data structure and discuss the factors influencing the selection of a specific data structure.
7. Analyze the complexity of a given algorithm represented by the pseudocode below:
Algorithm Sum(a,n)
{
s := 0;
for i = 1 to n do
s = s + a[i];
Return s;
}
8. Describe the stack Abstract Data Type (ADT) and implement it using dynamic memory allocation, including
operations like Insert, Delete, Empty, and Full.
9. Differentiate between primitive and non-primitive data types.
10. Explain the concept of time and space trade-off and define various asymptotic notations used to analyze
algorithm performance.
13