You are on page 1of 77

Abdelmalek Essaadi University

Java Data Structure

GCyB - I

Pr. Youssef SBAYTRI 2023-2024


Content
I-Introduction

II- Java collection framework

III- Algorithms analysis

IV- Implementation of main data structures

V- Searching algorithms

VI- Sorting algorithms

VII- Recursive algorithms


2
I- Introduction

3
1. Why studying data structure ?

• The study of data structures helps us to understand the basic concepts involved in organizing and
storing data as well as the relationship among the data sets.

• Data structure provides a way to store and organize data in a manner that allows for efficient retrieval
and modification.

• Choosing the right data structure can significantly impact the performance of an application or
algorithm. This enable faster processing and reduced memory consumption (more details in Chapter
IV).

• Data structures are supported by the majority of programming languages, including C, C++, Java,
Python, and others.

• Java provides a rich set of built-in data structures through its libraries (Chapter II). 4
2. Goals of data structure

• Data structure implements two complementary goals.

Correctness Efficiency

Data structure is designed It should process the data at high


such that it operates speed without utilizing much of
correctly for all kinds of the computer resources such as
input memory space.

5
3. Classification of data structure

Depending on the organization of the elements, data structures are classified into two categories :
• Primitive data structure: Primitive data structures consist of the numbers and the characters which
are built in programs. These can be manipulated or operated directly by the machine level instructions.
Basic data types such as integer, real, character, and Boolean come under primitive data structures.

• Non-primitive data structure: Non-primitive data structures are those that are derived from primitive
data structures. These data structures cannot be operated or manipulated directly by the machine level
instructions. In java, non-primitive data structure further divided into:
• Linear data structures: Data elements are accessed in a sequential order but it is not compulsory
to store all elements sequentially (Ex: Array, Linked Lists, Stacks and Queues).

• Non-linear data structures: Data elements are not arranged in a sequential order. There is a
hierarchical relationship between individual data items (Ex: Trees and Graphs).
6
3. Classification of data structure

7
4. Abstract Data Type ?

• Abstract Data Types (ADT) is the combination of data structure with their operations.

• An ADT consists of two parts: Declaration of data and declaration of operations.


➢ For the primitive data structure (int, float, double), the system provides the implementation of
basic operation such as addition and substruction.
➢ For non-primitive data structure, operations should be defined. The implementation for these
operations can be done when we want to actually use them. That means, in general, user defined
data structure along with their operations.

8
2. Abstract Data Type

Properties off ADTs

• The operations of an ADTs are separated from their implementation (The same operation or
functionality can result from different implementations).

• ADTs are language-independent representations of data types (Can be used to specify a new data type
that can then be implemented in various ways using different programming languages).

• In Object-oriented language, classes are language-specific structures that allow implementation of


ADTs.
▪ A given ADTs can be implemented in different ways using different classes (Ex: Stack, Queue,
LinkedList can be implemented in different ways).
▪ A given class can in fact be used to represent more than one ADTs (a Java class ArrayList can
be used to represent a Stack, Queue and other ADTs). 9
II- Java Collection Framework

10
1. Arrays
• Array is a type of data structure that stores data elements of same data types in adjacent locations.

• The array’s length is set when the array is created, and it cannot be changed.

• Array index values must be integers in the range 0...length –1.

public class ArrayMethod1 { public class ArrayMethod2 {


public static void main(String[] args) { public static void main(String[] args) {
int[] arr = new int[6]; int[] arr ={7,15,103,45,23,31};
arr[0] = 7; }
arr[1] = 15; }
arr[2] = 103;
arr[3] = 45;
arr[4] = 23;
arr[5] = 31; Index 0 1 2 3 4 5
}
} Arr 7 15 103 45 23 31
11
1. Arrays
Arrays can be classified according to their dimension into:

➢ One-dimensional Array: It has only one row of elements. It is stored in ascending storage location.

➢ Two-dimensional Array: It consists of multiple rows and columns of data elements. It is also called
as a matrix.

➢ Multidimensional Array: Multidimensional arrays can be defined as array of arrays.


Multidimensional arrays are not bounded to two indices or two dimensions. They can include as
many indices as required.

12
1. Arrays
• Example of a 2D array (3x3) of integers

