Data Structures Intro Jan2024
Data Structures Intro Jan2024
3
Chapter One:
Data structures and Abstracts
Data structure is a representation of data and the
operations allowed on that data.
Data Structures
Physical implementation of an ADT
data structures used in implementations are provided in a language
(primitive or built-in) or are built from the language constructs (user-
defined)
Each operation associated with the ADT is implemented by one or
more subroutines in the implementation
Abstract Data Type
ADTs support abstraction, encapsulation, and information
hiding.
The data type is the form of a variable to which a value can be assigned. It
Data structure is a collection of different kinds of data. That entire data can
defines that the particular variable will assign the values of the given data
be represented using an object and can be used throughout the program.
type only.
It can hold value but not data. Therefore, it is dataless. It can hold multiple types of data within a single object.
The implementation of a data type is known as abstract implementation. Data structure implementation is known as concrete implementation.
There is no time complexity in the case of data types. In data structure objects, time complexity plays an important role.
While in the case of data structures, the data and its value acquire the space
In the case of data types, the value of data is not stored because it only
in the computer’s main memory. Also, a data structure can hold different
represents the type of data that can be stored.
kinds and types of data within one single object.
Data type examples are int, float, double, etc. Data structure examples are stack, queue, tree, etc.
Need Of Data Structure:
The structure of the data and the synthesis of the algorithm are relative to
each other. Data presentation must be easy to understand so the developer, as
well as the user, can make an efficient implementation of the operation.
Data structures provide an easy way of organising, retrieving, managing, and
storing data.
Here is a list of the needs for data.
▪ • Data structure modification is easy.
▪ • It requires less time.
▪ • Save storage memory space.
▪ • Data representation is easy.
▪ • Easy access to the large database
Applications of Data Structures:
• Operating system
• Graphics
• Computer Design
• Blockchain
• Genetics
• Image Processing
• Simulation,
• etc.
Algorithms
An algorithm is a sequence of steps that must be
carried out in order to accomplish a particular task.
Three things are to be considered while writing an
algorithm: input, process, and output.
The input that we give to an algorithm is processed
with the help of the procedure and finally, the
algorithm returns the output.
Features/characteristics of algorithms.
Not all procedures can be called an algorithm. An algorithm should
have the following characteristics −
➢ Unambiguous −Each of its steps (or phases), and their inputs/outputs
should be clear and must lead to only one meaning.
➢ Input − An algorithm should have 0 or more well-defined inputs.
➢ Output − An algorithm should have 1 or more well-defined outputs,
and should match the desired output.
➢ Finiteness − Algorithms must terminate after a finite number of steps.
➢ Feasibility − Should be feasible with the available resources.
➢ Independent − An algorithm should have step-by-step directions,
which should be independent of any programming code.
➢ It should be efficient both in terms of memory and time.
From the data structure point of view, following are
some important categories of algorithms.
i. Search − Algorithm to search an item in a data
structure.
ii. Sort − Algorithm to sort items in a certain order.
iii. Insert − Algorithm to insert item in a data structure.
iv. Update − Algorithm to update an existing item in a
data structure.
v. Delete − Algorithm to delete an existing item from a
data structure.
Ways of writing an algorithm
There are various ways of writing an algorithm. Today, I'm going to explain 3 ways
of writing an algorithm.
English-Like Algorithm: An algorithm can be written in many ways. It can be
written in simple English but this method also has some demerits. Natural language
can be ambiguous and therefore lack the characteristic of being definite. Each step
of an algorithm should be clear and shouldn't have more than one meaning.
English language-like algorithms are not considered good for most of the tasks.
Flowchart
Flowchart Conventions
Flowcharts
Flowcharts pictorially depict a
process. They are easy to
understand and are commonly
used in the case of simple
problems.
Pseudocode
Pseudocode
The pseudocode has an
advantage of being easily
converted into any
programming language.
This way of writing algorithm is
most acceptable and most
widely used. In order to write a
pseudocode, one must be
familiar with the conventions
of writing it.
Importance of algorithms
i. It helps in enhancing the thinking process. They are like brain
stimulants that will give a boost to our thinking process.
ii. It helps in solving many problems in computer science,
computational biology, and economics.
iii. Without the knowledge of algorithms we can become a coder but
not a programmer.
iv. A good understanding of algorithms will help us to get a job. There is
an immense demand of good programmers in the software industry
who can analyse the problem well.
v. Genetic algorithms and randomized approach will help us to retain
that job in the changing market.
array
Linked list
queue
tree stack
Chapter Two: Arrays
An array is a collection of data
items stored at contiguous
memory locations.
The idea is to store multiple
items of the same type
together.
This makes it easier to
calculate the position of each
element by simply adding an
offset to a base value, i.e., the
memory location of the first
element of the array (generally
denoted by the name of the
array).
Characteristics of an Array
An array has various characteristics which are as follows:
• Arrays use an index-based data structure which helps to identify each of the
elements in an array easily using the index.
• If a user wants to store multiple values of the same data type, then the array
can be utilized efficiently.
• An array can also handle complex data structures by storing data in a two-
dimensional array.
• An array is also used to implement other data structures like Stacks, Queues,
Heaps, Hash tables, etc.
• The search process in an array can be done very easily.
Operations performed on array:
• Initialization: An array can be initialized with values at the time of declaration or later using an assignment
statement.
• Accessing elements: Elements in an array can be accessed by their index, which starts from 0 and goes up to
the size of the array minus one.
• Searching for elements: Arrays can be searched for a specific element using linear search or binary search
algorithms.
• Sorting elements: Elements in an array can be sorted in ascending or descending order using algorithms
like bubble sort, insertion sort, or quick sort.
• Inserting elements: Elements can be inserted into an array at a specific location, but this operation can be
time-consuming because it requires shifting existing elements in the array.
• Deleting elements: Elements can be deleted from an array by shifting the elements that come after it to fill
the gap.
• Updating elements: Elements in an array can be updated or modified by assigning a new value to a specific
index.
• Traversing elements: The elements in an array can be traversed in order, visiting each element once.
Applications of Array:
34
Characteristics of a Linked list:
35
Operations performed on Linked list:
A linked list is a linear data structure where each node contains a value and a reference to the next node.
Here are some common operations performed on linked lists:
• Initialization: A linked list can be initialized by creating a head node with a reference to the first node.
Each subsequent node contains a value and a reference to the next node.
• Inserting elements: Elements can be inserted at the head, tail, or at a specific position in the linked list.
• Deleting elements: Elements can be deleted from the linked list by updating the reference of the
previous node to point to the next node, effectively removing the current node from the list.
• Searching for elements: Linked lists can be searched for a specific element by starting from the head
node and following the references to the next nodes until the desired element is found.
• Updating elements: Elements in a linked list can be updated by modifying the value of a specific node.
• Traversing elements: The elements in a linked list can be traversed by starting from the head node and
following the references to the next nodes until the end of the list is reached.
• Reversing a linked list: The linked list can be reversed by updating the references of each node so that
they point to the previous node instead of the next node.
36
Applications of the Linked list:
Different applications of linked lists are as follows:
• It is used in the representation of Polynomial Manipulation where each polynomial term represents a
node in the linked list.
• Linked lists are used to display image containers. Users can visit past, current, and next images.
• Linked are used in software development where they indicate the correct syntax of a tag.
37
Real-Life Applications of a Linked list:
38
Linked list implementation
In this operation, we are adding an element at
the beginning of the list:
Algorithm
1. START
2. Create a node to store the data
3. Check if the list is empty
4. If the list is empty, add the data to the node and
assign the head pointer to it.
5. If the list is not empty, add the data to a node and link
to the current head. Assign the head to the newly
added node.
6. END
39
C++ Implementation code
#include <bits/stdc++.h> //insertion at the beginning
#include <string> void insertatbegin(int data){
using namespace std;
struct node { //create a link
int data; struct node *lk = (struct node*) malloc(sizeof(struct
struct node *next; node));
}; lk->data = data;
struct node *head = NULL;
struct node *current = NULL; // point it to old first node
lk->next = head;
// display the list
void printList(){ //point first to new first node
struct node *p = head; head = lk;
cout << "\n["; }
int main(){
//start from the beginning insertatbegin(12);
while(p != NULL) { insertatbegin(22);
cout << " " << p->data << " "; insertatbegin(30);
p = p->next; insertatbegin(44);
} insertatbegin(50);
cout << "]"; cout << "Linked List: ";
}
// print list
printList();
}
40
Output
Linked List:
[ 50 44 30 22 12 ]
41
Chapter Four: Stack
Stack:
Stack is a linear data structure which follows a particular order in which the operations
are performed. The order may be LIFO(Last In First Out) or FILO(First In Last Out). In stack,
all insertion and deletion are permitted at only one end of the list.
Stack Operations:
• push(): When this operation is performed, an element is inserted into the stack.
• pop(): When this operation is performed, an element is removed from the top of
the stack and is returned.
• top(): This operation will return the last inserted element that is at the top
without removing it.
• size(): This operation will return the size of the stack i.e. the total number of
elements present in the stack.
• isEmpty(): This operation indicates whether the stack is empty or not.
43
Characteristics of a Stack:
44
Applications of Stack:
45
Real-Life Applications of Stack:
• Real life example of a stack is the layer of eating plates arranged one above the
other. When you remove a plate from the pile, you can take the plate to the top
of the pile. But this is exactly the plate that was added most recently to the pile. If
you want the plate at the bottom of the pile, you must remove all the plates on
top of it to reach it.
• Browsers use stack data structures to keep track of previously visited sites.
• Call log in mobile also uses stack data structure.
46
Stack Insertion: push()
The push() is an operation that inserts elements into the stack. The following is an
algorithm that describes the push() operation in a simpler way.
Algorithm
1. Checks if the stack is full.
2. If the stack is full, produces an error and exit.
3. If the stack is not full, increments top to point next
empty space.
4. Adds data element to the stack location, where top
is pointing.
5. Returns success.
47
Implementation in C
#include <iostream>
int MAXSIZE = 8;
int stack[8]; /* Main function */
int top = -1; int main(){
int i;
/* Check if the stack is full*/ push(44);
int isfull(){ push(10);
if(top == MAXSIZE) push(62);
return 1; push(123);
else push(15);
return 0; printf("Stack Elements: \n");
}
// print stack data
/* Function to insert into the stack */ for(i = 0; i < 8; i++) {
int push(int data){ printf("%d ", stack[i]);
if(!isfull()) { }
top = top + 1; return 0;
stack[top] = data; }
} else { Output
printf("Could not insert data, Stack is full.\n"); Stack Elements:
} 44 10 62 123 15 0 0 0
return data;
}
48
Chapter Five: Queues
Queue:
Like Stack, Queue is a linear structure which follows a particular order in which the
operations are performed. The order is First In First Out (FIFO). In the queue, items are
inserted at one end and deleted from the other end. A good example of the queue is any
queue of consumers for a resource where the consumer that came first is served first.
The difference between stacks and queues is in removing. In a stack we remove the item
the most recently added; in a queue, we remove the item the least recently added.
49
Queue Operations:
50
Characteristics of a Queue:
51
Applications of Queue:
52
Real-Life Applications of Queue:
53
Queue Insertion Operation: Enqueue()
54
Implementation Example
#include <iostream> void insert(int data){
#include <string> if(!isFull()) {
#define MAX 6 if(rear == MAX-1) {
int intArray[MAX]; rear = -1;
int front = 0; }
int rear = -1; intArray[++rear] = data;
int itemCount = 0; itemCount++;
bool isFull(){ }
return itemCount == MAX; }
} int main(){
bool isEmpty(){ insert(3);
return itemCount == 0; insert(5);
} insert(9);
int removeData(){ insert(1);
int data = intArray[front++]; insert(12);
if(front == MAX) { insert(15);
front = 0; printf("Queue: ");
} while(!isEmpty()) {
itemCount--; int n = removeData();
return data; printf("%d ",n);
} }
}
Output
Queue: 3 5 9 1 12 15
55
Queue - The peek() Operation
57
Important Terms
Following are the important terms with respect to tree.
• Path − Path refers to the sequence of nodes along the edges of a tree.
• Root − The node at the top of the tree is called root. There is only one root per tree and one
path from the root node to any node.
• Parent − Any node except the root node has one edge upward to a node called parent.
• Child − The node below a given node connected by its edge downward is called its child
node.
• Leaf − The node which does not have any child node is called the leaf node.
• Subtree − Subtree represents the descendants of a node.
• Visiting − Visiting refers to checking the value of a node when control is on the node.
• Traversing − Traversing means passing through nodes in a specific order.
• Levels − Level of a node represents the generation of a node. If the root node is at level 0,
then its next child node is at level 1, its grandchild is at level 2, and so on.
• Keys − Key represents a value of a node based on which a search operation is to be carried
out for a node.
58
Characteristics of a Tree:
59
Types of Trees
General Trees
➢ General trees are unordered tree data
structures where the root node has
minimum 0 or maximum ‘n’ subtrees.
➢ The General trees have no constraint
placed on their hierarchy. The root
node thus acts like the superset of all
the other subtrees.
60
Binary Trees
Binary Tree:
Unlike Arrays, Linked Lists, Stack and queues, which are linear data structures, trees are
hierarchical data structures. A binary tree is a tree data structure in which each node has
at most two children, which are referred to as the left child and the right child. It is
implemented mainly using Links.
A Binary Tree is represented by a pointer to the topmost node in the tree. If the tree is
empty, then the value of root is NULL. A Binary Tree node contains the following parts.
1. Data
2. Pointer to left child
3. Pointer to the right child
61
Binary Search Tree:
A Binary Search Tree is a Binary Tree following the additional properties:
• The left part of the root node contains keys less than the root node key.
• The right part of the root node contains keys greater than the root node key.
• There is no duplicate key present in the binary tree.
A Binary tree having the following properties is known as Binary search tree (BST).
62
Operation performed on tree:
A tree is a non-linear data structure that consists of nodes connected by edges. Here
are some common operations performed on trees:
• Insertion: New nodes can be added to the tree to create a new branch or to increase
the height of the tree.
• Deletion: Nodes can be removed from the tree by updating the references of the
parent node to remove the reference to the current node.
• Search: Elements can be searched for in a tree by starting from the root node and
traversing the tree based on the value of the current node until the desired node is
found.
• Traversal: The elements in a tree can be traversed in several different ways, including
in-order, pre-order, and post-order traversal.
• Height: The height of the tree can be determined by counting the number of edges
from the root node to the furthest leaf node.
• Depth: The depth of a node can be determined by counting the number of edges
from the root node to the current node.
• Balancing: The tree can be balanced to ensure that the height of the tree is
minimized and
63
the distribution of nodes is as even as possible.
Applications of Tree:
64
Real-Life Applications of Tree:
65
Search Operation
66
Search Implementation example in C++
#include <iostream> else {
using namespace std; current = root;
struct Node { parent = NULL;
int data; while(1) {
struct Node *leftChild, *rightChild; parent = current;
};
Node *root = NULL; //go to left of the tree
Node *newNode(int item){ if(data < parent->data) {
Node *temp = (Node *)malloc(sizeof(Node)); current = current->leftChild;
temp->data = item;
temp->leftChild = temp->rightChild = NULL; //insert to the left
return temp; if(current == NULL) {
} parent->leftChild = tempNode;
void insert(int data){ return;
Node *tempNode = (Node*) malloc(sizeof(Node)); }
Node *current; }//go to right of the tree
Node *parent; else {
tempNode->data = data; current = current->rightChild;
tempNode->leftChild = NULL;
tempNode->rightChild = NULL; //insert to the right
if(current == NULL) {
//if tree is empty parent->rightChild = tempNode;
if(root == NULL) { return;
root = tempNode; }
} }
}
}
}
67
Search example continuation
Node* search(int data){ int main(){
Node *current = root; insert(55);
while(current->data != data) { insert(20);
//go to left tree insert(90);
if(current->data > data) { insert(50);
current = current->leftChild; insert(35);
}//else go to right tree insert(15);
else { insert(65);
current = current->rightChild; cout<<"Insertion done";
} cout<<"\nBST: "<<endl;
printTree(root);
//not found struct node* k;
if(current == NULL) { int ele = 35;
return NULL; cout<<"\nElement to be searched: "<<ele;
} Node* result = search(35);
} if(k != NULL)
return current; cout<<"\nElement "<<result->data<<" found ";
} else
void printTree(Node* Node) { cout<<"\nElement not found";
if (Node == nullptr) return 0;
return; }
printTree(Node->leftChild); Output
cout << " --" << Node->data; Insertion done
printTree(Node->rightChild); BST:
} --15 --20 --35 --50 --55 --65 --90
Element to be searched: 35
68 Element 35 found
Chapter Seven: Graphs
A graph is a non-linear data structure that consists of vertices (or nodes) and
edges. It consists of a finite set of vertices and set of edges that connect a pair of
nodes. The graph is used to solve the most challenging and complex programming
problems. It has different terminologies which are Path, Degree, Adjacent vertices,
Connected components, etc.
69
Graphs
Formally, a graph is a pair of sets (V, In the above graph,
E), where V is the set of vertices
V = {a, b, c, d, e}
and E is the set of edges, connecting
the pairs of vertices. Take a look at E = {ab, ac, bd, cd, de}
the following graph − Vertex − Each node of the graph is
represented as a vertex.
Edge − Edge represents a path between
two vertices or a line between two vertices.
Adjacency − Two node or vertices are
adjacent if they are connected to each
other through an edge. In the following
example, B is adjacent to A, C is adjacent to
B, and so on.
Path − Path represents a sequence of
edges between the two vertices. In the
following example, ABCD represents a path
from A to D.
70
Representation of Graphs
71
Shortest Path Algorithm implementation
#include <bits/stdc++.h> for (i = 0; i < x; i++) {
using namespace std; int start = edges[i].start;
#define V 5 int end = edges[i].end;
struct vertex *v = (struct vertex *) malloc (sizeof
// Maximum number of vertices in the graph (struct vertex));
struct graph { v->end = end;
v->next = graph->point[start];
// declaring graph data structure graph->point[start] = v;
struct vertex *point[V]; }
}; return graph;
struct vertex { }
int main (){
// declaring vertices struct Edge edges[] = { {0, 1}, {0, 2}, {0, 3}, {1, 2},
int end; {1, 4}, {2, 4}, {2, 3}, {3, 1} };
struct vertex *next; int n = sizeof (edges) / sizeof (edges[0]);
}; struct graph *graph = create_graph (edges, n);
struct Edge { int i;
cout<<"The graph created is: ";
// declaring edges for (i = 0; i < V; i++) {
int end, start; struct vertex *ptr = graph->point[i];
}; while (ptr != NULL) {
struct graph *create_graph (struct Edge edges[], int x){ cout << "(" << i << " -> " << ptr->end << ")\t";
int i; ptr = ptr->next;
struct graph *graph = (struct graph *) malloc (sizeof }
(struct graph)); cout << endl;
for (i = 0; i < V; i++) { }
graph->point[i] return 0;
72 = NULL;
Output
The graph created is:
(1 -> 3) (1 -> 0)
(2 -> 1) (2 -> 0)
(3 -> 2) (3 -> 0)
(4 -> 2) (4 -> 1)
73
Characteristics of Graph:
74
Applications of Graph:
75
Operation performed on Graph:
A graph is a non-linear data structure consisting of nodes and edges. Here are some
common operations performed on graphs:
• Add Vertex: New vertices can be added to the graph to represent a new node.
• Add Edge: Edges can be added between vertices to represent a relationship
between nodes.
• Remove Vertex: Vertices can be removed from the graph by updating the
references of adjacent vertices to remove the reference to the current vertex.
• Remove Edge: Edges can be removed by updating the references of the adjacent
vertices to remove the reference to the current edge.
• Depth-First Search (DFS): A graph can be traversed using a depth-first search by
visiting the vertices in a depth-first manner.
• Breadth-First Search (BFS): A graph can be traversed using a breadth-first search
by visiting the vertices in a breadth-first manner.
76
Operation performed on Graph:
• Shortest Path: The shortest path between two vertices can be determined using
algorithms such as Dijkstra’s algorithm or A* algorithm.
• Connected Components: The connected components of a graph can be
determined by finding sets of vertices that are connected to each other but not to
any other vertices in the graph.
• Cycle Detection: Cycles in a graph can be detected by checking for back edges
during a depth-first search.
77
Real-Life Applications of Graph:
• One of the most common real-world examples of a graph is Google Maps where
cities are located as vertices and paths connecting those vertices are located as
edges of the graph.
• A social network is also one real-world example of a graph where every person on
the network is a node, and all of their friendships on the network are the edges of
the graph.
• A graph is also used to study molecules in physics and chemistry.
78
Chapter Eight: Heap
Heap:
A Heap is a special Tree-based data structure in which the tree is a complete binary tree.
Generally, Heaps can be of two types:
• Max-Heap: In a Max-Heap the key present at the root node must be greatest among the
keys present at all of its children. The same property must be recursively true for all sub-
trees in that Binary Tree.
• Min-Heap: In a Min-Heap the key present at the root node must be minimum among
the keys present at all of its children. The same property must be recursively true for all
sub-trees in that Binary Tree.
79
Chapter Nine: Hashing Tag structures, Matrix and
Trie
Hashing Data Structure:
Hashing is an important Data Structure which is designed to use a special function called
the Hash function which is used to map a given value with a particular key for faster
access of elements. The efficiency of mapping depends on the efficiency of the hash
function used.
Let a hash function H(x) maps the value x at the index x%10 in an Array. For example, if
the list of values is [11, 12, 13, 14, 15] it will be stored at positions {1, 2, 3, 4, 5} in the
array or Hash table respectively.
80
Matrix:
81
Trie:
82
Advantages and Disadvantages of Data structures
Advantages of data structure:
2. Improved data organization and storage efficiency.
3. Faster data retrieval and manipulation.
4. Facilitates the design of algorithms for solving complex problems.
5. Eases the task of updating and maintaining the data.
6. Provides a better understanding of the relationships between data elements.
printf("%d ",value);
}
84
Example − a function that calls another function
which in turn calls it again.
int function1(int value1) {
if(value1 < 1)
return;
function2(value1 - 1);
printf("%d ",value1);
}
int function2(int value2) {
function1(value2);
}
85
Properties
86
Implementation
87
Implementation of recursions in C++
// CPP program for Recursion Data Structure
#include <iostream> Output
int factorial(int n) {
// Base case: factorial of 0 is 1 Number is: 6
if (n == 0)
return 1; Factorial of 6 is: 720
// Recursive case: multiply n with factorial of (n-1)
return n * factorial(n - 1);
}
int main() {
// case 1
int number = 6;
std::cout<<"Number is: "<<number<<"\n";
//case 2
if (number < 0) {
std::cout << "Error: Factorial is undefined for
negative numbers.\n";
return 1;
}
int result = factorial(number);
//print the output
std::cout << "Factorial of " << number << " is: " <<
result << std::endl;
return 0;
}
88