Data Structures and Algorithms
Data Structures and Algorithms
May 2024
Addis Ababa, Ethiopia
1
Data Structures and Algorithms Analysis
2
I. Big-Oh Notation (O): It’s only concerned with what happens for very a large value of n.
Big-O expresses an upper bound on the growth rate of a function, for sufficiently large
values of n. An upper bound is the best algorithmic solution that has been found for a
problem. NB: Each of the following functions is big-O of its successors:
II. Big-Omega Notation (): provides an asymptotic lower bound; f(n)= ( g (n)) means
that the growth rate of f(n) is greater than or equal to g(n).
III. Theta Notation (): If f(n)= (g(n)), then g(n) is an asymptotically tight bound for f(n)
i.e f(n) and g(n) have the same rate of growth.
IV. Little-o Notation (o): f(n)=o(g(n)) means f(n) has less growth rate compared to g(n).
V. Little-Omega Notation (): notation used to denote a lower bound that is not
asymptotically tight.
B. Selection Sort
Loop through the list/array from i=0 to n-1 and select the smallest element in the list and swap
this value with value at position i. Repeat this step for the rest of the items.
How many comparisons? => O(n2) and How many swaps? => O(n)
3
C. Bubble Sort
The simplest to implement but slowest algorithm on very large inputs. The algorithm loops through
the array from i=0 to n and swap adjacent elements if they are out of order bubbling or pushing
out the smallest or largest element to its proper position.
How many comparisons? => O(n2) and How many swaps? => O(n2)
Here, struct is the required keyword; tag (optional) is a name that identifies structures of this type;
and member1, meber2, …, member m are individual member declarations.
The individual members can be ordinary variables, pointers, arrays, or other structures.
A storage class cannot be assigned to an individual member, and individual members
cannot be initialized within a structure type declaration.
4
II. Adding node to the list
While adding a node to a list, the chain needs to be rearranged properly so that the each nodes
are connected to each other. It is possible to add a node anywhere in the chain (at the start,
middle or end). Let’s consider adding a node at the end of the list:
Step 1: First we need to reserve a space for the new node using the keyword new.
Node *temp = new *Node; // Reserving a space for temporary pointer which points to new
node;
Step 2: Then using the above declared node pointer acquire the data for the new node
cin >> temp->Name;
cin >> temp->CGPA;
temp->next = NULL; // Indicates that this node will be the last node in the list.
Step 3: Then add the node at the end of the list. For this we need to check whether the list is
empty or not. If list is empty the new node will be the first (Start) node, otherwise navigate
forward starting form the first node till last node found and add it next to this node which make
the new node the last node.
if (start == NULL) // If list is empty, then new node become start node
start = temp;
else {
temp2 = start; // list not empty! Thus temp2 starts from first node
while (temp2->next!= NULL) { // till the current last node found navigates forward
temp2 = temp2->next; // Move to next link in chain
}
temp2->next = temp; // new node (temp) appended at the end of the list
}
III. Displaying the list of Nodes
After adding nodes to the list, we need to display the list of nodes on the screen. Here are the
steps required to display all nodes item in the list:
1. Set a temporary pointer to point to the same thing as the start pointer.
2. If the pointer points to NULL, display the message "End of list" and stop.
3. Otherwise, display the details of the node pointed to by the start pointer.
4. Make the temporary pointer point to the same thing as the next pointer of the node it is
currently indicating.
5. Jump back to step 2.
5
1. Look at the start pointer. If it is NULL, then the list is empty, so print out a "No nodes to delete"
message.
2. Make temp1 point to whatever the start pointer is pointing to.
3. If the next pointer of what temp1 indicates is NULL, then we've found the last node of the list, so
jump to step 7.
4. Make another pointer, temp2, point to the current node in the list.
5. Make temp1 point to the next item in the list.
6. Go to step 3.
7. If you get this far, then the temporary pointer, temp1, should point to the last item in the list and
the other temporary pointer, temp2, should point to the last-but-one item.
8. Delete the node pointed to by temp1.
9. Mark the next pointer of the node pointed to by temp2 as NULL - it is the new last node.
2. STACK :
“A stack is an ordered list in which all insertions and deletions are made at one end, called the
top”. Stacks are sometimes referred to as Last in First out (LIFO) lists. Stacks have some
useful terminology associated with them:
Push To add an element to the stack
Pop To remove an element from the stock
Peek To look at elements in the stack without removing them
LIFO Refers to the last in, first out behavior of thestack
FILO (First in Last Out) : equivalent to LIFO
Implementation of stack:
I. Array implementation (static memory).
Push(n): Adding item at top Pop(): removing item from stack top
top is the current top of stack and n is its remove top element from the stack and put it in
maximum size the item
Begin Begin:
if (top == n) then stack is full; if (top == 0) then stack is empty;
else else
top := top+1; item := stack[top];
stack[top] := item; top := top-1;
end: end;
II. Linked list (dynamic memory)
The linked list implementation of stack is adding items at the end of the chain (Push item) and
deleting the end node of the list (Pop item).
Applications of Stack
It is very useful to evaluate arithmetic expressions. (Postfix Expressions)
Infix to Postfix Transformation
It is useful during the execution of recursive programs
A Stack is useful for designing the compiler in operating system to store local
variables inside a function block.
A stack (memory stack) can be used in function calls including recursion.
6
Reversing Data and Reverse a List
Convert Decimal to Binary
Parsing – It is a logic that breaks into independent pieces for further processing
Backtracking
Examples:
1. Infix notation: A+(B*C) Equivalent Postfix notation ABC*+
2. Infix notation: (A+B)*C Equivalent Postfix notation AB+C*
3. Queue
“A queue is an ordered list in which all insertions at one end called REAR and deletions are made
at another end called FRONT”. Queues are sometimes referred to as First in First out (FIFO) lists.
The two basic operations are:
Enqueue – Inserts an item / element at the rear end of the queue. An overflow error occurs
if the queue is full.
Dequeue – Removes an item / element from the front end of the queue, and returns it to
the user. An underflow error occurs if the queue is empty.
7
Example: Consider a queue with MAX_SIZE = 4; the following table demonstrate the difference
between simple array and circular array implementation of Queue!
Simple array Circular array
Operation Content of Content of QUEUE Message Content of Content of QUEUE Message
the array the Queue SIZE the array the queue SIZE
Enqueue(B) B B 1 B B 1
Enqueue(C) B C BC 2 B C BC 2
Dequeue() C C 1 C C 1
Enqueue(G) C G CG 2 C G CG 2
Enqueue (F) C G F CGF 3 C G F CGF 3
Dequeue() G F GF 2 G F GF 2
Enqueue(A) G F GF 2 Overflow A G F GFA 3
Enqueue(D) G F GF 2 Overflow A D G F GFAD 4
Enqueue(C) G F GF 2 Overflow A D G F GFAD 4 Overflow
Dequeue() F F 1 A D F FAD 3
Enqueue(H) F F 1 Overflow A D H F FADH 4
Dequeue () Empty 0 A D H ADH 3
Dequeue() Empty 0 Underflow D H DH 2
Dequeue() Empty 0 Underflow H H 1
Dequeue() Empty 0 Underflow Empty 0
Dequeue() Empty 0 Underflow Empty 0 Underflow
Application of Queue
Queue is good for fair (first come first served) ordering of actions. Some of queue applications:
Print server- maintains a queue of print jobs
Disk Driver- maintains a queue of disk input/output requests
Task scheduler in multiprocessing system- maintains priority queues of processes
Telephone calls in a busy environment –maintains a queue of telephone calls
Simulation of waiting line- models real life queues (e.g. supermarkets checkout,
customer service line (Bank, custom, airport etc) )
8
4. TREE STRUCTURES
A tree is a set of nodes and edges that connect pairs of nodes that connect pairs of nodes. It is an
abstract model of a hierarchical structure. Rooted tree has the following structure:
One node distinguished as root.
Every node C except the root is connected from exactly other node P. P is C's parent,
and C is one of C's children.
There is a unique path from the root to the each node.
The number of edges in a path is the length of the path.
Tree Terminologies:
Root: a node without a parent.
Internal node: a node with at least one child.
External (leaf) node: a node without a child.
Ancestors of a node: parent, grandparent, grand-grandparent, etc of a node.
Descendants of a node: children, grandchildren, grand-grandchildren etc of a node.
Depth of a node: number of ancestors or length of the path from the root to the node.
Height of a tree: depth of the deepest node.
Subtree: a tree consisting of a node and its descendants.
Binary tree: a tree in which each node has at most two children called left child and right child.
Binary search tree (ordered binary tree): a binary tree that may be empty, but if it is not empty it
satisfies the following.
o Every node has a key and no two elements have the same key.
o The keys in the right subtree are larger than the keys in the root.
o The keys in the left subtree are smaller than the keys in the root.
o The left and the right subtrees are also binary search trees.
Operations on Binary Search Tree
I. Tree structure definition:
Example: struct Node{
int Num; // Node value/Number
Node * Left, *Right; // Pointer to Left and right Children
};
Node *RootNodePtr=NULL; // pointer to root Node
II. Insertion
When a node is inserted the definition of binary search tree should be preserved.
Case 1: There is no data in the tree (i.e. RootNodePtr is NULL)
The node pointed by InsNodePtr should be made the root node.
9
IV. Searching
To search a node (whose Num value is Number) in a binary search tree (whose root node is
pointed by RootNodePtr), one of the three traversal methods can be used.
V. Deletion
To delete a node (whose Num value is N) from binary search tree (whose root node is pointed by
RootNodePtr), four cases should be considered. When a node is deleted the definition of binary
search tree should be preserved.
Case 1: Deleting a leaf node (a node having no child): it is simple and straightforward operation.
Delete the leaf node and make the pointer of the deleted node parent NULL (right or left child
pointer based on whether the deleted child is left child or right child)
Case 2: Deleting a node having only one child
Case 3: Deleting a node having two children
Case 4: Deleting the root node
Note: For case 2, 3 and 4 there are two approaches
Approach 1: Deletion by copying
Copy the node containing the largest element in the left (or the smallest element in the right)
to the node containing the element to be deleted
Delete the Copied Node
Approach 2: Deletion by merging
If deleted node is Root:
the root node pointer is made to point to the right child or left child
the left child/right child of the root node is made the left child/right child of the node
containing the smallest element/largest in the right/left of the root node respectively.
If deleted node has only one child:
Promote a child (left or right) of deleted node as a child (left or right) for the parent of the
deleted node keeping the definition of BST.
If deleted node has two children:
10
Promote a child (left or right) of deleted node as a child for the parent of the deleted node.
The other child of the deleted node is made the left child of the node containing smallest
element in the right of the deleted node or the right child of the deleted node is made the
right child of the node containing largest element in the left of the deleted node
11