int[][] Array2D = {
{7, 12, 31},
{14, 62, 49},
{10, 55, 75}
};

// Accessing elements of the 2D array


System.out.println("Element at row 1, column 2: " + Array2D[0][1]);
System.out.println("Element at row 2, column 3: " + Array2D[1][2]);

13
1. Arrays
• Example of a 3D array (2x3x4) of integers

int[][][] Array3D = {
{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12} },
{
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
}
};

// Accessing elements of the 3D array


System.out.println("Element at layer 1, row 2, column 3: " + Array3D[0][1][2]);
System.out.println("Element at layer 2, row 3, column 4: " + Array3D[1][2][3]);
14
1. Arrays
• JAVA standard arrays are of fixed length. Sometime we do not know how much memory we need so
we create a bigger size array. There by wasting space.

• If an array is already full and we want to add more values to it than we need to create a new array,
which have sufficient space and then copy the old array to the new array.

• To avoid this manual reallocation and copy we can use ArrayList or Linked Lists to store data.

15
2. Linked List
• A linked list is a dynamic data structure in which memory is allocated at run time.
• A linked list is considered as a chain of records called nodes. Each node in the list contains two
items: the data and a reference to the next node
• A linked list has the following properties.
➢ The number of nodes in a list is not fixed and can grow and shrink on demand.
➢ The last node has a reference to null.
➢ The entry point into a linked list is called the head (the reference to the first node) of the list.
➢ If the linked list is empty then the head is a null reference.

16
2. Linked List
• Types of Linked List:
➢ Singly Linked List: In a singly linked, each node has some data and a pointer to the next node in the list.

➢ Doubly Linked List: In a doubly linked list each node contains two pointer: one points to the previous node and the
other points to next node. This allows easy traversal in both forward and backward direction.

➢ Circular Linked List: In a circular linked list, the last node points to the first node. This implies that the list can be
traversed infinitely in a loop. Circular linked lists can be implemented using either singly linked nodes or doubly linked
nodes.

17
2. Linked List
import java.util.LinkedList;
LinkedList<Integer> LL = new LinkedList<>(); //Creating a linkedlist
LL.add(10); // Adding elements to the LinkedList
LL.add(20);
System.out.println("LinkedList elements: " + LL); // Displaying the elements of the LinkedList
System.out.println("the first element: "+LL.getFirst()); //get the first element
System.out.println("the second element: "+LL.get(1)); //get the second element
LL.addFirst(5); // Inserting at the beginning
System.out.println("the first element: "+LL.getFirst());
LL.addLast(50); // Inserting at the end
System.out.println("After insertion: " + LL);
LL.removeFirst(); // Removing the first element
LL.removeLast(); // Removing the last element
System.out.println("After deletion: " + LL);
boolean contains30 = LL.contains(30); // Searching
System.out.println("Does the list contain '30'? " + contains30);
System.out.println("Elements of the LinkedList:"); // Traversal
for (int number : LL) {
System.out.println(number); 18
}
2. Linked List

Applications:

➢ Implementing stacks, queues, binary trees and graphs of predefined size.

➢ Implement dynamic memory management functions of operating system. They can be used to
maintain a list of free memory blocks, allowing the operating system to efficiently allocate memory to
processes as needed.

➢ Linked lists can represent polynomials efficiently. Each node of the linked list can store a term of the
polynomial (e.g., coefficient and exponent), allowing for operations such as addition, subtraction,
multiplication, and division of polynomials.

19
2. Linked List
Advantages of Linked List:

➢ Dynamic Data Structure: Linked List being a dynamic data structure can shrink and grow at the runtime by deallocating or
allocating memory, so there is no need for an initial size in linked list.

➢ No Memory Wastage: As the size of a linked list can grow or shrink at runtime, so there is no memory wastage. Only the
required memory is allocated.

➢ Implementation: data structures like queues and stacks can be easily implemented using a Linked List.

➢ Insertion and Deletion Operation: In a Linked List, insertion and deletion operations are quite easy, as there is no need to
shift every element after insertion or deletion. Only the address present in the pointers needs to be updated.

20
2. Linked List
Disadvantages of Linked List:

➢ Slower Access Time: Accessing elements in a linked list requires traversing the list from the beginning (or end) to the
desired position, which can result in slower access times.

