Unit 3 Notes
Unit 3 Notes
Syllabus: List, Stack and Queue ADT Linked List: Array Vs Linked List - Singly Linked
List, Doubly Linked Lists – Circular Linked Lists-implementation - application. Stack and
Queue: Introduction – implementation (static and dynamic) application – Circular queues-
application.
int a[10] ;
int num[6] = { 2, 4, 12, 5, 45, 5 } ;
int n[ ] = { 2, 4, 12, 5, 45, 5 } ;
Where int specifies the data type or type of elements array stores. ”a” is the name of
array, and the number specified inside the square brackets is the number of
elements an array can store this is also called size or length of array.
2. The first element of the array has index zero [0]. It means the first element and
last element will be specified as: a[0] and a[9] respectively.
4. The number of elements that can be stored in an array i.e, the size of an array or
its length is given by the following equation: - (upper bound – lower bound) + 1.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 1 | 43
Some common operations performed on arrays are :
1. Creation of an array.
2. Traversing an array (accessing array elements)
3. Insertion of new elements.
4. Deletion of required elements.
5. Modification of an element.
6. Merging of arrays.
main( )
{
int avg, sum = 0 ; int i ;
int marks[30] ; /* array declaration */
clrscr();
for ( i = 0 ; i <= 29 ; i++ )
{
printf ( "\nEnter marks " ) ;
scanf ( "%d", &marks[i] ) ; /* store data in array */
}
for ( i = 0 ; i <= 29 ; i++ )
sum = sum + marks[i] ; /* read data from an array*/
avg = sum / 30 ;
printf ( "\nAverage marks = %d", avg ) ;
getch();
}
Types of Array
One dimension Array - Above details are of 1-D array. Declaration of one dimension
arrays is as follows :
int a[10] ;
int num[6] = { 2, 4, 12, 5, 45, 5 } ;
int n[ ] = { 2, 4, 12, 5, 45, 5 } ;
Two dimension Array - It is also possible for arrays to have two or more dimensions.
The two-dimensional array is also called a matrix. A sample program that stores roll
number and marks obtained by a student side by side in a matrix.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 2 | 43
main( )
{
int stud[4][2] ; int i, j ;
clrscr();
for ( i = 0 ; i <= 3 ; i++ )
{
printf ( "\n Enter roll no. and marks" ) ;
scanf ( "%d %d", &stud[i][0], &stud[i][1] ) ;
}
for ( i = 0 ; i <= 3 ; i++ )
printf ( "\n%d %d", stud[i][0], stud[i][1] ) ;
getch();
}
There are two parts to the program—in the first part through a for loop we read in the
values of roll no. and marks, whereas, in second part through another for loop we
print out these values.
In stud[i][0] and stud[i][1] the first subscript of the variable stud, is row number which
changes for every student. The second subscript tells which of the two columns are we
talking about—the zeroth column which contains the roll no. or the first column which
contains the marks. Remember the counting
of rows and columns begin with zero. The
complete array arrangement is shown
below.
In our sample program the array elements have been stored rowwise and accessed
rowwise. However, you can access the array elements columnwise as well.
Traditionally, the array elements are being stored and accessed rowwise; therefore we
would also stick to the same strategy. Initialising a 2-Dimensional Array is done as
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 3 | 43
behind the other to yield a three-
dimensional array containing three 2-
dimensional arrays. In the array
declaration note how the commas have
been given. Figure 2 would possibly
help you in visualising the situation
better. In practice, 3-D array is rarely
used. An example of initializing a
three-dimensional array for
understanding is shown: Figure 2. 3-D array
int arr[4][2][3] = { { { 2, 4 }, { 7, 8 }, { 3, 4 }, { 5, 6 } }, { { 7, 6 }, { 3, 4 }, { 5, 3 }, { 2, 3
} }, { { 8, 9 }, { 7, 2 }, { 3, 4 }, { 5, 1 }, } } ;
Advantages of Arrays:
1. Arrays store multiple data of similar types with the same name.
2. It allows random access to elements.
3. As the array is of fixed size and stored in contiguous memory locations there is
no memory shortage or overflow.
4. It is helpful to store any type of data with a fixed size.
5. Since the elements in the array are stored at contiguous memory locations it is
easy to iterate in this data structure and unit time is required to access an
element if the index is known.
Disadvantages of Arrays:
1. The array is static in nature. Once the size of the array is declared then we can’t
modify it.
2. Insertion and deletion operations are difficult in an array as elements are stored
in contiguous memory locations and the shifting operations are costly.
3. The number of elements that have to be stored in an array should be known in
advance.
4. Wastage of memory is the main problem in the array. If the array size is big the
less allocation of memory leads to wastage of memory.
For stacks and queues, we employed arrays to represent them in computer memory.
When we choose array representation (also called sequential representation) it is
necessary to declare in advance the amount of memory to be utilized. We do this by
declaring the maximum size of an array. When we really take up arrays, all the memory
may not be unused even though allocated. This leads to unnecessary wastage of
memory. The memory is very important resource. So, we should handle it efficiently.
Linked lists are special list of some data elements linked to one another. The logical
ordering is represented by having each element pointing to the next element. Each
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 4 | 43
element is called a node, which has two parts. INFO part which stores the information
and POINTER which points to the next element. Following Figure 1 shows both types
of lists (singly linked list and doubly linked lists).
Singly linked list nodes have one pointer (Next) pointing to the next node, whereas nodes
of doubly linked lists have two pointers (prev. and next). Prev. points to the previous
node and next points to the next node in the list.
KEY TERMS
A linked list is a non-sequential collection of data items called nodes. These nodes in
principle are structures containing fields. Each node in a linked list has basically two
fields.
1. Data field
2. Link field
The Data field contains an actual value to be stored and processed. And, the link
field contains the address of the next data item in the linked list. The address used to
access a particular node is known as a pointer. Therefore, the elements in a linked list
are ordered not by their physical placement in memory but their logical links stored as
part of the data within the node itself.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 5 | 43
Null Pointer - The link field of the last node contains NULL rather than a valid address.
It is a null pointer and indicates the end of the list.
External Pointer - It is a pointer to the very first node in the linked list, it enables us
to access the entire linked list.
Empty list - If the nodes are not present in a linked list, then it is called an empty
linked list or simply empty list. It is also called the null list.
Notations - Let P be s pointer to node in a linked list. Then, we can form the following
notations. To do something on linked lists.
Node (P) ; a node pointed to by the pointer P.
Data (P) ; data of the node pointed to by P.
Link (P) ; address of the next node that follows the node pointed to by the pointer p.
struct node
{
int a ;
struct node *p;
};
typedef stuct node n1;
node *start;
The above declaration defines a new data type, whose each element is of type node_type
and gives it a name n1.
Advantages: Linked lists have many advantages. Some of the very important
advantages are:
1. Linked lists are dynamic data structures. That is, they can grow or shrink during
the execution of a program.
2. Efficient memory utilization. Here, memory is not pre-allocated. Memory is
allocated whenever it is required. And it is deallocated (removed) when it is no longer
needed.
3. Insertion and deletions are easier and efficient. Linked lists provide flexibility in
inserting a data item at a specified position and deletion of a data item from the
given position.
4. Many complex applications can be easily carried out with linked lists.
Example: suppose we maintain a sorted list of IDs in an array id[ ] = [1000, 1010,
1050, 2000, 2040, …..]. And if we want to insert a new ID 1005, then to maintain
the sorted order, we have to move all the elements after 1000 (excluding 1000).
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 6 | 43
Deletion is also expensive with arrays unless some special techniques are used.
For example, to delete 1010 in id[], everything after 1010 has to be moved.
1. Dynamic size
2. Ease of insertion/deletion
1. Size: Since data can only be stored in contiguous blocks of memory in an array,
its size cannot be altered at runtime due to the risk of overwriting other data.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 7 | 43
However, in a linked list, each node points to the next one such that data can exist
at scattered (non-contiguous) addresses; this allows for a dynamic size that can
change at runtime.
2. Memory allocation: For arrays at compile time and at runtime for linked lists.
but, a dynamically allocated array also allocates memory at runtime.
3. Memory efficiency: For the same number of elements, linked lists use more
memory as a reference to the next node is also stored along with the data. However,
size flexibility in linked lists may make them use less memory overall; this is useful
when there is uncertainty about size or there are large variations in the size of data
elements; Memory equivalent to the upper limit on the size has to be allocated
(even if not all of it is being used) while using arrays, whereas linked lists can
increase their sizes step-by-step proportionately to the amount of data.
4. Execution time: Any element in an array can be directly accessed with its index.
However, in the case of a linked list, all the previous elements must be traversed
to reach any element. Also, better cache locality in arrays (due to contiguous
memory allocation) can significantly improve performance. As a result, some
operations (such as modifying a certain element) are faster in arrays, while others
(such as inserting/deleting an element in the data) are faster in linked lists.
5. Insertion: In an array, insertion operation takes more time but in a linked list
these operations are fast. For example, if we want to insert an element in the array
at the end position in the array and the array is full then we copy the array into
another array and then we can add an element whereas if the linked list is full
then we find the last node and make it next to the new node
A singly linked list is a dynamic data structure. It may grow or shrink, Growing or
shrinking depends on the operations made. Let us start the study of the singly list by
first creating it. It is called singly because this list consists of only one link, to point
to next node or element. This is a also called linear list because the last element point
to nothing it is linear in nature. The last field of last node is NULL which means that
there is no further list. The very first node is called head or first. A singly linked list is
one in which all nodes are linked together in some sequential manner. Hence, it is also
called linear linked list. Clearly it has the beginning and the end. The problem with this
list is that we cannot access the predecessor of node from the current node. This can
be overcome in doubly linked lists.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 8 | 43
In C, a linked list is created using structures, pointers and dynamic memory
allocation function malloc. We consider head as an external pointer. This helps in
creating and accessing other nodes in the linked list. Consider the following structure
definition and head creation.
struct node
{
int a ;
struct node *p; /* pointer to node */
};
typedef stuct node NODE; /* type, Definition making it abstract data type */
node *start; /* pointer to the node of linked list */
start = (NODE *) malloc (sizeof (NODE)); /* dynamic memory */
is executed, a block of memory sufficient to store the NODE is allocated and assigns
head as the starting address of the NODE. (Now, head is an external pointer). This
activity can be pictorially shown as follows:
Any number of nodes can be created and linked to the existing node. Suppose we want
to add another node to the above list, then the following statements are required
Inserting Nodes
To insert the nodes into the linked list and to insert an element into the node, following
three things should be done:
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 9 | 43
And, inserting a new node into the linked list has the following three instances:
In order to insert a new node at one of the above three situations, we have to search for
the location of insertion. The steps involved in deciding the position of insertion are as
follows:
1. If the linked list is empty or the node to be inserted appears before the starting node
then insert that node at the beginning of the linked list (i. e., as the starting node).
2. If the node to be inserted appears after the last node in the linked list then insert
that node at the end of the linked list.
3. If the above two conditions do not hold true then insert the new at the specified
position within the linked list.
1. Inserting A Node At the Beginning - This algorithm inserts item as the first Node
of the Linked list pointed by START: Insert_first(START, item)
a. Before Insertion
b. After Insertion
2. This algorithm inserts an item at the last of the linked list. Insert_last(START,
item)
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 10 | 43
Print, overflow
exit
else
PTR = (Node *) malloc (size of (node))
End if
Step 2. Set PTR → Info = Item
Step 3. Set PTR → Next = NULL
Step 4. If START = NULL then set START = PTR End if & exit, else goto step 5
Step 5. Set LOC = start
Step 6. Repeat step 7 unti1 Loc →next != NULL
Step 7. Set LOC = LOC → next
Set LOC → next = PTR
End if
3. Inserting A New Node at the Specified Position - This algorithm inserts an item
at the specified position in the linked Iist. Insert_loc(START, item, loc)
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 11 | 43
Step 8. Set PTR → next = temp → next
Step 9. Set temp → next = PTR
Deleting Nodes
The algorithms to delete the nodes from the linked list. Deleting a node from the linked
list has the following three instances.
In order to delete a node from the list, it is necessary to search for location of deletion.
The steps involved in deleting the node from the linked list are as follows:
1. If the linked list is empty then display the message - Deletion is not possible.
2. If the node to be deleted is the first node (pointed to by head pointer) then set the
pointer head to point to the second node in the linked list.
3. If the node to be deleted is the last node, then go on locating the last but one node
and set its link field to point to null pointer.
4. If the situation is other than the above three, then delete the node from the
specified position within the linked list.
1. Deleting the First Node - This algorithm Deletes an element from the first position
or front of the lined list. Delete_First (START)
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 12 | 43
Step 4. Print (element deleted is PTR → Info)
Step 5. free (PTR)
2. This algorithm deletes an element from the last position of the linked list.
Delete (START)
(a)Before Deletion
(b)After Deletion
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 13 | 43
Step 1. [Check for under f1ow]
If START == NULL, then
Print ”underf1ow”
Exit
Step 2. [Initialize the counter I and 2 pointers]
Node *temp, Node *PTR
Set I = 0
Set *PTR = START
Step 3. Repeat step 4 to 6 until I <= LOC (Loc is location given by user)
Step 4. Set temp = PTR
Step 5. Set PTR = PTR → Next
Step 6. Set I = I + 1
Step 7. Print “element de1eted is PTR → info
Step 8. Set temp → next = PTR → Next.
Step 9. Free (PTR)
Disadvantages
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 14 | 43
Doubly Linked Lists - Implementation & Application
So far you have studied singly linked lists and their variation circular linked lists. One
of the most striking disadvantages of these lists is the inability to traverse the list in
the backward direction. In most of the real world applications it is necessary to
traverse the list both in forward direction and backward direction, The most
appropriate data structure for such an application is a doubly linked list.
A doubly linked list is one in which all nodes are linked together by multiple number
of links which help in accessing both the successor node and predecessor node from
the given node position. It provides bi-directional traversing. This list called doubly
because each node has two pointers previous and next pointers. The previous pointer
points to previous node and next pointer points to next node. Only in case of head
node the previous pointer is obviously NULL and last node‘s next pointer points to
NULL. This list is a linear one.
A doubly linked list is one in which all nodes are linked together by multiple links which
help in accessing both the successor node (next node) and predecessor node (previous
node) for any arbitrary node within the list. Therefore each node in a doubly linked list
fields (pointers) to the left node (previous) and the right node (next). This helps to
traverse the list in the forward direction and backward direction.
Each node in a doubly linked list has two link fields. These are used to point to the
successor node and predecessor nodes. Figure 10 shows the structure of a node in the
doubly linked list. The LEFT link points to the predecessor node and the RIGHT link
points to the successor node.
The new node is always added before the head of the given Linked List. The task can
be performed by using the following steps:
Algorithm
1. START
2. Create a new node with three variables: prev, data, next.
3. Store the new data in the data variable
4. If the list is empty, make the new node as head.
5. Otherwise, link the address of the existing first node to the next variable of the
new node, and assign null to the prev variable.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 15 | 43
6. Point the head to the new node.
7. END
Algorithm
1. START
2. Check the head status of the doubly linked list.
3. If the head is NULL, then list is empty, deletion is not possible and Exit.
4. If the list is not empty, then create a variable Temp and assign head to temp
5. Head pointer is shifted to the next node and perform
5.1. Set NULL in pervious field of next node.
5.2. Delete Temp to delete first node
6. END
Add a node in between two nodes: It is further classified into the following two
parts:
Add a node after a given node in a Doubly Linked List: We are given a pointer to a
node as prev_node, and the new node is inserted after the given node. This can be
done using the following 6 steps: ‘E‘ is being inserted after ‘B‘.
1. Start & Firstly create a new node (say new_node). ‘E‘ is being inserted after ‘B‘.
2. Now insert the data in the new node (i.e E).
3. Using loop read the data field till you reached to desired location and perform
step 4 to 7.
4. Point the next field of new_node E to the previous field of new successor node
(i.e. C).
5. Point the previous field of new successor node to next field of new_node.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 16 | 43
6. Point the previous field of new_node to next field of predecessor Node (i.e. B).
7. Change the next field of predecessor Node to the previous field of new node and
Exit.
Add a node before a given node in a Doubly Linked List: Let the pointer to this
given node be next_node. This can be done using the following 6 steps.
1. Start & Allocate memory for the new node, let it be called new_node.
2. Put the data in new_node (i.e. B).
3. Using loop read the data field till you reached to desired location and perform step
4 to 7.
4. Point the previous field of the successor node (i.e. C) to the next field of
the new_node.
5. Point the next filed of this new_node to the previous field of successor node.
6. Point the next field of the predecessor node (i.e. A) previous field of
this new_node.
7. Now set the previous field of new_node.
• If the previous field of new node is not NULL, then set the next pointer of
this predecessor node to previous field of new_node and Exit.
• Else, if the previous field of new node is NULL, it will be the new head node
& Exit.
The new node is always added after the last node of the given Linked List. This can
be done using the following 7 steps:
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 17 | 43
Applications of Doubly Linked List:
1. Doubly linked list can be used in navigation systems where both forward and
backward traversal is required.
2. The doubly linked list is used to implement data structures like a stack, queue,
binary tree, and hash table.
3. It is also used in algorithms of LRU (Least Recently used) and MRU(Most Recently
Used) cache.
4. It can be used to implement undo/redo operations.
5. Doubly linked lists are used in web page navigation in both forward and backward
directions.
6. It can be used in games like a deck of cards.
Advantages
1. Efficient traversal in both forward and backward directions: With a doubly
linked list, you can traverse the list in both forward and backward directions,
which can be useful in certain applications.
2. Dynamic size adjustment: The size of the list can be easily adjusted based on
the number of elements added or removed, making it a flexible data structure.
5. Efficient memory utilization: The doubly linked list can be used to manage
memory efficiently, as nodes can be easily added or removed as needed.
Disadvantages
1. More memory usage: Each node in a doubly linked list requires two pointers
(previous and next), resulting in higher memory usage compared to a singly linked
list.
2. Slower access and search times: Access and search operations have O(n) time
complexity, where n is the number of elements in the list. This can result in slower
performance compared to other data structures like arrays or trees, especially for
large lists.
4. Higher overhead for updates: Updates to the list, such as inserting or deleting
elements, can be more time-consuming compared to arrays or other data
structures.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 18 | 43
Comparison between Singly linked Doubly Linked List
A circular linked list has no end. Therefore, it is necessary to establish the FIRST and
LAST nodes in such a linked list. It is useful if we set the external pointer (i.e., head
pointer) to point to the last node in this list. If head is the external pointer, then Head
→ PTR would indicate the START node in the list. The START and the LAST nodes of a
circular linked list with this conversion are shown in Figure 16.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 19 | 43
Application of Circular Linked Lists:
• Efficient operations: Since the last node of the list points back to the first
node, circular linked lists can be traversed quickly and efficiently.
• Space efficiency: Circular linked lists can be more space-efficient than other
types of linked lists because they do not require a separate pointer to keep track
of the end of the list.
• Flexibility: The circular structure of the list allows for greater flexibility in
certain applications.
• Dynamic size: Circular linked lists can be dynamically sized, which means
that nodes can be added or removed from the list as needed.
• Ease of implementation: Implementing circular linked lists is often simpler
than implementing other types of linked lists.
• Complexity: Circular linked lists can be more complex than other types of
linked lists, especially when it comes to algorithms for insertion and deletion
operations.
• Memory leaks: If the pointers in a circular linked list are not managed
properly, memory leaks can occur. T
• Traversal can be more difficult: While traversal of a circular linked list
become more difficult than linear traversal, especially if the circular list has a
complex structure.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 20 | 43
predecessor pointer in circular manner. The objective behind considering circular
doubly linked lists is to simplify the insertion and deletion operations performed on
doubly linked list.
Consider the situation of an empty list. This situation can be dispensed with by never
allowing a list to be empty. It can be accomplished by providing a special node called
start node that always remains in realizing a some degree of symmetry in the linked
list structure by making the list circular. A circular doubly linked list with a head node
is shown in Figure 17.
To implement a circular singly linked list, we take an external pointer that points to
the last node of the list. If we have a pointer last pointing to the last node, then
last→next will point to the first node. The pointer last points to node Z and last→next
points to node P.
Why have we taken a pointer that points to the last node instead of the first
node?
For the insertion of a node at the beginning, we need to traverse the whole list. Also,
for insertion at the end, the whole list has to be traversed. If instead of
the start pointer, we take a pointer to the last node, then in both cases there won’t be
any need to traverse the whole list. So insertion at the beginning or at the end takes
constant time, irrespective of the length of the list.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 21 | 43
• Create a node, say T and insert value in data filed
• Make T -> next = last -> next
• last -> next = T
Suppose 12 needs to be inserted after the node that has the value 8
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 22 | 43
Figure 21. A circular linked list node insertion in between node.
3. Any node can be a starting point. We can traverse the whole list by starting from any
point. We just need to stop when the first visited node is visited again.
4. Useful for implementation of queue. We don’t need to maintain two pointers for
front and rear if we use circular linked list. We can maintain a pointer to the last
inserted node and front can always be obtained as next of last.
5. Circular lists are useful in applications to repeatedly go around the list. For
example, when multiple applications are running on a PC, it is common for the
operating system to put the running applications on a list and then to cycle through
them, giving each of them a slice of time to execute, and then making them wait while
the CPU is given to another application.
1. The real life application where the circular linked list is used is our Personal
Computers, where multiple applications are running. All the running applications
are kept in a circular linked list and the OS gives a fixed time slot to all for running.
The Operating System keeps on iterating over the linked list until all the applications
are completed.
2. Another example can be Multiplayer games. All the Players are kept in a Circular
Linked List and the pointer keeps on moving forward as a player's chance ends.
3. Circular Linked List can also be used to create Circular Queue. In a Queue we
have to keep two pointers, FRONT and REAR in memory all the time, where as in
Circular Linked List, only one pointer is required.
1. Creation - This operation is used to create a linked list. Node is created when it is
required and linked to the list to preserve the integrity of the list.
2. Searching - This operation is used to search the item in the linked list from the
starting node to end node. If the desired element is found, we signal operation
”SUCCESSFULL”. Otherwise, we signal it as “UNSUCCESSFULL”.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 23 | 43
3. Insertion - This operation is used to insert a new node in the linked list at the
specified position. A new node may be inserted
4. Concatenation - It is the process of appending (joining) the second list to the end
of the first list consisting of m nodes. When we concatenate two lists, the second list
has n nodes, and then the concatenated list will be having (m+ n) nodes. The last
node of the list is modified so that it is now pointing to the first node in the second
list.
5. Deletion - This operation is used to delete an item (a node) from the linked list. A
node may be deleted from the
6. Display - This operation is used to print each and every node’s information. We
access each node from the beginning (or the specified position) of the list and output
the data housed there.
7. Traversing - It is a process of going through all the nodes of a linked list from one
end to the other end. If we start traversing from the very first node towards the last
node, it is called forward traversing.
Example 2: Some of you may eat biscuits. If you assume only one side of the cover is
torn and biscuits are taken out one by one. This is what is called poping and similarly,
if you want to preserve some biscuits for some time later, you will put them back into
the pack through the same torn end called pushing.
Whenever a stack is created, the stack base remains fixed, as a new element is added
to the stack from the top, the top goes on increasing; conversely as the top most element
of the stack is removed the stack top is decrementing. Shown above in example.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 24 | 43
(a) Stack top Increases during Insertion. (b) Stack top decreases during deletion.
Fig. 23 shows various stages of stack top, during insertion and during deletion.
1. Static implementation.
2. Dynamic implementation.
Declaration 1:
The stack is nothing but an array of integers. And most recent index of that array will
act as a top. The stack is of the size 100. As we insert the numbers, the top will get
incremented. The elements will be placed from 0th position in the stack. At the most we
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 25 | 43
can store 100 elements in the stack,
so at the most last element can be at
(size-1) position, i.e., at index 99.
Declaration 2:
ALGORITHMS FOR PUSH - Algorithm for inserting item info the Stack (PUSH)
PUSH (stack[Maxsize], Item): This algorithm inserts ah item at the top of the Stack
Step 1: Initialize the Maxsize and set top = - 1 to show stack is empty
Step 2: Insert the element in stack Repeat steps until top <= Maxsize - 1
2.1. Read, Item in stack
2.2. Increase top by 1, Set top = top + 1
2.3. Set top at current inserted item of the stack, Set Stack [top] = Item
Step 3: Insertion after condition get false, Print “Stack overflow"
The function for the stack push operation in C Is as follows- Considering stack declared
as int Stack [5], top -1;
void push( )
{
int item ;
if (top<4)
{
printf ("Enter the no") ;
scanf ("%d", & item) ;
top = top + 1 ;
stack [top] = item ;
}
else
printf ("Stack overflow") ;
}
ALGORITHMS FOR POP - Algorithm for deleting an item from the Stock (POP)
POP (Stack[Maxsize], Item). This algorithm delete item from the top of stack.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 26 | 43
Step 1: Remove the element form stack and repeat steps until top >= -1
1.1. Remove element from top of stack, Set item = Stack [top]
1.2. After removing element decrement top by 1, Set top = top - 1
1.3. When element is removed Print, Number deleted is Item
Step 2: When all element is deleted and condition is false Print “stack under flow”.
Dynamic implementation is also called linked list representation and uses pointers
to implement the stack type of data structure. Stack is a special case of list and
therefore we can represent stack using arrays as well as using linked list. The
advantage of implementing stack using linked list is that we need not have to worry
about the size of the stack. Since we are using linked list as many elements we want
to inset those many nodes can be created. And the nodes are dynamically getting
created so there won’t be any stack full condition. The typical ‘C’ structure for linked
stack can be
struct stack
{
int data;
struct stack *next;
} node;
1. Push
2. Pop
3. Stack empty
4. Stack full
OPERATIONS ON STACK - The basic operations that can be performed on stack are
as follows:
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 27 | 43
1. Create a stack - The create function creates a stack in the memory. Creation of
stack can be either arrays or linked list.
2. Insert an element onto the stack – PUSH - The insert function inserts a new
element at the top of the stack.
Push - The process of adding a new element to the top of stack is called PUSH
operation. Pushing an element in the stack invoke adding of element, as the new
element will be inserted at the top after every push operation the top is incremented
by one. In case the array is full and no new element can be accommodated, it is
called STACK-FULL condition. This condition is called STACK OVERFLOW.
3. Delete an element from the stack – POP – The delete function removes that
element which is at the top of the stack.
POP - The process of deleting an element from the top of stack is called POP
operation. After every pop operation the stack is decremented by one. If there is no
element on the stack and the pop is performed then this will result into STACK
UNDERFLOW condition.
4. Check which element is at the top of the stack - Many times we need to test for
the topmost element of the stack and to test whether the stack is empty or nonempty
before we make subsequent operations. The function which test for the emptiness
of the stack, say, ’stempty’ is a Boolean function which returns true if the stack
is empty and false otherwise.
Initially stack is empty and the top should be initialized to -1 or 0. If we set top
to -1 initially then the stack will contain the elements from 0th position and if we
set top to 0 initially, the elements will be stored from 1st position, in the stack.
Elements may be pushed onto the stack and there may be a case that all the
elements are removed from the stack. Then the stack becomes empty. Thus
whenever top reaches to -1 we can say the stack is empty.
int stempty()
{
if (sttop==-1)
return 1;
else
return 0;
}
In the representation of stack using arrays, size of array means size of stack. As we
go on inserting the elements the stack gets filled with the elements. So it is necessary
before inserting the elements to check whether the stack is full or not. Stack full
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 28 | 43
condition is achieved when stack reaches to maximum size of array. If
top>=maxsize-1 or top>=maxsize it means stack is full.
int stfull()
{
if (sttop>=maxsize-1)
return 1;
else
return 0;
}
Stack terminology
1. MAXSIZE - This term is not a standard one; we use this term to refer the maximum
size of stack.
2. TOP - This term refers to the top of stack (TOS). The stack top is used to check stack
overflow or underflow conditions. Initially Top stores -1. Top is first incremented and
then the item is inserted into the location currently indicated by the Top.
3. Stack - It is an array of size MAXSIZE.
4. Stack empty or underflow - This is the situation when the stack contains no
element. At this point the top of stack is present at the bottom of the stack.
5. Stack overflow - This is the situation when the stack becomes full, and no more
elements can be pushed onto the stack. At this point the stack top is present at the
highest location of the stack.
ALGORITHMS FOR PUSH - Algorithm for inserting item info the Stack (PUSH)
PUSH : This algorithm inserts ah item at the top of the Stack
The function for the stack push operation in C Is as follows- Considering stack declared
as
struct node
{
int data;
struct node *next;
};
struct node *top = NULL;
void push(int item)
{
struct node *nptr = malloc(sizeof(struct node));
nptr->data = item;
nptr->next = top;
top = nptr;
}
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 29 | 43
ALGORITHMS FOR POP - Algorithm for deleting an item from the Stock (POP)
Step 1: Check top is == Null, if yes stack is empty and stop program otherwise go to
steps 2
Step 2: Create a new pointer *temp and assign top node to temp pointer.
2.1. Assign top to next node (node after deleted node) and print data deleted.
2.2. Free temp pointer
2.3. Repeat these steps till all data items get deleted
void pop()
{
if (top == NULL)
{
printf("\n\nStack is empty ");
}
else
{
struct node *temp;
temp = top;
top = top->next;
printf("\n\n%d deleted", temp->data);
free(temp);
}
}
To display the new node and to check Inserted/deleted item use Display() function
void display()
{
struct node *temp;
temp = top;
while (temp != NULL)
{
printf("\n%d", temp->data);
temp = temp->next;
}
}
Applications Of Stacks
There are a number of applications of stacks, some of them are discussed here.
1. Reversing a String
2. Expression conversion
3. Expression evaluation
4. Parsing well-formed parenthesis
5. Decimal to binary conversion
6. Storing function calls
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 30 | 43
Evaluation Of Expression - postfix, Infix, prefix expression
There are basically three types of notations for an expression (mathematical
expression; An expression is defined as a number of operand or data items combined
using several operators.
1. Infix notation
2. Prefix notation
3. Postfix notation
Infix notation - The infix notation is what we come across in our general mathematics,
where the operator is written in between the operands. For example - The expression to
add two numbers A and B is written 1n infix notation as: A + B
Note that the operator ’ + ' is written in-between the operands A and B. That’s why this
notation is called infix.
Prefix notation - The prefix notation a notation in which the operator is written before
the operands, it is also called polish notation in the honor of the mathematician Jan
Lukasiewicz who developed this notation. The same expression when written in prefix
notation looks like: +AB
As the operator ’+’ is written before the operands A and B, this notation is called prefix
(pre means before).
Postfix notation - In the postfix notation the operators are written after the operands,
so it is called the postfix notation (post means after), it is also known as suffix notation
or reverse polish notation. The above expression if written in postfix expression looks
like follows: AB+
Although human beings are quite used to work with mathematical expressions in infix
notation, which is rather complex, as using this notation one has to remember a set of
non-trivial rules. That set of rules must be applied to expressions in order to determine
the final value. These rules include precedence, BODMAS, and Associativity.
Using infix notation, one cannot tell the order in which operators should be applied
by only looking at expression. Whenever an infix expression consists of more than one
operator, the precedence rules (BODMAS) should be applied to decide that which
operator) are evaluated first. As compared to postfix notation, which is much easier to
work with or evaluate. In postfix expression operands appear before the operators, there
is no need for operator precedence and other rules. As soon as an operator appears in
the postfix expression during scanning of postfix expression the topmost operands are
popped off and are calculated applying the encountered operator. Place the result back
onto the stack, doing so the stack will contain finally a single value at the end of process.
Notation Conversions - Let an expression A + B * C is given, which is in infix notation.
To calculate this expression for values 4, 3, 7 for A, B, C respectively e must follow
certain rule (called BODMAS in general mathematics) in order to have right result. For
example
A + B * C = 4 + 3 * 7 = 7 * 7 = 49
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 31 | 43
Is this the right result? No, this is because the multiplication is to be done before
addition, because it has higher precedence over addition. This means that for
expression to be calculated we must have the knowledge of precedence of operators.
The error in the above calculation occurs, as there are no braces to define the
precedence of operators. Thus, expression A + B * C is interpreted as, A + (B * C). This
is an alternative for us to convey the computer that multiplication has higher
precedence over addition, as there is no other way to specify this.
Operator precedence
Exponential operator ^, $, ↑ Highest precedence
Bracket ( ), [ ], { } Next precedence
Division /Multiplication /, * Next precedence
Addition/Subtraction +, - Least precedence
Converting infix expression to postfix form
A+B*C Infix Form
A + (B * C) Parenthesized expression,
A + (BC*) Convert the multiplication
A(BC*) + Convert the addition
ABC* + Postfix form
1. Read the infix expression for left to right one character at a time.
2. If input symbol read is “(” then push it on to the stack.
3. If the input symbol read is an operand (e.g. A,B,C) then place it in postfix
expression.
4. If the input symbol read is operator (e.g. +, - ,^) then
4.1 Check if priority of operator in stack is greater than the priority of incoming (or
input read) operator then pop that operator from stack and place it in the postfix
expression Repeat step 4.1 till we get the operator in the stack which has greater
priority than the incoming operator.
4.2 Otherwise push the operator being read, onto the stack.
4.3 If we read input operator as “)” then pop all the operators until we get "(" and
append popped operators to postfix expression. Finally just pop ")".
5. Final pop the remaining contents, from the stack until stack becomes empty
append them to postfix expression.
6. Print the postfix expression as a result.
The priorities of different operators when they are in stack will be called as in stack
priorities. And the priorities of different operator when then are from input will be called
as incoming priorities.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 32 | 43
Solved Problems - Convert: (A + ( B * C - ( D / E – F ) * G ) * H) into postfix form
showing stack status after every step in tabular form.
SOLUTION -
This algorithm is bit tricky, in this we first reverse the input expression a+b*c will
become c*b+a and then we do the conversion and then again we reverse to get the result.
Doing this has an advantage that except for some minor modification algorithm for infix
to prefix remains almost same as the one for infix to postfix.
Algorithm
1. Add bracket in starting and end of expression & Reverse the input string.
2. Examine the next e1ement in the input.
3. If it is operand, add it to the output string.
4. If it is c1osing parenthesis, push it on stack.
5. If it is an operator, then
5.1. if stack is empty, push operation on stack.
5.2. if the top of stack is closing parenthesis push operator on stack.
5.3. If it has same or higher priority than the top of stack, push operator on output
string S.
5.4. Else pop the operator from the stack and add it to output string S.
6. If it is a opening parenthesis, pop operator from stack and add them to S until a
closing parenthesis is encountered. POP and discard the closing parenthesis.
7. If there is more input go to step 2. If there is no more input, unstack the remaining
operators and add them.
8. Reverse the output string.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 33 | 43
Convert (A + B * C) into the prefix expression.
So postfix expression is CB*A+. Now reverse it to get final result i.e. +A*BC
The important feature of the queue - new customers got into the queue from the rear
end, whereas the customers who get their seats reserved leave the queue from the front
end. It means the customers are serviced in the order in which they arrive at the service
center i.e. first come first serve (FCFS) type of service.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 34 | 43
Figure 27. (a) Empty Queue (b) Queue with one element.
(c) Queue with two elements (d) Queue with three elements
Note that during the insertion of the first element in the queue we always increment
the Front by one i.e.: Front = Front + 1. Afterwards the Front will not be changed
during the entire addition operation. The following figure shows queue graphically
during deletion operation:
Figure 28. (a) Queue with three elements (b) Two elements deleted from front (20 & 30)
Now if we insert two elements in the queue, the queue will look like: R = 4 and F =2
shown in Figure 29.
1. The first and most important advantage of queue over stack is that there are two
different pointers maintained for handling queues. That is front and rear.
2. In stack the element which is inserted first will be removed at the last. That means
the first element has to stay in the stack for a long time. But in queue the element
which is inserted first will get removed first.
3. Using queue we can maintain the priority of the elements, which is not possible using
stack.
4. In certain type of queue such as deque both the ends can be utilized for insertion and
deletion of elements.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 35 | 43
2. Dynamic implementation (using pointers & structure).
If Queue is Implemented using arrays, we must be sure about the exact number of
elements we want to store In the queue, because we have to declare the size of the
array at design time or before the processing starts in this case, the beginning of the
array will become the front for the queue and the last location of the array will act as
rear for the queue. The following relation gives the total number of elements present in
the queue, when implemented using arrays: front - rear + 1
If rear < front then there will be no element in the queue or queue will always be
empty.
Let Queue be the array of some specified size say - MAXSIZE, then the insertion
algorithm will be as given below. While implementing a queue using arrays we must
check underflow and overflow conditions for queue.
Queue is declared as int queue [5], Front = -1 and rear = -1. This algorithm inserts an
Item at the rear of the queue (maxsize).
Step 1: Initialization of
Set Front = -1
Set Rear = -1
Step 2: Repeat steps 3 to Until Rear <= maxsize - 1.
Step 3: Read item
Step 4: if Front == -1 then
Front = 0
Rear = 0
else
Rear = rear + 1
endif
Step 5: Set Queue [Rear] = Item
Step 6: Print. Queue overflow if step 2 condition failed
Function
void queue( )
{
int item
if (rear < maxsize -1)
{
printf (“Enter the number”) ;
scanf (“%d” & item) ;
if (front == -1)
{ front = 0 ; rear = 0 ; }
else
{ rear = rear+1; }
queue [rear] = item ;
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 36 | 43
}
else
printf (“queue is full");
}
Function
void delete( )
{
int item ;
if (front != -1)
{
item = queue[front] ;
if (front == rear)
{ front = -1 : rear = -1 ; }
else
front = front +1 ;
printf ("No deleted is = %d", item) ;
}
else
printf (“Queue is empty”) ;
}
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 37 | 43
There are certain problems associated with a simple queue when queue is implemented
using arrays as we have just studied. Consider an example of a simple queue Q[5],
which is initially empty. We analyze the problem with a series of insertion and deletion
operations performed on the queue. The insertion and deletion processes are shown in
the following Figures (insertion in fig 30 and deletion in fig 31).
Figure 31. (a) After Deleting an Element (b) After Adding two Elements
Up to now (fig. 31) there is no problem; the problem arises when we delete an element
from the queue. If we further add new items to the queue, the queue looks like as given
in figure below (figure 11). Now if we attempt to add more elements, Problem arise that
the elements can’t be inserted, because in a queue the new elements are always added
from the rear end, and rear here indicates to last location of the array (location with
subscript 4). Hence though the space is there in the queue we are not able to insert the
elements (if we try to do so the program will show the message “Queue Is Full” though
it is empty.
Figure 32. (a) After Deleting an Element (b) After Deleting an Element
(c) Alter Deleting an Element in (d) After Deleting an Element
To remove this problem, the first solution is, which comes into our mind is whenever
an element is deleted, shift all afterward elements to the left by one position. But if the
queue is too large of say 5000 elements it will be a difficult job to do and time consuming
too. To remove this problem we use circular queue. Now if Front = 5, this takes Front
pointer out. To take this pointer again pointing to the right position in the queue we
always reset (or check) both the pointers Front and Rear, this step is highlighted in the
deletion algorithm given above.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 38 | 43
when it is needed to insert or delete an element in the middle of a group of other
elements.
Suppose we wish to insert an element between the second and third element in the
array of size 10, which already contains eight elements (a[0] to a[7]). This requires us to
shift one place, all the elements from third onwards (a[2] to a[7]) so that a slot should
be made empty to insert new element. It means we have to shift five elements. If the
array is of large size say 1000 or 2000 elements, it would be equivalent of doing a bulk
of work and gaining no special results. Similarly when element is deleted between array,
it requires all the elements beyond the deleted element to be shifted, so as to fill the gap
created by deletion of element. On the other hand linked representation allows us to
stress on our primary aim (i.e., addition or deletion) and not on the overheads related
to these processes.
In other words the amount of work required is independent of the size of the list.
Addition of a new node in between a queue in a linked representation requires creating
a new node, inserting it in the required position by adjusting two pointers. Whereas
to delete a node from the queue all we require is to adjust a pointer to point the node
to be deleted, and then free that node. An individual node and queue in linked
representation is shown in the figure 33.
struct qnode
{
int n;
qnode *next;
} *start = NULL;
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 39 | 43
Step 7: temp -> next =- NEWPTR
Step 8: end
Note that we don’t need to check overflow condition in dynamic implementation during
addition operation. Though overflow can occur in this implementation but it occurs
almost never. Only the underflow condition is to be checked during deletion operation.
Operation On Queue
Applications of Queue:
3. Job Scheduling: The computer has a task to execute a particular number of jobs
that are scheduled to be executed one after another. These jobs are assigned to the
processor one by one which is organized using a queue.
4. Shared resources: Queues are used as waiting lists for a single shared resource.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 40 | 43
Advantages of Queue:
1. A large amount of data can be managed efficiently with ease.
2. Operations such as insertion and deletion can be performed with ease as it follows
the first in first out rule.
3. Queues are useful when a particular service is used by multiple consumers.
4. Queues are fast in speed for data inter-process communication.
5. Queues can be used in the implementation of other data structures.
Disadvantages of Queue:
1. The operations such as insertion and deletion of elements from the middle are time
consuming.
2. Limited Space.
3. In a classical queue, a new element can only be inserted when the existing elements
are deleted from the queue.
4. Searching an element takes O(N) time.
5. Maximum size of a queue must be defined prior.
1. Front will always be pointing to the first element (as in the linear queue).
2. If Front = Rear the queue will be empty.
3. Each time a new element is inserted into the queue the Rear is incremented by one.
Rear = Rear + 1.
4. Each time an element is deleted from the queue the value of Front is incremented
by one. Front = Front + 1
Insertion
Consider the circular queue shown above, the insertion in this queue will be same as
with linear queue, we only have to keep track of Front and Rear with some extra logic.
A Circular Queue is shown in the figure 35(a). If more elements are added to the queue,
it looks like shown in figure (b).
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 41 | 43
Figure 35. (a)(b)(c) Insertion in a queue
Now the queue will be full. If we now try to add another element to the queue, as the
new element is inserted from the Rear end, the position of the element to be inserted
will be calculated by the relation:
For the current queue Fig 35(b) the value of Rear is 4, and value Of MAXSIZE is 5, hence
Note that Front is also pointing to Q[0] (Front = 0) and Rear also comes out to be 0 (i.e.,
Rear is also pointing to Q[0]). Since Rear = Front, the Queue Overflow condition is
satisfied, and we try to add new element will flash the message “Queue Overflow” which
avoids us to add new element.
Consider the Fig 35(c). Consider the situation when we add, an element to the queue.
The Rear is calculated as
The new element will be added to Q[Rear] or Q[0] location of the array and Rear is
increased by one (i.e., Rear = Rear + 1 = 0 + 1) The next element will be added to location
Q[1] of the array.
Deletion
The deletion method for a circular queue also requires some modification as compared
to linear queues. The deletion is explained in Fig 36. In the Fig 36 the queue is full. Now
if we delete one element from the queue, it will be deleted from the Front end. After
deleting the front element, the front should be modified according to position of Front,
i.e., if Front indicates to the last element of the circular queue then after deleting that
element the Front should be again reset to 0 (Front = 0). Otherwise after every deletion
the new position which Front should indicate will be as:
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 42 | 43
Step 1: If (Front == (Rear + 1) % MAXSIZE) write Queue Overflow and Exit.
Else: Take the value
If (Front == -1)
Set Front = Rear = 0 Figure 36. Queue deletion
Else
Rear = ((Rear + 1) % MAXSIZE) [Assign Value]
Queue [Rear] = value.
[End if]
Step 2: Exit.
D r. D h e r e s h S o n i , A s s t . P r o f. V I T B h o p a l U n i v e r s i t y P a g e 43 | 43