Module 3
Module 3
Module 3
Self-Referential structures are those structures that have one or more pointers which point to
the same type of structure, as their member.
In other words, structures pointing to the same type of structures are self-referential in nature.
struct node {
int data1;
char data2;
struct node* link;
};
int main()
{
struct node ob;
return 0;
}
In the above example ‘link’ is a pointer to a structure of type ‘node’. Hence, the structure
‘node’ is a self-referential structure with ‘link’ as the referencing pointer.
An important point to consider is that the pointer should be initialized properly before
accessing, as by default it contains garbage value. (Link is pointer member that points to a
structure of the same types as the one being declared. Is referred to as a link. Link can tie
one node to another node.)
#include <stdio.h>
struct node {
int data1;
char data2;
struct node* link;
};
int main()
{
struct node ob1; // Node1
// Initialization
ob1.link = NULL;
ob1.data1 = 10;
ob1.data2 = 20;
// Initialization
ob2.link = NULL;
ob2.data1 = 30;
ob2.data2 = 40;
Output
30
40
Self-Referential Structure with Multiple Links: Self-referential structures with multiple
links can have more than one self-pointer. Many complicated data structures can be easily
constructed using these structures. Such structures can easily connect to more than one node
at a time. The following example shows one such structure with more than one links.
The connections made in the above example can be understood using the following figure.
#include <stdio.h>
struct node {
int data;
struct node* prev_link;
struct node* next_link;
};
int main()
{
struct node ob1; // Node1
// Initialization
ob1.prev_link = NULL;
ob1.next_link = NULL;
ob1.data = 10;
// Initialization
ob2.prev_link = NULL;
ob2.next_link = NULL;
ob2.data = 20;
// Initialization
ob3.prev_link = NULL;
ob3.next_link = NULL;
ob3.data = 30;
// Forward links
ob1.next_link = &ob2;
ob2.next_link = &ob3;
// Backward links
ob2.prev_link = &ob1;
ob3.prev_link = &ob2;
Output
10 20 30
10 20 30
10 20 30
In the above example we can see that ‘ob1’, ‘ob2’ and ‘ob3’ are three objects of the self-
referential structure ‘node’. And they are connected using their links in such a way that any
of them can easily access each other’s data. This is the beauty of the self-referential
structures. The connections can be manipulated according to the requirements of the
programmer.
Applications: Self-referential structures are very useful in creation of other complex data
structures like:
• Linked Lists
• Stacks
• Queues
• Trees
• Graphs etc.
Dynamic memory allocation in Data Structure
In most programming languages, including C++, memory can be classified into two categories:
stack memory and heap memory. Local variables and function calls are stored in the stack
memory, whereas the more adaptable heap memory can be allocated and released at runtime.
The process of allocating and releasing memory from the heap is known as dynamic memory
allocation. It allows the programmer to manage memory explicitly, providing the ability to
create data structures of varying sizes and adjust memory requirements dynamically.
Why would we need to allocate memory while the program is executing? Because, even
though it isn’t blatantly visible, not being able to allocate memory during run time precludes
flexibility and compromises with space efficiency. Specially, those cases where the input
isn’t known beforehand, we suffer in terms of inefficient storage use and lack or excess of
slots to enter data (given an array or similar data structures to store entries). So, here we
define Dynamic Memory Allocation: The mechanism by which storage/memory/cells can
be allocated to variables during the run time is called Dynamic Memory Allocation.
Dynamic Memory Allocation Techniques:
1. malloc:
The malloc (memory allocation) function is used to allocate a specified number of bytes in
memory. When memory is allocated successfully, it returns a pointer to that block, otherwise
it returns NULL. By dividing the necessary number of elements by each one's individual size,
the block's size is calculated. For example:
2. calloc:
Resizing a previously allocated memory block is possible with the realloc (reallocate) function.
It accepts two arguments: the new size in bytes and a pointer to the current block. Upon
successful reallocation, a pointer to the newly allocated memory is returned. If not, NULL is
returned. Here's an example:
The realloc (reallocate) function allows you to resize a previously allocated memory block. It
takes a pointer to the existing block and the new size in bytes as arguments. If reallocation is
successful, it returns a pointer to the reallocated memory. Otherwise, it returns NULL. Example
usage:
1. malloc()
2. calloc()
3. realloc()
4. free()
Before learning above functions, let's understand the difference between static memory
allocation and dynamic memory allocation.
Memory can't be increased while executing Memory can be increased while executing
program. program.
Linked List
A linked list is a fundamental data structure in computer science. It mainly allows efficient
insertion and deletion operations compared to arrays. Like arrays, it is also used to implement
other data structures like stack, queue and deque.
Linked List is a linear data structure, in which elements are not stored at a contiguous
location, rather they are linked using pointers. Linked List forms a series of connected
nodes, where each node stores the data and the address of the next node.
Head and Tail: The linked list is accessed through the head node, which points to the first
node in the list. The last node in the list points to NULL or nullptr, indicating the end of the
list. This node is known as the tail node.
Singly linked list is a linear data structure in which the elements are not stored in contiguous
memory locations and each element is connected only to its next element using a pointer.
A singly linked list is a fundamental data structure in computer science and programming, it
consists of nodes where each node contains a data field and a reference to the next node in
the node. The last node points to null, indicating the end of the list. This linear structure
supports efficient insertion and deletion operations, making it widely used in various
applications. In this tutorial, we’ll explore the node structure, understand the operations on
singly linked lists (traversal, searching, length determination, insertion, and deletion), and
provide detailed explanations and code examples to implement these operations effectively.
A singly linked list is a fundamental data structure in computer science and programming. It
is a collection of nodes where each node contains a data field and a reference (link) to the
next node in the sequence. The last node in the list points to null, indicating the end of the
list. This linear data structure allows for efficient insertion and deletion operations, making
it a popular choice for various applications.
In a singly linked list, each node consists of two parts: data and a pointer to the next node.
The data part stores the actual information, while the pointer (or reference) part stores the
address of the next node in the sequence. This structure allows nodes to be dynamically linked
together, forming a chain-like sequence.
In this representation, each box represents a node, with an arrow indicating the link to the
next node. The last node points to NULL, indicating the end of the list.
In most programming languages, a node in a singly linked list is typically defined using a
class or a struct.
Node Creation
struct node
{
int data;
struct node *next;
};
struct node *head, *ptr;
ptr = (struct node *)malloc(sizeof(struct node *));
Insertion
The insertion into a singly linked list can be performed at different positions. Based on the
position of the new node being inserted, the insertion is categorized into the following
categories.
SN Operation Description
1 Insertion at It involves inserting any element at the front of the list. We just need to
beginning a few link adjustments to make the new node as the head of the list.
2 Insertion at end It involves insertion at the last of the linked list. The new node can be
of the list inserted as the only node in the list or it can be inserted as the last one.
Different logics are implemented in each scenario.
3 Insertion after It involves insertion after the specified node of the linked list. We need
specified node to skip the desired number of nodes in order to reach the node after
which the new node will be inserted. .
The Deletion of a node from a singly linked list can be performed at different positions. Based
on the position of the node being deleted, the operation is categorized into the following
categories.
SN Operation Description
1 Deletion at It involves deletion of a node from the beginning of the list. This is the
beginning simplest operation among all. It just needs a few adjustments in the node
pointers.
2 Deletion at the It involves deleting the last node of the list. The list can either be empty
end of the list or full. Different logic is implemented for the different scenarios.
3 Deletion after It involves deleting the node after the specified node in the list. we need
specified node to skip the desired number of nodes to reach the node after which the
node will be deleted. This requires traversing through the list.
4 Traversing In traversing, we simply visit each node of the list at least once in order
to perform some specific operation on it, for example, printing data part
of each node present in the list.
5 Searching In searching, we match each element of the list with the given element.
If the element is found on any of the location then location of that
element is returned otherwise null is returned. .
Double-linked list:
In a doubly linked list, each node contains references to both the next and previous nodes.
This allows for traversal in both forward and backward directions, but it requires additional
memory for the backward reference.
In a circular linked list, the last node points back to the head node, creating a circular
structure. It can be either singly or doubly linked.
Operations on Linked Lists
Insertion: Adding a new node to a linked list involves adjusting the pointers of the existing
nodes to maintain the proper sequence. Insertion can be performed at the beginning, end, or
any position within the list
Deletion: Removing a node from a linked list requires adjusting the pointers of the
neighbouring nodes to bridge the gap left by the deleted node. Deletion can be performed at
the beginning, end, or any position within the list.
Searching: Searching for a specific value in a linked list involves traversing the list from the
head node until the value is found or the end of the list is reached.