➢ Extra Space for Pointers: Linked lists require additional space for storing pointers/references to the next node, which
increases the overall space complexity of the data structure. This overhead can become significant, especially for large lists
or in scenarios where memory usage is critical.

➢ Difficulty in Reversal and Traversal: Singly linked lists can be traversed in only one direction, doubly linked lists support
traversal in both directions but require more memory for storing additional pointers.

➢ Potential for Fragmentation: Linked lists can suffer from memory fragmentation over time, especially in dynamic memory
allocation scenarios. As nodes are dynamically allocated and deallocated, memory fragmentation may occur, leading to
inefficient memory utilization and increased memory management overhead.
21
3. Stacks
• A stack is a linear data structure in which insertion and deletion of elements are done at only one
end, which is known as the top of the stack.
• Stack is called a last-in, first-out (LIFO) structure because the last element which is added to the
stack is the first element which is deleted from the stack.

22
3. Stacks
➢ When an element is inserted in a stack, the concept is called push.

➢ when an element is removed from the stack, the concept is called pop.

➢ Trying to pop out an empty stack is called underflow.

➢ Trying to push an element in a full stack is called overflow.

23
2. Stacks

import java.util.Stack;
public class StacksDemo
{
public static
void main(String[] args) {
Stack<Integer> ST = new Stack<Integer>();
// Pushing elements onto the stack
ST.push(1);
ST.push(2);
ST.push(3);
System.out.println("Stack: "+ST); ); // Displaying the elements of the Stacks
System.out.println("Stack size : "+ST.size()); //Returns the number of elements stored in the stack
System.out.println("Stack top : "+ST.peek()); // Peeking the top element of the stack
System.out.println("Stack pop : "+ST.pop());//Remove the top element of the stack
System.out.println("Stack top : "+ST.peek());
System.out.println("Stack isEmpty : "+ST.isEmpty()); //Verify if the stack is emptyt
}
} 24
3. Stacks
Applications:
➢ Depth-first search of trees and graphs traversal.

➢ Managing function calls and recursion in programming.

➢ Backtracking Algorithms: The backtracking algorithm uses stacks to keep track of the states of the problem-solving
process. The current state is pushed onto the stack, and when the algorithm backtracks, the previous state is popped
from the stack.

➢ UNDO and REDO operation: Undo/Redo operations: Each time an action is performed, it is pushed onto the stack.
To undo the action, the top element of the stack is popped.

➢ Browser history: manage the history of visited pages in web browsers. Each time you visit a new page, the URL is
pushed onto the stack, and when you hit the back button, the previous URL is popped from the stack.

25
3. Stacks
Advantages of stacks:

➢ Easy implementation: Stack data structure is easy to implement using arrays or linked lists, and its operations are simple to
understand and implement.

➢ Efficient memory utilization: Stack uses a contiguous block of memory, making it more efficient in memory utilization as
compared to other data structures.

➢ Fast access time: Stack data structure provides fast access time for adding and removing elements as the elements are
added and removed from the top of the stack.

26
3. Stacks
Disadvantages of stacks:

➢ Limited capacity: Stack data structure has a limited capacity as it can only hold a fixed number of elements. If the stack
becomes full, adding new elements may result in stack overflow, leading to the loss of data.

➢ No random access: Stack data structure does not allow for random access to its elements, and it only allows for adding and
removing elements from the top of the stack. To access an element in the middle of the stack, all the elements above it must
be removed.

➢ Memory management: Stack data structure uses a contiguous block of memory, which can result in memory fragmentation
if elements are added and removed frequently.

➢ Not suitable for certain applications: Stack data structure is not suitable for applications that require accessing elements
in the middle of the stack, like searching or sorting algorithms.

➢ Stack overflow and underflow: Stack data structure can result in stack overflow if too many elements are pushed onto the
27
stack, and it can result in stack underflow if too many elements are popped from the stack.
4. Queue
• A queue is an ordered list in which the elements in a queue are added at one end called the “rear”
and removed from the other end called the “front”.
• The first element to be inserted is the first one to be deleted. Hence, it is called First in First out
(FIFO).

28
4. Queue

