DS Notes
DS Notes
A data structure is a particular way of organising data in a computer so that it can be used
effectively. The idea is to reduce the space and time complexities of different tasks.
Time Complexity: It tells you how the runtime of an algorithm grows as the input size increases. It's
like estimating how long it will take to complete a task based on the amount of work needed. Time
complexity is about how fast an algorithm runs
Space Complexity: It tells you how much memory an algorithm uses relative to the input size. It's
like figuring out how much space you need to store all the necessary data while running the algorithm.
Space complexity is about how much memory it uses
Data structures provide an easy way of organising, retrieving, managing, and storing data.
Types:
1. Linear Data Structure
2. Non-Linear Data Structure.
- Linear data structures are arrangements of data elements where each element has a unique
predecessor and successor, except for the first and last elements.
- Elements in linear data structures are stored and accessed sequentially, with each element having a
specific position relative to the others.
- Examples of linear data structures include arrays, linked lists, stacks, queues.
- Non-linear data structures are arrangements of data elements where each element can have
multiple predecessors and successors, forming hierarchical or interconnected relationships.
- Elements in non-linear data structures are not stored and accessed sequentially but may be
connected in various ways, such as through parent-child relationships or arbitrary connections
between elements.
Self-Referential Structure
A self-referential structure, also known as a recursive structure, is a data structure in which an
instance of the structure contains a reference (or pointer) to another instance of the same type.
These structures often form recursive relationships, where instances of the structure are nested
within each other.
One common example of a self-referential structure is a linked list node.
In a singly linked list, each node contains a data field and a reference to the next node in the
sequence.
The last node in the list typically has a reference to null, indicating the end of the list. Here's a
simple example in C:
C Program
struct Node {
int data;
struct Node *next;
};
In this example, the `Node` structure contains an integer `data` field and a pointer `next` field that
points to another `Node` structure. This self-referential property allows us to create a linked list by
connecting multiple nodes together.
Self-referential structures are also commonly used in tree structures, graphs, and other hierarchical
data representations. They provide a flexible way to model relationships between elements and enable
the construction of complex data structures with recursive properties.
- Usage:
- Commonly used in implementing linked lists, trees, graphs, and other recursive data structures.
Self Referential Structure with Single Link: These structures can have only one self-pointer as
their member. The following example will show us how to connect the objects of a self-referential
structure with the single link and access the corresponding data members. The connection formed is
shown in the following figure.
Self Referential Structure with Multiple Links: Self referential structures with multiple links can
have more than one self-pointers. Many complicated data structures can be easily constructed using
these structures. Such structures can easily connect to more than one nodes at a time. The following
example shows one such structure with more than one links.
Dynamic memory allocation in data structures involves allocating memory for data elements at
runtime and deallocating it when it's no longer needed. Here's a basic overview:
1. Allocation:
- Memory is allocated for data elements using functions like `malloc()` (in C) or `new` (in C++).
- For example, in C, `malloc()` is used to allocate memory for a certain number of elements in an
array or a node in a linked list.
2. Deallocation:
- Once data elements are no longer needed, memory is deallocated to free up resources.
- Functions like `free()` (in C) or `delete` (in C++) are used for deallocating memory.
- Failing to deallocate memory can lead to memory leaks, where memory is not released even after
it's no longer needed.
4. Methods:
- In C, memory allocation functions like `malloc()` and `free()` are commonly used.
- In C++, `new` and `delete` operators are often used for dynamic memory allocation and
deallocation.
Dynamic memory allocation allows data structures to adapt to changing requirements, such as varying
input sizes or the need to create or destroy elements dynamically. However, it's important to manage
memory carefully to avoid issues like memory leaks or fragmentation, which can degrade
performance and stability.
Linked List Implementation:
- Definition:
- A linked list is a linear data structure where elements, called nodes, are connected via pointers.
- Operations:
- Insertion: Adding elements at the beginning, end, or middle of the list.
- Deletion: Removing elements from the list.
- Traversal: Accessing each element sequentially.
- Variations:
- Single Linked List: Each node points to the next node.
- Double Linked List: Each node points to both the next and previous nodes.
- Circular Linked List: Last node points back to the first node, forming a circle.
- Usage:
- Efficient for dynamic data storage, especially when the size is not known beforehand or may
change frequently.
- At the end: The new node is appended at the end of the list, and the next pointer of the last node
points to it.
- At a specific position: Inserting a node at a particular index involves traversing the list until
- At the end: The last node is removed by traversing the list until reaching the second-to-last node
and updating its next pointer to null.
- At a specific position: Deleting a node at a particular index involves traversing the list until
reaching the desired position and updating pointers to bypass the node to be deleted.
3. Traversal: Iterating through the list from the head node to the last node to access or manipulate
each element.
4. Searching: Finding a specific element in the list by traversing through the nodes until the desired
value is found or the end of the list is reached.
Overall, singly linked lists are versatile data structures suitable for various applications, particularly
when dynamic memory allocation and efficient insertion and deletion operations are essential.
However, their performance characteristics should be carefully considered based on the specific
requirements of the problem at hand.
Here's a detailed breakdown of the components and operations associated with a doubly linked list:
Components:
1. Node: Each element in a doubly linked list is represented by a node. A node contains three parts:
- Data: The actual value or payload stored in the node.
- Next Pointer: A reference or pointer to the next node in the sequence.
- Previous Pointer: A reference or pointer to the previous node in the sequence.
Operations:
1. Insertion:
- At the beginning: The new node is added at the start of the list. The new node's next pointer
points to the current head, and the current head's previous pointer points to the new node. The head
pointer is updated to point to the new node.
- At the end: The new node is appended at the end of the list. The new node's previous pointer
points to the current tail, and the current tail's next pointer points to the new node. The tail pointer is
updated to point to the new node.
- At a specific position: Inserting a node at a particular index involves traversing the list until
reaching the desired position and adjusting pointers accordingly.
2. Deletion:
- At the beginning: The first node is removed. The head pointer is updated to point to the next
node, and the next node's previous pointer is updated to point to null.
- At the end: The last node is removed. The tail pointer is updated to point to the previous node, and
the previous node's next pointer is updated to point to null.
- At a specific position: Deleting a node at a particular index involves traversing the list until
reaching the desired position and adjusting pointers to bypass the node to be deleted.
3. Traversal: Iterating through the list from the head node to the tail node or from the tail node to the
head node to access or manipulate each element.
4. Searching: Finding a specific element in the list by traversing through the nodes until the desired
value is found or the end of the list is reached.
- Additional memory overhead: Each node in a doubly linked list requires additional memory for
storing both next and previous pointers, increasing the overall memory footprint compared to singly
linked lists.
- Increased complexity: Managing the previous pointers adds complexity to insertion, deletion, and
traversal operations compared to singly linked lists.
Overall, doubly linked lists are useful data structures when bidirectional traversal is required, such as
in applications involving reverse traversal or frequent insertions and deletions at both ends of the list.
However, their performance characteristics and memory usage should be carefully considered based
on the specific requirements of the problem at hand.
Circular Linked List:
A circular linked list is a variation of a linked list where the last node of the list points back to the first
node, forming a loop. This differs from a regular singly linked list, where the last node points to null.
Circular linked lists can be implemented with singly linked nodes or doubly linked nodes.
Here's a detailed breakdown of the components and operations associated with a circular linked list:
Components:
1. Node: Each element in a circular linked list is represented by a node. A node contains two parts:
- Data: The actual value or payload stored in the node.
- Next Pointer: A reference or pointer to the next node in the sequence.
Operations:
1. Insertion:
- At the beginning: The new node is added at the start of the list. The new node's next pointer
points to the current head, and the last node's next pointer is updated to point to the new node.
After Insertion
- At the end: The new node is appended at the end of the list. The new node's next pointer points to
the head node, and the previous last node's next pointer is updated to point to the new node. After
Insertion
- At a specific position: Inserting a node at a particular index involves traversing the list until
reaching the desired position and adjusting pointers accordingly.
After Insertion
2. Deletion:
- At the beginning: The first node is removed. The head pointer is updated to point to the next
node, and the last node's next pointer is updated to point to the new head.
- At the end: The last node is removed. The previous node's next pointer is updated to point to the
head node, and the head pointer is updated to point to the new last node.
- At a specific position: Deleting a node at a particular index involves traversing the list until
reaching the desired position and adjusting pointers to bypass the node to be deleted.
3. Traversal: Iterating through the list from the head node to the last node, or vice versa, to access or
manipulate each element.
4. Searching: Finding a specific element in the list by traversing through the nodes until the desired
value is found or until completing one loop of the circular list.
- No clear end: There is no clear end to the list, which can make it challenging to determine when to
stop traversing or performing operations.
- Additional complexity: The circular nature of the list adds complexity to some operations
compared to linear linked lists.
Overall, circular linked lists are suitable for scenarios where looping through the elements is a
common operation or where constant-time insertion and deletion at both ends are required.
A Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle. Stack has one
end. It contains only one pointer top pointer pointing to the topmost element of the stack. Whenever
an element is added in the stack, it is added on the top of the stack, and the element can be deleted
only from the stack. In other words, a stack can be defined as a container in which insertion and
deletion can be done from the one end known as the top of the stack.
Suppose we want to store the elements in a stack and let's assume that stack is empty. We have taken
the stack of size 5 as shown below in which we are pushing the elements one by one until the stack
becomes full.
o push(): When we insert an element in a stack then the operation is known as a push. If the
stack is full then the overflow condition occurs.
o pop(): When we delete an element from the stack, the operation is known as a pop. If the
stack is empty means that no element exists in the stack, this state is known as an underflow
state.
o isEmpty(): It determines whether the stack is empty or not.
o isFull(): It determines whether the stack is full or not.'
o peek(): It returns the element at the given position.
o count(): It returns the total number of elements available in a stack.
o change(): It changes the element at the given position.
o display(): It prints all the elements available in the stack.
PUSH operation
POP operation
o Before deleting the element from the stack, we check whether the stack is empty.
o If we try to delete the element from the empty stack, then the underflow condition occurs.
o If the stack is not empty, we first access the element which is pointed by the top
o Once the pop operation is performed, the top is decremented by 1, i.e., top=top-1.
In linked list implementation of stack, the nodes are maintained non-contiguously in the memory.
Each node contains a pointer to its immediate successor node in the stack. Stack is said to be
overflown if the space left in the memory heap is not enough to create a node.
Adding an element into the top of the stack is referred to as push operation. Push operation involves
following two steps.
1. Increment the variable Top so that it can now refere to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new element at
the top of the stack.
Deletion of an element from the top of the stack is called pop operation. The value of the variable top
will be incremented by 1 whenever an item is deleted from the stack. The top most element of the
stack is stored in an another variable and then the top is decremented by 1. the operation returns the
deleted value that was stored in another variable as the result.
Applications of Stack
o Balancing of symbols
o String reversal
o UNDO/REDO
o Recursion
o DFS(Depth First Search)
o Backtracking
o Expression conversion
o Memory management
QUEUE – LINEAR DATA STRUCTURE
A queue is a fundamental data structure in computer science that follows the First-In-First-Out (FIFO)
principle. In a queue, elements are added at the rear end (enqueue operation) and removed from the
front end (dequeue operation). This means that the element that is added first will be the first one to
be removed.
2. Dequeue: Removes and returns the element from the front end of the queue.
3. Peek (or Front): Returns the element at the front end of the queue without removing it.
Queues can be implemented using various data structures such as arrays, linked lists, or other dynamic
data structures. The choice of implementation depends on the specific requirements of the application
and the operations that need to be performed efficiently.
Types of Queue
There are four different types of queue that are listed as follows -
Circular Queue
o In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue
except that the last element of the queue is connected to the first element. It is also known as
Ring Buffer, as all the ends are connected to another end. The representation of circular queue
is shown in the below image -
o The drawback that occurs in a linear queue is overcome by using the circular queue. If the
empty space is available in a circular queue, the new element can be added in an empty space
by simply incrementing the value of rear.
Priority Queue
o It is a special type of queue in which the elements are arranged based on the priority. It is a
special type of queue data structure in which every element has a priority associated with it.
Suppose some elements occur with the same priority, they will be arranged according to the
FIFO principle. The representation of priority queue is shown in the below image -
Deque (or, Double Ended Queue)
o In Deque or Double Ended Queue, insertion and deletion can be done from both ends of the
queue either from the front or rear. It means that we can insert and delete elements from both
front and rear ends of the queue. Deque can be used as a palindrome checker means that if we
read the string from both ends, then the string would be the same.
o Deque can be used both as stack and queue as it allows the insertion and deletion operations
on both ends. Deque can be considered as stack because stack follows the LIFO (Last In First
Out) principle in which insertion and deletion both can be performed only from one end. And
in deque, it is possible to perform both insertion and deletion from one end, and Deque does
not follow the FIFO principle.
o
o Input restricted deque - As the name implies, in input restricted queue, insertion operation
can be performed at only one end, while deletion can be performed from both ends.
o Output restricted deque - As the name implies, in output restricted queue, deletion operation
can be performed at only one end, while insertion can be performed from both ends.
Operations performed on queue
The fundamental operations that can be performed on queue are listed as follows -
o Enqueue: The Enqueue operation is used to insert the element at the rear end of the queue. It
returns void.
o Dequeue: It performs the deletion from the front-end of the queue. It also returns the element
which has been removed from the front-end. It returns an integer value.
o Peek: This is the third operation that returns the element, which is pointed by the front pointer
in the queue but does not delete it.
o Queue overflow (isfull): It shows the overflow condition when the queue is completely full.
o Queue underflow (isempty): It shows the underflow condition when the Queue is empty,
i.e., no elements are in the Queue.
Array representation of Queue
We can easily represent queue by using linear arrays. There are two variables i.e. front and rear, that
are implemented in the case of every queue. Front and rear variables point to the position from where
insertions and deletions are performed in a queue. Initially, the value of front and queue is -1 which
represents an empty queue. Array representation of a queue containing 5 elements along with the
respective values of front and rear, is shown in the following figure.
The above figure shows the queue of characters forming the English word "HELLO". Since, No
deletion is performed in the queue till now, therefore the value of front remains -1 . However, the
value of rear increases by one every time an insertion is performed in the queue. After inserting an
element into the queue shown in the above figure, the queue will look something like following. The
value of rear will become 5 while the value of front remains same.
After deleting an element, the value of front will increase from -1 to 0. however, the queue will look
something like following.
Algorithm to insert any element in a queue
Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow error.
In a linked queue, each node of the queue consists of two parts i.e. data part and the link part. Each
element of the queue points to its immediate next element in the memory.
In the linked queue, there are two pointers maintained in the memory i.e. front pointer and rear
pointer. The front pointer contains the address of the starting element of the queue while the rear
pointer contains the address of the last element of the queue.
Insertion and deletions are performed at rear and front end respectively. If front and rear both are
NULL, it indicates that the queue is empty.
There are two basic operations which can be implemented on the linked queues. The operations are
Insertion and Deletion.
Insert operation
o Step 1: Allocate the space for the new node PTR
o Step 2: SET PTR -> DATA = VAL
o Step 3: IF FRONT = NULL
SET FRONT = REAR = PTR
SET FRONT -> NEXT = REAR -> NEXT = NULL
ELSE
SET REAR -> NEXT = PTR
SET REAR = PTR
SET REAR -> NEXT = NULL
[END OF IF]
o Step 4: END
Deletion Algorithm
o Step 1: IF FRONT = NULL
Write " Underflow "
Go to Step 5
[END OF IF]
o Step 2: SET PTR = FRONT
o Step 3: SET FRONT = FRONT -> NEXT
o Step 4: FREE PTR
o Step 5: END
o A tree data structure is defined as a collection of objects or entities known as nodes that are
linked together to represent or simulate hierarchy.
o A tree data structure is a non-linear data structure because it does not store in a sequential
manner. It is a hierarchical structure as elements in a Tree are arranged in multiple levels.
o In the Tree data structure, the topmost node is known as a root node. Each node contains
some data, and data can be of any type. In the above tree structure, the node contains the
name of the employee, so the type of data would be a string.
o Each node contains some data and the link or reference of other nodes that can be called
children
o Root: The root node is the topmost node in the tree hierarchy. In other words, the root node is
the one that doesn't have any parent. In the above structure, node numbered 1 is the root
node of the tree. If a node is directly linked to some other node, it would be called a parent-
child relationship.
o Child node: If the node is a descendant of any node, then the node is known as a child node.
o Parent: If the node contains any sub-node, then that node is said to be the parent of that sub-
node.
o Sibling: The nodes that have the same parent are known as siblings.
o Leaf Node:- The node of the tree, which doesn't have any child node, is called a leaf node. A
leaf node is the bottom-most node of the tree. There can be any number of leaf nodes present
in a general tree. Leaf nodes can also be called external nodes.
o Internal nodes: A node has atleast one child node known as an internal
o Ancestor node:- An ancestor of a node is any predecessor node on a path from the root to
that node. The root node doesn't have any ancestors. In the tree shown in the above image,
nodes 1, 2, and 5 are the ancestors of node 10.
o Descendant: The immediate successor of the given node is known as a descendant of a node.
In the above figure, 10 is the descendant of node 5.
1. Binary Tree: Binary trees are fundamental in computer science and serve as the basis for many
other tree data structures. They are used in a wide range of applications, including binary search trees,
expression trees, and Huffman trees for data compression.
2. Binary Search Tree (BST): BSTs are essential for efficient searching, insertion, and deletion
operations. They are widely used in databases, compilers, and other applications where sorted data
needs to be maintained and queried.
3. Balanced Tree: Balanced trees, such as AVL trees and Red-Black trees, ensure that the tree
remains balanced, preventing performance degradation in operations like insertion, deletion, and
searching. They are crucial for maintaining efficient performance in various applications.
4. Trie (Prefix Tree): Tries are fundamental in string processing and are commonly used in
applications like autocomplete, spell checkers, and IP routing tables.
5. Heap: Heaps are essential for priority queue implementations and are used in algorithms like heap
sort and Dijkstra's shortest path algorithm.
Binary Tree
The Binary tree means that the node can have maximum two children. Here, binary name itself
suggests that 'two'; therefore, each node can have either 0, 1 or 2 children.
The above tree is a binary tree because each node contains the utmost two children. The logical
representation of the above tree is given below:
In the above tree, node 1 contains two pointers, i.e., left and a right pointer pointing to the left and
right node respectively. The node 2 contains both the nodes (left and right node); therefore, it has two
pointers (left and right). The nodes 3, 5 and 6 are the leaf nodes, so all these nodes
contain NULL pointer on both left and right parts.
The full binary tree is also known as a strict binary tree. The tree can only be considered as the full
binary tree if each node must contain either 0 or 2 children. The full binary tree can also be defined as
the tree in which each node must contain 2 children except the leaf nodes.
The complete binary tree is a tree in which all the nodes are completely filled except the last level. In
the last level, all the nodes must be as left as possible. In a complete binary tree, the nodes should be
added from the left.
The above tree is a complete binary tree because all the nodes are completely filled, and all the nodes
in the last level are added at the left first.
A tree is a perfect binary tree if all the internal nodes have 2 children, and all the leaf nodes are at the
same level.
The below tree is not a perfect binary tree because all the leaf nodes are not at the same level.
Note: All the perfect binary trees are the complete binary trees as well as the full binary tree, but vice
versa is not true, i.e., all complete binary trees and full binary trees are the perfect binary trees.
The degenerate binary tree is a tree in which all the internal nodes have only one children.
The above tree is a degenerate binary tree because all the nodes have only one child. It is also known
as a right-skewed tree as all the nodes have a right child only.
The above tree is also a degenerate binary tree because all the nodes have only one child. It is also
known as a left-skewed tree as all the nodes have a left child only.
Balanced Binary Tree
The balanced binary tree is a tree in which both the left and right trees differ by atmost 1. For
example, AVL and Red-Black trees are balanced binary tree.
The above tree is a balanced binary tree because the difference between the left subtree and right
subtree is zero.
The above tree is not a balanced binary tree because the difference between the left subtree and the
right subtree is greater than 1.
Tree traversal refers to the process of visiting all the nodes of a tree in a specific order. In the case of
binary trees, there are three main traversal techniques: in-order traversal, pre-order traversal, and
post-order traversal. Here's how each traversal works:
1. In-Order Traversal:
3. Post-Order Traversal:
/\
2 3
/\
4 5
In-Order traversal of the above tree would result in the sequence: `4 -> 2 -> 5 -> 1 -> 3`.
Pre-Order traversal of the above tree would result in the sequence: `1 -> 2 -> 4 -> 5 -> 3`.
Post-Order traversal of the above tree would result in the sequence: `4 -> 5 -> 2 -> 3 -> 1`.
A binary search tree follows some order to arrange the elements. In a Binary search tree, the value of
left node must be smaller than the parent node, and the value of right node must be greater than the
parent node. This rule is applied recursively to the left and right subtrees of the root.
Similarly, we can see the left child of root node is greater than its left child and smaller than its right
child. So, it also satisfies the property of binary search tree. Therefore, we can say that the tree in the
above image is a binary search tree.
Suppose if we change the value of node 35 to 55 in the above tree, check whether the tree will be
binary search tree or not.
In the above tree, the value of root node is 40, which is greater than its left child 30 but smaller than
right child of 30, i.e., 55. So, the above tree does not satisfy the property of Binary search tree.
Therefore, the above tree is not a binary search tree.
Now, let's see the creation of binary search tree using an example.
Suppose the data elements are - 45, 15, 79, 90, 10, 55, 12, 20, 50
o First, we have to insert 45 into the tree as the root of the tree.
o Then, read the next element; if it is smaller than the root node, insert it as the root of the left
subtree, and move to the next element.
o Otherwise, if the element is larger than the root node, then insert it as the root of the right
subtree.
Now, let's see the process of creating the Binary search tree using the given data element. The process
of creating the BST is shown below -
As 15 is smaller than 45, so insert it as the root node of the left subtree.
As 79 is greater than 45, so insert it as the root node of the right subtree.
As 79 is greater than 45, so insert it as the root node of the right subtree.
Step 4 - Insert 90.
90 is greater than 45 and 79, so it will be inserted as the right subtree of 79.
55 is larger than 45 and smaller than 79, so it will be inserted as the left subtree of 79.
Step 7 - Insert 12.
12 is smaller than 45 and 15 but greater than 10, so it will be inserted as the right subtree of 10.
20 is smaller than 45 but greater than 15, so it will be inserted as the right subtree of 15.
50 is greater than 45 but smaller than 79 and 55. So, it will be inserted as a left subtree of 55.
Now, the creation of binary search tree is completed. After that, let's move towards the operations that
can be performed on Binary search tree.
Insert, delete and search operations can be performed on the binary search tree.
A heap is a complete binary tree, and the binary tree is a tree in which the node can have utmost two
children. Before knowing more about the heap data structure, we should know about the complete
binary tree.
Complete binary tree is a binary tree in which all the levels except the last level, i.e., leaf node
should be completely filled, and all the nodes should be left-justified.
In the above figure, we can observe that all the internal nodes are completely filled except the leaf
node; therefore, we can say that the above tree is a complete binary tree.
o Min Heap
o Max heap
Min Heap: The value of the parent node should be less than or equal to either of its children.
Or
In other words, the min-heap can be defined as, for every node i, the value of node i is greater than or
equal to its parent value except the root node. Mathematically, it can be defined as:
In the above figure, 11 is the root node, and the value of the root node is less than the value of all the
other nodes (left child or a right child).
Max Heap: The value of the parent node is greater than or equal to its children.
Or
In other words, the max heap can be defined as for every node i; the value of node i is less than or
equal to its parent value except the root node. Mathematically, it can be defined as:
The above tree is a max heap tree as it satisfies the property of the max heap.
In the above figure, we can observe that the tree satisfies the property of max heap; therefore, it is a
heap tree.
The term "height-balanced tree" typically refers to a tree data structure where the heights of the left
and right subtrees of any node differ by at most one. This property ensures that the tree remains
balanced, which can lead to more efficient operations such as insertion, deletion, and searching.
One of the most common types of height-balanced trees is the **AVL tree**. AVL trees are self-
balancing binary search trees named after their inventors, Adelson-Velsky and Landis. In an AVL
tree, the balance factor of every node, defined as the difference in heights between its left and right
subtrees, is either -1, 0, or 1. If the balance factor of any node violates this property, the tree is
rebalanced using rotations.
1. Balancing Condition: For every node in the tree, the heights of the left and right subtrees can
differ by at most one.
3. Efficiency: AVL trees maintain a height-balanced structure, ensuring that the height of the tree
remains O(log n), where n is the number of nodes in the tree. This leads to efficient search, insert, and
delete operations with worst-case time complexity of O(log n).
4. Operations: AVL trees support standard binary search tree operations such as insertion, deletion,
and search, with the added benefit of automatic self-balancing to maintain height balance.
Here's a simple illustration of an AVL tree:
30
/ \
20 40
/ \ \
10 25 50
- The heights of the left and right subtrees of each node differ by at most one.
- The balance factors of all nodes are within the range [-1, 1].
- The tree remains balanced after insertion or deletion operations, ensuring efficient performance.
AVL trees are widely used in practice, especially in scenarios where maintaining balanced trees is
essential for performance guarantees, such as database indexing, compiler implementations, and other
applications requiring efficient search, insert, and delete operations.
AVL Tree
AVL Tree is invented by GM Adelson - Velsky and EM Landis in 1962. The tree is named AVL in
honour of its inventors.
AVL Tree can be defined as height balanced binary search tree in which each node is associated with
a balance factor which is calculated by subtracting the height of its right sub-tree from that of its left
sub-tree.
Tree is said to be balanced if balance factor of each node is in between -1 to 1, otherwise, the tree will
be unbalanced and need to be balanced.
If balance factor of any node is 1, it means that the left sub-tree is one level higher than the right sub-
tree.
If balance factor of any node is 0, it means that the left sub-tree and right sub-tree contain equal
height.
If balance factor of any node is -1, it means that the left sub-tree is one level lower than the right sub-
tree.
An AVL tree is given in the following figure. We can see that, balance factor associated with each
node is in between -1 and +1. therefore, it is an example of AVL tree.
SN Operation Description
1 Insertion Insertion in AVL tree is performed in the same way as it is performed in a binary search
tree. However, it may lead to violation in the AVL tree property and therefore the tree
may need balancing. The tree can be balanced by applying rotations.
2 Deletion Deletion can also be performed in the same way as it is performed in a binary search
tree. Deletion may also disturb the balance of the tree therefore, various types of
rotations are used to rebalance the tree.
AVL Rotations
We perform rotation in AVL tree only in case if Balance Factor is other than -1, 0, and 1. There are
basically four types of rotations which are as follows:
Where node A is the node whose balance Factor is other than -1, 0, 1.
The first two rotations LL and RR are single rotations and the next two rotations LR and RL are
double rotations. For a tree to be unbalanced, minimum height must be at least 2, Let us understand
each rotation
1. RR Rotation
When BST becomes unbalanced, due to a node is inserted into the right subtree of the right subtree of
A, then we perform RR rotation, RR rotation is an anticlockwise rotation, which is applied on the
edge below a node having balance factor -2
In above example, node A has balance factor -2 because a node C is inserted in the right subtree of A
right subtree. We perform the RR rotation on the edge below A.
2. LL Rotation
When BST becomes unbalanced, due to a node is inserted into the left subtree of the left subtree of C,
then we perform LL rotation, LL rotation is clockwise rotation, which is applied on the edge below a
node having balance factor 2.
In above example, node C has balance factor 2 because a node A is inserted in the left subtree of C
left subtree. We perform the LL rotation on the edge below A.
3. LR Rotation
Double rotations are bit tougher than single rotation which has already explained above. LR rotation =
RR rotation + LL rotation, i.e., first RR rotation is performed on subtree and then LL rotation is
performed on full tree, by full tree we mean the first node from the path of inserted node whose
balance factor is other than -1, 0, or 1.
State Action
A node B has been inserted into the right subtree of A the left subtree of C, because of
which C has become an unbalanced node having balance factor 2. This case is L R
rotation where: Inserted node is in the right subtree of left subtree of C
As LR rotation = RR + LL rotation, hence RR (anticlockwise) on subtree rooted at A
is performed first. By doing RR rotation, node A, has become the left subtree of B.
After performing RR rotation, node C is still unbalanced, i.e., having balance factor 2,
as inserted node A is in the left of left of C
Now we perform LL clockwise rotation on full tree, i.e. on node C. node C has now
become the right subtree of node B, A is left subtree of B
Balance factor of each node is now either -1, 0, or 1, i.e. BST is balanced now.
4. RL Rotation
As already discussed, that double rotations are bit tougher than single rotation which has already
explained above. R L rotation = LL rotation + RR rotation, i.e., first LL rotation is performed on
subtree and then RR rotation is performed on full tree, by full tree we mean the first node from the
path of inserted node whose balance factor is other than -1, 0, or 1.
State Action
A node B has been inserted into the left subtree of C the right subtree of A, because
of which A has become an unbalanced node having balance factor - 2. This case is RL
rotation where: Inserted node is in the left subtree of right subtree of A
After performing LL rotation, node A is still unbalanced, i.e. having balance factor -2,
which is because of the right-subtree of the right-subtree node A.
Balance factor of each node is now either -1, 0, or 1, i.e., BST is balanced now.
PART A
PART B
1.5 a. Explain with a diagram how dynamic memory allocation works and discuss its advantages.
b. Illustrate the process of inserting and deleting nodes ina double linked list with examples
1.6 a. Describe the implementation of a circular linked list and explain its advantages over a singly
linked list with examples.
b. Explain the concept of self-referential structures and demonstrate with code how they are used
in linked list implementations.
PART A
PART B
2.5 a. Discuss the method to evaluate the postfix express 9 3 / 2 * 7 + Provide a step-by-step
breakdown of the operations, including the stack changes and the intermediate results after each
operations
b. Explain with a diagram the array implementation of a stack
PART A
3.1 What are the methods to traverse a binary tree?
3.2 Define a binary search tree and its unique property
PART B
3.3 a. Discuss tree traversals with examples
b. Discuss the concept of binary search trees and illustrate with a diagram how insertion and
deletion operations are performed.