Algorithms and Data Structures
Algorithms and Data Structures
1
2 CONTENTS
Instructions
Each instruction is considered to have a constant time complexity, denoted as
O(1).
1 < instruction1 > O (1)
2 < instruction2 > O (1)
The overall time complexity for these instructions is O(1 + 1) = O(2) ⇒ O(1)
because O(constant) = O(1).
Loops
For loops, we consider the number of iterations in each case.
1 for ( i = 0; i < array . length ; i ++) [ n times ]
2 < instruction > [1 time ]
3 < instruction > [1 time ]
Nested Loops
0.2 Arrays
Arrays are used when the size of our data is known.
C = O(1)
Lookup by Value
In the worst case, the value is at the last position of the array:
C = O(n)
Inserting a Value
C = O(1)
In dynamic arrays, if the array is full, we must increase its size. This involves
creating a new array with a larger size and copying all old array values to the
new array:
C = O(n)
0.3. TYPES OF LINKED LISTS 5
To remove an item from an array, we shift the subsequent items to the left,
replacing each element array[i] with array[i+1] starting from the removal index.
In the worst case, when removing the first item, we iterate n − 1 times:
C = O(n)
In a circular singly linked list, the last node of the list contains a pointer to the
first node of the list, forming a circular loop.
A circular doubly linked list combines properties of both doubly linked lists and
circular linked lists. Each element is connected to its previous and next elements,
and the last node points back to the first node, completing the circular structure.
0.4.2 AddLast(item)
Adds an item to the end of the linked list. Time complexity: O(1).
1 Node node = new Node ( item ) ;
2 if ( isEmpty () )
3 head = tail = node ;
4 else {
5 tail . next = node ;
6 tail = node ;
7 }
0.4.3 AddFirst(item)
Adds an item to the beginning of the linked list. Time complexity: O(1).
1 Node node = new Node ( item ) ;
2 if ( isEmpty () )
3 head = tail = node ;
4 else {
5 node . next = head ;
6 head = node ;
7 }
0.4.6 removeFirst()
Removes the first item from the linked list. Time complexity: O(1).
1 if ( isEmpty () )
2 throw new N o S u c h E l e m e n t E x c e p t i o n () ;
3 else if ( head == tail )
4 head = tail = null ;
0.4. SINGLY LINKED LIST OPERATIONS 7
5 else {
6 Node second = head . next ;
7 head . next = null ;
8 head = second ;
9 }
0.4.7 getPrevious(node)
Returns the node preceding a given node in the linked list.
1 Node current = head ;
2 while ( current != null ) {
3 if ( current . next == node )
4 return current ;
5 current = current . next ;
6 }
7 return null ;
0.4.8 removeLast()
Removes the last item from the linked list. Time complexity: O(n).
1 if ( isEmpty () )
2 throw new N o S u c h E l e m e n t E x c e p t i o n () ;
3 else if ( head == tail )
4 head = tail = null ;
5 else {
6 Node previous = getPrevious ( tail ) ;
7 previous . next = null ;
8 tail = previous ;
9 }
0.4.9 removeItem(value)
Removes the first occurrence of a specific value from the linked list. Time
complexity: O(n).
1 if ( isEmpty () )
2 throw new N o S u c h E l e m e n t E x c e p t i o n () ;
3 Node current = head ;
4 Node previous = null ;
5 while ( current != null ) {
6 if ( current . value == value )
7 break ;
8 previous = current ;
9 current = current . next ;
10 }
11 if ( current != null ) {
12 if ( previous == null )
13 removeFirst () ;
14 else {
15 Node nextItem = current . next ;
16 current . next = null ;
17 previous . next = nextItem ;
8 CONTENTS
18
19 if ( current == tail )
20 tail = prev ;
21 }
22 }
0.4.10 Reverse()
1 if ( isEmpty () )
2 return ;
3 Node previous = head ;
4 Node current = head . next ;
5 while ( current != null ) {
6 Node next = current . next ;
7 current . next = previous ;
8 previous = current ;
9 current = next ;
10 }
11 tail = head ;
12 tail . next = null ;
13 head = previous ;
0.4.11 getKthFromEnd(k)
Finds the k-th node from the end of the linked list.
1 if ( k < 1)
2 throw new I n d e x O u t O f B o u n d s E x c e p t i o n ( " Out of bounds index " ) ;
3 Node a = head ;
4 Node b = head ;
5 int index = 0;
6 while ( b != null && index < k ) {
7 b = b . next ;
8 index ++;
9 }
10 if ( index < k )
11 throw new I n d e x O u t O f B o u n d s E x c e p t i o n ( " Out of bounds index " ) ;
12 while ( b != null ) {
13 a = a . next ;
14 b = b . next ;
15 }
16 return a . value ;
0.5. ARRAYS VS LINKED LIST: 9
empty()
Checks if the stack is empty.
1 return peek == null ;
Push(value)
Pushes a value onto the stack.
1 Node newItem = new Node () ;
2 newItem . value = value ;
3 newItem . next = peekNode ;
4 peekNode = newItem ;
pop()
Pops the top value off the stack and returns it.
1 // Check if empty and throw exception if needed
2 var value = peekNode . value ;
3 var next = peekNode . next ;
4 peekNode . next = null ;
5 peekNode = next ;
6 return value ;
10 CONTENTS
peek()
Returns the value at the top of the stack without removing it.
1 // Check if empty and throw exception if needed
2 return peekNode . value ;
Implementation
1 class Stack {
2 private int [] items ;
3 private int top ;
4 private int capacity ;
5
6 public Stack ( int capacity ) {
7 this . capacity = capacity ;
8 this . items = new int [ capacity ];
9 this . top = -1; // Initialize top index to -1 ( empty stack )
10 }
11
12 public boolean isEmpty () {
13 return top == -1;
14 }
15
16 public void push ( int value ) {
17 if ( top == capacity - 1) {
18 // Handle stack overflow ( expand array or throw
exception )
19 System . out . println ( " Stack overflow " ) ;
20 return ;
21 }
22 items [++ top ] = value ;
23 }
24
25 public int pop () {
26 if ( isEmpty () ) {
27 // Handle stack underflow ( throw exception or return
error value )
28 System . out . println ( " Stack underflow " ) ;
29 return -1;
30 }
31 return items [ top - -];
32 }
33
34 public int peek () {
35 if ( isEmpty () ) {
36 // Handle empty stack ( throw exception or return error
value )
37 System . out . println ( " Stack is empty " ) ;
38 return -1;
39 }
40 return items [ top ];
41 }
42 }
0.7 Queue
A Queue is a FIFO (First In First Out) data structure. We use it when we want
to share a resource amoungst many consumers
12 CONTENTS
enqueue
Adds an element to the end of the queue.
1 public void enqueue ( int value )
2 {
3 if ( isFull () )
4 throw new Exception ( " Queue is full " ) ;
5
6 queue [ tail ] = value ;
7 tail = ( tail + 1) % queue . length ;
8 count ++;
9 }
dequeue
Removes and returns the element at the front of the queue.
1 public int dequeue ()
2 {
3 if ( isEmpty () )
4 throw new Exception ( " Queue is empty " ) ;
5
6 int value = queue [ head ];
7 head = ( head + 1) % queue . length ;
8 count - -;
9 return value ;
10 }
isEmpty
Checks if the queue is empty.
1 public boolean isEmpty ()
2 {
3 return count == 0;
4 }
0.7. QUEUE 13
isFull
Checks if the queue is full.
1 public boolean isFull ()
2 {
3 return count == queue . length ;
4 }
• If both stack1 and stack2 are empty, the queue is empty (underflow
condition).
isFull O(1)
add O(n)
isEmpty O(1)
3 return count == 0;
4 }
remove O(1)
18
19 return Character . MIN_VALUE ;
20 }
0.10 Sets
a set is an abstract data type that can store unique values, without any partic-
ular order. It is a computer implementation of the mathematical concept of a
finite set
Recursion
1.1 Trees
19
20 CHAPTER 1. NON-LINEAR DATA STRUCTURE
preorder traversal:
Inorder traversal:
Postorder traversal:
1 public void l e v e l O r d e r T r a v e r s a l ()
2 {
3 for ( int i =0; i <= height () ; i ++)
4 {
5 ArrayList < Integer > list = n od eA t KD is ta n ce ( i ) ;
6 for ( int v : list )
7 {
8 System . out . println ( v ) ;
9 }
10 }
11 }
22 CHAPTER 1. NON-LINEAR DATA STRUCTURE
1.2.4 Equality
1 public boolean equal ( TreeNode node1 , TreeNode node2 )
2 {
3 if ( node1 == null && node2 == null )
4 return true ;
5
6 if ( node1 == null || node2 == null )
7 return false ;
8
9 boolean left = equal ( node1 . leftChild , node2 . leftChild ) ;
10 boolean right = equal ( node1 . rightChild , node2 . rightChild ) ;
11
12 return node1 . value == node2 . value && left && right ;
13
14 }
• Lookup : O(log(n))
• Insert : O(log(n))
• Delete : O(log(n))
1.3.2 Operations
Insert:
recursive insert
6
7 private AVLNode insert ( AVLNode node , int value )
8 {
9 if ( node == null )
10 return new AVLNode ( value ) ;
11
12 if ( node . value > value )
13 node . leftChild = insert ( node . leftChild , value ) ;
14 else
15 node . rightChild = insert ( node . rightChild , value ) ;
16
17 return node ;
18
19 }
find:
1.4.2 Insert
1 public void insert ( int value )
2 {
3 root = insert ( root , value ) ;
4
5 }
6
7 private AVLNode insert ( AVLNode node , int value )
8 {
9 if ( node == null )
10 return new AVLNode ( value ) ;
11
12 if ( node . value > value )
13 node . leftChild = insert ( node . leftChild , value ) ;
14
15 else
16 node . rightChild = insert ( node . rightChild , value ) ;
17
18 setHeight ( node ) ;
19
20 node = balance ( node ) ;
21
22 return node ;
23
24 }
25
26 AVLNode balance ( AVLNode node )
27 {
28 int bf = balanceFactor ( node ) ;
29 if ( bf > 1)
30 {
1.4. AVL TREE 27
86 }
87
88 private int getHeight ( AVLNode node )
89 {
90 if ( node == null )
91 return -1;
92 else
93 return node . height ;
94 }
1.5 Heap
Heap is particular type of tree, witch is:
• Complete: means every level of tree must be filled except from the left
(so we can have only left leafs in the tree)
1.5.2 Operations
Insert:
remove:
29 }
30
31 private boolean hasRightChild ( int index )
32 {
33 return r ig h tC hi ld I nd ex ( index ) < size ;
34 }
35
36
37 private boolean isValid ( int index )
38 {
39 if (! hasLeftChild ( index ) )
40 return true ;
41
42 if (! hasRightChild ( index ) )
43 return heap [ index ] > leftChild ( index ) ;
44
45 return heap [ index ] > leftChild ( index ) && heap [ index ] >
rightChild ( index ) ;
46 }
47
48 private int g r e a t e s t C h i l d I n d e x ( int index )
49 {
50 if (! hasLeftChild ( index ) )
51 return r ig h tC hi l dI nd ex ( index ) ;
52
53 if (! hasRightChild ( index ) )
54 return leftC hildInde x ( index ) ;
55
56 return leftChild ( index ) > rightChild ( index ) ? leftC hildInde x (
index ) : ri gh t Ch il dI n de x ( index ) ;
57 }
58
59 private void bubbleDown ()
60 {
61 int i =0;
62
63 while (i < size && ! isValid ( i ) )
64 {
65
66 int gci = g r e a t e s t C h i l d I n d e x ( i ) ;
67 swap (i , gci ) ;
68 i = gci ;
69 }
70 }
71
72 public int remove ()
73 {
74 if ( isEmpty () )
75 throw new I l l e g a l S t a t e E x c e p t i o n () ;
76
77 int root = heap [0];
78 heap [0]= heap [ - - size ];
79
80 bubbleDown () ;
81
82 return root ;
83
1.5. HEAP 31
84 }
Heapify:
10 }
11
12 for ( int i = a . length -1; i >=0; i - -)
13 a [ i ] = h . remove () ;
14
15 System . out . println ( Arrays . toString ( a ) ) ;
16 }
1.6 Graphs
Each element of graph is called Vertex, and each connection between two ver-
tices is called edge.
We use weights to vertices (edges) to see how strong the connection is be-
tween them.
• Directed graph
Adjacency matrix:
In graph theory and computer science, an adjacency matrix is a square matrix
used to represent a finite graph. The elements of the matrix indicate whether
pairs of vertices are adjacent or not in the graph.
• Add a new node: if the matrix is full we must allocate a new matrix
with new size and copy the old matrix to it so O(V 2 )
Adjacency list:
In graph theory and computer science, an adjacency list is a collection of un-
ordered lists used to represent a finite graph. Each unordered list within an
adjacency list describes the set of neighbors of a particular vertex in the graph.
• remove a node: here we must remove the node so O(V) and remove
it from every edge of other nodes so in worst case (dense graph): O((V-
1)*V)== O(V 2 ) so the totale is O(V + V 2 ) == O(V 2 )
• remove an edge: iterate t5he list to find previous and delete it, O(K),
O(V) in worst case.
What we use:
So in general we use adjacency matrix for dense graph, otherwise we use adja-
cency list.
Iterative method:
5
6 Stack < String > s = new Stack < String >() ;
7 Set < String > visited = new HashSet < >() ;
8
9 for ( String v : adjancecyList . keySet () )
10 t o p o l o g i c a l S o r t i n g (v , visited , s ) ;
11
12 List < String > sorted = new ArrayList < >() ;
13
14 while (! s . empty () )
15 sorted . add ( s . pop () ) ;
16
17 return sorted ;
18 }
19
20 private void t o p o l o g i c a l S o r t i n g ( String node , Set < String >
visited , Stack < String > s )
21 {
22 if ( visited . contains ( node ) )
23 return ;
24
25 visited . add ( node ) ;
26
27 for ( var n : adjancecyList . get ( node ) )
28 {
29 t o p o l o g i c a l S o r t i n g (n , visited , s ) ;
30 }
31
32 s . push ( node ) ;
33
34 }