import java.util.ArrayDeque;
public class QueueDemo
{
public static
void main(String[] args) {
ArrayDeque<Integer> que = new ArrayDeque<Integer>();
que.add(1);
que.add(2);
que.add(3);
System.out.println("Queue "+que);
System.out.println("Queue size : "+que.size());
System.out.println("Queue peek : "+que.peek());
System.out.println("Queue remove : "+que.remove());
System.out.println("Queue isEmpty : "+que.isEmpty());
}
}
29
4. Queue
Applications:
➢ Operating System Scheduling: In an operating system, queues are used to schedule processes and
allocate resources.
➢ Computer Networks: Queues are also used in computer networks to manage network traffic.
➢ Simulation and Modeling: Queues are commonly used in simulation and modeling to simulate real-
life scenarios. For example, a queue can be used to simulate the waiting time at a supermarket
checkout counter.
➢ Traffic Management Systems: Queues are used in traffic management systems to manage traffic
flow and avoid congestion.

30
4. Queue

Advantages of queue:

➢ Queues are easy to implement and understand.

➢ They are efficient for storing and accessing elements in a specific order.

➢ Queues are suitable for applications that require processing elements in a FIFO manner.

Disadvantages of queue:

➢ Queues have a fixed size, which means they can’t accommodate more elements than their size.

➢ Adding and removing elements from the middle of the queue is not efficient and requires shifting all the
elements.

31
5. Tree
• Tree is a hierarchical data structure. The top node of a
tree is called the root of the tree.
• A tree can contain no nodes or root node with zero or
more subtrees.
• Except the root node, every node in a tree has a parent
node, and zero or more child nodes.
• Every edge of the tree is directly or indirectly originated
from the root.
• Every child has only one parent, but one parent can
have many children.
• A leaf node is a node that has no children. It's at the end
of the tree branch.

32
5. Tree

• The depth of a node is the length of the path from the root to
the node (The depth of B is 2, P-Q-B).

• The height of a node is the length of the path from that node
to the deepest node (The height of R is 3, R-C-M-I).

• The height of a tree is the length of the path from the root to
the deepest node in the tree (The height of this tree is 4).

• The size of a node is the number of descendants it has


including itself (The size of subtree L is 4).

33
5-1. Binary Tree
A binary tree is a type of tree in which each node has at most two children (0, 1 or 2) which are referred as left child and
right child, binary tree also can divide into three category:
➢ Strict Binary Tree: A binary tree is called strict binary tree if each node has exactly two children or no children.
➢ Complete binary tree : A binary tree is called complete binary tree if all leaf nodes are at height h or h-1 (h is
height of the binary tree) and also without any missing number in the sequence.
➢ Perfect Binary Tree: A binary tree is called perfect binary tree if each node has exactly two children and all
leaf nodes are at the same level.

34
5-1. Binary Tree

A binary search tree (BST) is a special type of binary tree in which the left child of a node contains a value smaller than
the node's value, and the right child contains a value greater than the node's value. This property enables efficient
searching, insertion, and deletion operations.

35
5. Tree
• Tree implementation in JAVA Collection
➢ TreeSet: Implements the NavigableSet interface and import java.util.TreeSet;
public class TreeDemo {
is also based on a red-black tree. It represents a set public static
where elements are sorted in their natural order or void main(String[] args) {
// Create a tree set.
according to a specified comparator. TreeSet<String> ts = new TreeSet<String>();
// Add elements to the tree set.
ts.add("A");
➢ TreeMap: Implements the NavigableMap interface ts.add("B");
and is based on a red-black tree. It provides a sorted ts.add("C");
System.out.println(ts);
key-value mapping, where keys are sorted in their }
natural order or according to a specified comparator. }

36
5. Tree
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<String, Integer> tm = new TreeMap<String, Integer>(); // create a tree map.
// Put elements into the map
tm.put("Adam", 55);
tm.put("Sara", 77);
tm.put("Ali",89);
tm.put("rim", 75);
System.out.println("Students count: " + tm.size());
for(String key : tm.keySet()){
System.out.println(key + " score marks:" + tm.get(key));
}
System.out.println("Adam score: " + tm.containsKey("Adam"));
tm.remove("sara");
System.out.println(tm.toString());
}
}
37
5 Tree

Application
The tree is the most useful data structure when we have hierarchical information to store/maintain:
• Implementing the hierarchical structures in computer systems like directory and file system.
• Implementing the navigation structure of a website.
• Decision making in gaming applications.
• Implementation of priority queues for priority-based OS scheduling functions
• Storing data keys for DBMS for indexing
• Spanning trees for routing decisions in computer and communications networks
• Path-finding algorithm to implement in AI, robotics and video games applications

38
5 Tree

Application
The tree is the most useful data structure when we have hierarchical information to store/maintain:
• Implementing the hierarchical structures in computer systems like directory and file system.
• Implementing the navigation structure of a website.
• Decision making in gaming applications.
• Implementation of priority queues for priority-based OS scheduling functions
• Storing data keys for DBMS for indexing
• Spanning trees for routing decisions in computer and communications networks
• Path-finding algorithm to implement in AI, robotics and video games applications

39
5 Tree

Advantages
• Hierarchical Structure: Trees represent hierarchical relationships among elements, making them suitable for
organizing and representing hierarchical data, such as file systems, organizational charts, and XML/HTML
documents.
• Efficient Search: Balanced trees (e.g., binary search trees, AVL trees, red-black trees) provide efficient search
operations with time complexity O(log n), where n is the number of elements in the tree. This makes them suitable for
applications requiring fast search operations.
• Ordered Data: Some trees, like binary search trees and B-trees, maintain elements in sorted order. This makes it
easy to retrieve elements in sorted order, which is useful in various applications like searching for nearest neighbors
or iterating through elements in sorted order.
• Dynamic Size: Trees can dynamically grow and shrink in size, allowing for efficient insertion and deletion operations
without requiring a fixed-size structure. 40
5 Tree

Disadvantages
• Complexity: Some types of trees, especially balanced trees, can be complex to implement and understand.
Managing balance conditions, rotations, and other operations can be challenging.
• Memory Overhead: Trees often have a higher memory overhead compared to simpler data structures like arrays or
linked lists. Each node in a tree typically requires additional memory for storing references to child nodes, leading to
increased memory consumption.
• Traversal Complexity: Traversing trees can sometimes be complex, especially in the case of unbalanced trees. In
worst-case scenarios, traversal operations can degrade to linear time complexity, making them less efficient than in
balanced trees.
• Insertion and Deletion Overhead: While balanced trees offer efficient insertion and deletion operations on average,
in some cases, these operations can involve complex restructuring of the tree, leading to increased overhead.

41
6. HashMap
• HashMap is a data structure that implements the Map interface provided by the Java Collections
Framework. It stores key-value pairs and allows rapid retrieval of values based on their associated keys.

• The HashMap class provides methods to add, remove, and retrieve elements from the map.
➢ The put() method is used to add a key-value pair to the map,
➢ The remove() method is used to remove a key-value pair,
➢ The get() method is used to retrieve the value associated with a key.

• Hashing is used to provide fast access to the elements in the map. When a key-value pair is added to the
map, the key is hashed to generate an index into an array of buckets. Each bucket contains a linked list
of elements with the same hash code. When a key is used to retrieve a value, its hash code is used to
locate the appropriate bucket, and the linked list is searched for the key.
42
6. HashMap
import java.util.HashMap;
public class HashMapDemo {
public static void main(String[] args) {
// Create a new HashMap to store country-domain mappings
HashMap<String, String> countryTLD = new HashMap<>();
// Add country-domain mappings to the HashMap
countryTLD.put("Morocco", ".ma");
countryTLD.put("France", ".fr");
countryTLD.put("United Kingdom", ".co.uk");
// Print the TLD results
System.out.println(countryTLD.toString());
System.out.println(countryTLD.hashCode());
countryTLD.replace("Morocco",".ma",".ac.ma");
System.out.println(countryTLD.toString());
System.out.println(countryTLD.hashCode());
countryTLD.remove("France"); // Remove a country tld mapping
System.out.println("After removing France, size of country TLD: " + countryTLD.size());
}
43
}
6. HashMap

Advantages
• Fast Retrieval: HashMap provides constant time O(1) performance for retrieving elements. This means that
regardless of the size of the map, the time to retrieve an element remains the same.
• Efficient Storage: HashMap uses a hash table data structure to store key-value pairs. This provides efficient storage
and retrieval of elements, as the hash function can map the key to the index of the array directly.
• Flexibility: HashMap can store any type of object as a key or value. This allows for a wide range of data to be stored
in the map.
• Dynamic Sizing: HashMap is dynamically sized, which means that the size of the map can be changed as elements
are added or removed. This allows the map to be optimized for memory usage and performance

44
6. HashMap

Disadvantages
• Unordered: The items in a HashMap are not stored in any particular order. This means that if you need to retrieve the
items in a specific order, you will need to sort the items yourself.
• Performance: In certain situations, the performance of a HashMap may not be optimal. This is because the hash
code function used to generate the keys must distribute the keys evenly to avoid collisions, which can lead to longer
lookup times.
• Thread safety: HashMaps are not thread-safe by default. This means that if multiple threads access the same
HashMap object concurrently, it can lead to data corruption or inconsistency. You can use synchronized or concurrent
HashMaps to overcome this issue, but this comes at the cost of performance.
• Memory usage: HashMaps can use a lot of memory, especially if the hash table needs to be resized. When the load
factor is exceeded, the HashMap needs to allocate a new, larger array and rehash all the elements. This can be a
costly operation in terms of time and memory usage. 45
III- Algorithms analysis

46
1. Why algorithms analysis?

• The analysis of an algorithm can help us understand it better and can suggest informed improvements.

• The main and important role of algorithm analysis is to predict the performance of different algorithms in
order to guide design decisions.

• The goal of the algorithms analysis is to compare algorithms mainly in terms of running time and
memory consumption.

• Most algorithms are designed to work with inputs of arbitrary length.

• Analysis of algorithms is the determination of the amount of time and space resources required to
execute it.

47
1. Why algorithms analysis?

• Algorithm analysis involves assessing various aspects of an algorithm, including its correctness, and
efficiency:
➢ Correctness: The algorithm should be correct. It should be able to process all the given inputs
and provide correct output.

➢ Efficiency: The algorithm should be efficient in solving problems. Efficiency of an algorithm can
be analyzed at two different stages, before implementation and after implementation:
➢ A Priori Analysis or Asymptotic Analysis: This is a theoretical analysis of an algorithm.
In this type of analysis we assume that all other factors like processor speed are constant
and have no effect on the implementation. We distinct two parameters:
✓ First is Time Complexity, how quick result is provided by an algorithm.
✓ Second is Space Complexity, how much memory space that an algorithm is going to
consume to give desired result.
48
1. Why algorithms analysis?

➢ A Posterior Analysis or Performance Measurement: The selected algorithm is implemented


using programming language and then executed on target computer machine. In this analysis,
actual statistics like running time and space required, are collected.

49
2-1 Time complexity

• Time complexity is a measure used to analyze the efficiency of an algorithm in terms of the amount of
time it takes to execute as a function of the length of the input.

• In time complexity, we are essentially estimating how the number of basic operations (such as
comparisons, assignments, arithmetic operations, etc.) increases with the size of the input. Each step or
operation contributes to the overall runtime of the algorithm.

• By analyzing the number of steps or operations, we can understand how the algorithm scales with
larger inputs and make predictions about its performance under different scenarios.

• This analysis allows us to compare algorithms and choose the most efficient one for a particular
problem or set of inputs.

50
2-2. Space complexity

• Space complexity of an algorithm represents the amount of memory space required by the algorithm in
its life cycle. The space required by an algorithm is equal to the sum of the following two components:

• A fixed part that is a space required to store certain data and variables, that are independent of
the size of the problem. For example, simple variables and constants, etc.

• A variable part is a space required by variables, whose size depends on the size of the problem.
For example, dynamic memory allocation, recursion stack space, etc.

51
3. Types of Analysis

• To analyze the given algorithm, we need to know with which inputs the algorithm takes less time
(performing well) and with which inputs the algorithm takes a long time.
• For such algorithms, we differentiate between the worst case, average case and best case analysis:

➢ Best Case Analysis: If an algorithm takes the least amount of time to execute a specific set of
input, then it is called the best case time complexity. The best case efficiency of an algorithm is
the efficiency for the best case input of size n. Because of this input, the algorithm runs the fastest
among all the possible inputs of the same size.

➢ Average Case Analysis: If the time complexity of an algorithm for certain sets of inputs are on an
average, then such a time complexity is called average case time complexity. Average case
analysis provides necessary information about an algorithm’s behavior on a typical or random
input.

52
3. Types of Analysis

➢ Worst Case Analysis: If an algorithm takes maximum amount of time to execute for a specific set
of input, then it is called the worst case time complexity. The worst case efficiency of an algorithm
is the efficiency for the worst case input of size n. The algorithm runs the longest among all the
possible inputs of the similar size because of this input of size n.

53
4-1. Asymptotic Notations

• Asymptotic notation is used to express the time and space complexity of an algorithm using a function
f(n) where n is the input size. It is used to analyze the algorithms. There are three main types of
asymptotic notations:
➢ Big-O Notation: Big O notation represents the upper bound of the growth rate of a function.
➢ Omega Notation: Omega notation represents the lower bound of the growth rate of a function.
➢ Theta Notation: Theta notation represents both the upper and lower bounds of the growth rate of
a function.

54
4-1. Asymptotic Notations

• ‘O’ is the representation for Big-O notation. Big-O is the method used to express the upper bound of the
running time of an algorithm.
• Big-O specifically describes the worst-case scenario and can be used to describe the execution time
required or the space used by the algorithm.
• Example of the common Big-O notation:

55
4-1. Asymptotic Notations

• The Big-O can be denoted as f(n) = O(g(n)), where, n can be any number of inputs or outputs and f(n)
as well as g(n) are two non-negative functions and f(n) < g(n) if g(n) is multiple of some constant c:
f(n) <=c * g(n)
• Example: Consider f(n)=4n^3 + 8n^2 + 5nlogn + 6n.
➢ As the value of n increases, n^3 becomes much larger than n^2, nlogn, and n.
➢ n^3 dominates the function f(n) and we can consider the running time to grow by the order of
n^3.
➢ Therefore, it can be written as f(n)=O(n^3).
• In this course, our emphasis will be on Big-O notation as it focus on the worst-case scenario, crucial for
assessing an algorithm's efficiency.

56
4-2. Rules of calculation

• There are some general rules to help us determine the time complexity of an algorithm:
➢ Loops: The running time of a loop is, at most, the running time of the statements inside the loop
(including tests) multiplied by the number of iterations.

➢ Total time = c * n = O(n)

57
4-2. Rules of calculation
➢ Nested loops: Analyze from the inside out. Total running time is the product of the sizes of all the
loops.

➢ Total time = c * n * n = c*n^2 = O(n^2)

58
4-2. Rules of calculation
➢ Consecutive statements: Add the time complexities of each statement

➢ Total time = c0 + c1*n + c2*n^2 = O(n^2)

59
4-2. Rules of calculation
➢ If-then-else statements: Worst-case running time: the test, plus either the then part or the else part
(which take more time).

➢ Total time = c0 + (c1 + c2)*n = O(n)

60
4-2. Rules of calculation
Useful formula in big-O calculation

61
IV- Implementation of main data structures

62
1. Singly Linked List Implementation

• A Singly-linked list is a collection of nodes linked together in a sequential way where each node of the
singly linked list contains a data field and an address field that contains the reference of the next node.
• The structure of the node in the Singly Linked List is: public class LinkedList{
private Node head;
class Node { private int length;
int data; class Node {
Node next; int data;
Node(int data) { Node next;
this.data = data; Node(int data) {
} this.data = data;
} }
}
public LinkedList(int val){
Node newNode = new Node(val);
head= newNode;
length=1;
}
63
}
1. Singly Linked List Implementation

• To create a singly linked list of one node:

public static void main(String[] args) {


LinkedList myLL = new LinkedList(1);
}

• To display the data of linked list we must verify if the linked list is empty.
public void DisplayLinkedList() {
Node current = head;
System.out.print("head ==> ");
while (current != null) {
System.out.print(current.data + " => ");
current = current.next;
}
System.out.println("Null");
} 64
1. Singly Linked List Implementation

• The various basic operations that we performing on linked lists are:


➢ Insert element into a linked list.
➢ Remove element from linked list.
➢ Search element by index/value.
➢ Update element in a linked list.

65
1-1. Insert element in Singly Linked List

• An element can be inserted into a linked list in various orders:


➢ Insertion of an element at the start of linked list
➢ Insertion of an element at the end of linked list
➢ Insertion of an element at the Nth position of linked list (this operation requires get function at
position N).

66
1-1-1. Insertion at the start of Singly Linked List

• Analysis:
➢ We need to create a new node with the value passed to the function as argument.
➢ The next pointer of the new node will point to the head of the linked list or null in case when list is
empty.
➢ The newly created node will become head of the linked list.
➢ Time Complexity: O(1).

67
1-1-1. Insertion at the start of Singly Linked List

public void addFirst(int val) {

Node newNode = new Node(val);

newNode.next = head;

head = newNode;
68
}
1-1-2. Insertion at the end of Singly Linked List
public void addEnd(int val) {
• Analysis:
➢ New node is created and the value is stored inside it
Node newNode = new Node(val);
and store null value to its next pointer.

if (head == null) {
➢ If the list is empty then this new node will become head = newNode;
head of linked list. }

Node current = head;


➢ If list is not empty then we have to traverse to the while (current.next != null) {
current = current.next;
end of the list and the new node. }
current.next = newNode;

➢ Time Complexity: O(n). 69


}
1-1-2. Insertion at the end of Singly Linked List
public void addEnd(int val) {
• Analysis:
➢ New node is created and the value is stored inside it
Node newNode = new Node(val);
and store null value to its next pointer.

if (head == null) {
➢ If the list is empty then this new node will become head = newNode;
head of linked list. }

Node current = head;


➢ If list is not empty then we have to traverse to the while (current.next != null) {
current = current.next;
end of the list and the new node. }
current.next = newNode;

70
}
1-1-2. Insertion at the end of Singly Linked List

• What is the inconvenience of the previous method?


➢ The previous method is un-efficient as each time we want to insert an element we have to
traverse to the end of the linked list which means a time Complexity of O(n).

• What do you propose as solution ?


➢ So to make it efficient we have to keep track of the last element by adding a tail pointer.
Therefore, if it is required to insert element at the end of linked list, then we will keep track of the
tail reference also. And the time complexity become O(1).

71
1-1-2. Insertion at the end of Singly Linked List
public void addEnd(int val) { public void addEnd(int val) {

Node newNode = new Node(val); Node newNode = new Node(val);

if (head == null) { if (head == null) {


head = newNode; head = newNode;
} tail = newNode;
}

Node current = head;


while (current.next != null) { else{
current = current.next; tail.next=newNode;
} tail=newNode;
current.next = newNode; }

} 72
}
1-1-3. Insertion at a position N

• Let first implement Get element by index function

public Node Get(int index){


if(index<0 || index>=length){
return null;
}else{
Node temp = head;
for(int i=0;i<index;i++){
temp=temp.next;
}
return temp;
}
}

73
public boolean insert(int index, int value){
1-1-3. Insertion at a position N if(index<0 || index>length) return false;

Insert element at position N if(index==0){


addFirst(value);
• If N<0 or N>length Return Null return true;
• If N=0 call addFirst (value) }

• If N=length call addEnd(value) if(index==length){


addEnd(value);
return true;
}

• Otherwise: Node newNode=new Node(value);


➢ Create a new Node newNode Node temp = get(index-1);
➢ Get the Node temp of index ’N -1’ newNode.next=temp.next;
temp.next=newNode;
➢ Point newNode to Node of index N (temp.next) length++;
➢ Point Node temp to newNode return true;
}
74
1-2. Remove element from a Singly Linked List

• An element can be removed from linked list in various orders:


➢ Remove first element
➢ Remove last element
➢ Remove an element from Nth position of linked list (this operation require get an element at
position N).

75
1-2-1. Remove first element in a Singly Linked List

public Node removeFirst(){


if(head==null) return null;

Node temp = head;

head = head.next;

temp.next=null;

length--;
return temp;
} 76
1-2-2. Remove last element in a Singly Linked List
public Node removeLast(){
if(head==null) return null;

Node temp = head;


Node pre = head;

while(temp.next!=null){
pre=temp;
temp=temp.next;
}

tail=pre;
tail.next=null;
length--;
return temp;
}

77

You might also like