Linked Lists
An array is a data structure where elements are
stored in consecutive memory locations. In order to occupy the adjacent space,
a block of memory that is required for the array should be allocated before hand. Once the memory is allocated, it cannot be
extended any more. This is why the array is known as a static data structure.
In contrast to this, the linked list is called a dynamic data structure
where the amount of memory required can be varied during its use. In the linked
list, the adjacency between the elements is maintained by means of links or
pointers. A link or pointer actually is the address (memory location) of
the subsequent element. Thus, in a linked list, data (actual content) and link
(to point to the next data) both are required to be maintained. An element in a
linked list is a specially termed node, which can be viewed as shown in
Figure 5.1. A node consists of two fields: DATA (to store the actual
information) and LINK (to point to the next node).
Figure 5.1 Node: an
element in a linked list.
Definition
A linked list is an ordered collection of
finite, homogeneous data elements called nodes where the linear order is
maintained by means of links or pointers.
Depending
on the requirements the pointers are maintained, and accordingly the linked
list can be classified into three major groups: single linked list, circular
linked list, and double linked list.
Single Linked List
In a single linked list each node contains only one
link which points to the subsequent node in the list. Figure 3.2 shows a linked
list with six nodes.
Here, N1,
N2, . . ., N6 are the constituent nodes in
the list. HEADER is an empty node (having data content NULL) and only used to
store a pointer to the first node N1. Thus, if one knows the address of the
HEADER node from the link field of this node, the next node can be traced, and
so on. This means that starting from the first node one can reach to the last node
whose link field does not contain any address but has a null value. Note that
in a single linked list one can move from left to right only; this is why a
single linked list is also called one way list.
Representation of a Linked List
in Memory
There are two ways to represent a linked list in memory:
1. Static
representation using array
2. Dynamic
representation using free pool of storage
Static representation
In static representation of a single linked list,
two arrays are maintained: one array for data and the other for links. The
static representation of the linked list in Figure 5.2 is shown in Figure 5.3.
Figure 5.2 A
single linked list with six nodes.
Figure 5.3 Static
representation using arrays of the single linked list of Figure 3.20.
Two
parallel arrays of equal size are allocated which should be sufficient to store
the entire linked list. Nevertheless this contradicts the idea of the linked
list (that is non-contagious location of elements). But in some programming
languages, for example, ALGOL, FORTRAN, BASIC, etc. such a representation is
the only representation to manage a linked list.
Dynamic representation
The efficient way of representing a linked list is
using the free pool of storage. In this method, there is a memory bank
(which is nothing but a collection of free memory spaces) and a memory
manager (a program, in fact). During the creation of a linked list,
whenever a node is required the request is placed to the memory manager; the
memory manager will then search the memory bank for the block requested and, if
found, grants the desired block to the caller. Again, there is also another
program called the garbage collector; it plays whenever a node is
no more in use; it returns the unused node to the memory bank. It may be noted
that memory bank is basically a list of memory spaces which is available to a
programmer. Such a memory management is known as dynamic memory management.
The dynamic representation of linked list uses the dynamic memory management
policy.
The
mechanism of dynamic representation of single linked list is illustrated in
Figures 3.4(a) and 3.4(b). A list of available memory spaces is there
whose pointer is stored in AVAIL. For a request of a node, the list AVAIL is
searched for the block of right size. If AVAIL is null or if the block of
desired size is not found, the memory manager will return a message
accordingly. Suppose the block is found and let it be XY. Then the memory
manager will return the pointer of XY to the caller in a temporary buffer, say
NEW. The newly availed node XY then can be inserted at any position in the
linked list by changing the pointers of the concerned nodes. In Figure 3.4(a), the
node XY is inserted at the end and change of pointers is shown by the dotted
arrows. Figure 3.4(b) explains the mechanism of how a node can be returned from
a linked list to the memory bank.
Figure 5.4(a) Allocation of a node from memory bank to a linked list.
Figure 5.4(b) Returning
a node from a linked list to memory bank.
The
pointers which are required to be manipulated while returning a node are shown with dotted arrows.
Note that such allocations or deallocations are carried out by changing the
pointers only.
Operations on a Single Linked
List
The operations possible on a single linked list are listed below:
● Traversing
the list
● Inserting
a node into the list
● Deleting
a node from the list
● Copying
the list to make a duplicate of it
● Merging
the linked list with another one to make a larger list
● Searching
for an element in the list.
We will assume the following convention in our
subsequent discussions: suppose X is a pointer to a node. The values in the
DATA field and LINK field will be denoted by XDATA and XLINK, respectively. We
will write NULL to imply nil value in the DATA and LINK fields.
Traversing a single linked list
In traversing a single linked list, we visit every node in the list
starting from the first node to the last node. The following is the algorithm Traverse_SL for the same.
Algorithm Traverse_SL
Input: HEADER is the pointer to the header node.
Output: According to the Process( )
Data structures: A single linked list whose address of the starting node is
known from the HEADER.
Steps: 1. ptr = HEADER®LINK // ptr is to
store the pointer to a current node 2. While (ptr ą NULL) do //
Continue till the last node 3. Process(ptr) // Perform Process(
) on the current node 4. ptr = ptr®LINK // Move to the next node 5. EndWhile 6.
Stop |
Note: A simple operation, namely Process( )
may be devised to print the data content of a node, or count the total number
of nodes, etc.
Inserting a node into a single
linked list
There are various positions where a node can be inserted:
(i) Inserting at the
front (as a first element)
(ii) Inserting at the end (as a last element)
(iii) Inserting
at any other position.
Before we discuss these insertions, let us assume a
procedure GetNode(NODE) to get a pointer of a memory block which suits the
type NODE. The procedure may be defined as follows:
Procedure GetNode
Input: NODE is the type of the data for which a memory has to be
allocated.
Output: Return a message if the allocation fails else the pointer to
the memory block allocated.
Steps: 1. If (AVAIL = NULL) //
AVAIL is the pointer to the pool of free storage 2. Return(NULL) 3. Print
“Insufficient memory: Unable to allocate memory” 4. Else //
Sufficient memory is available 5. ptr = AVAIL // Start
from the location, where AVAIL points 6. While
(SizeOf(ptr) ą SizeOf(NODE)) and (ptr®LINK
ą NULL) do // Till the desired block is found
or the search reaches the end of the pool 7. ptr1 = ptr // To keep the track of the
previous block 8. ptr = ptr®LINK // Move to the next block 9. EndWhile 10. If (SizeOf(ptr) = SizeOf(NODE)) //
Memory block of right size is found 11. ptr1®LINK = ptr®LINK // Update the AVAIL list 12. Return(ptr) 13. Else 14. Print
“The memory block is too large to fit” 15. Return(NULL) 16. EndIf 17. EndIf 18. Stop |
Node: The GetNode( ) procedure as defined above is just to understand how a
node can be allocated from the available storage space. In C, C++ and Java,
there is a library routine for doing the same such as alloc(), malloc() (in C, C++), new (in C++, Java).
Inserting a node at the front of a
single linked list
The algorithm InsertFront_SL is used to
insert a node at the front of a single linked list.
Algorithm InsertFront_SL
Input: HEADER is the pointer to the header node and X is
the data of the node to be inserted.
Output: A single linked list with a newly inserted node at the front of
the list.
Data structures: A single linked list whose address of the starting node is known
from the HEADER.
Steps: 1. new = GetNode(NODE) // Get a memory block of type NODE
and store its pointer in new 2. If (new = NULL) then // Memory manager returns NULL on searching the memory
bank 3. Print “Memory underflow: No insertion” 4. Exit //
Quit the program 5. Else // Memory
is available and get a node from memory bank 6. new®LINK = HEADER®LINK // Change of pointer 1 as
shown in Figure 3.5(a) 7. new®DATA = X // Copy the data X to
newly availed node 8. HEADER®LINK = new //
Change of pointer 2 as shown in Figure 3.5(a) 9. EndIf 10. Stop |
Figure 5.5(a) Inserting
a node in the front of a single linked list.
Inserting a node at the end of a
single linked list
The algorithm InsertEnd_SL is used to
insert a node at the end of a single linked list.
Algorithm InsertEnd_SL
Input: HEADER is the pointer to the header node and X is
the data of the node to be inserted.
Output: A single linked list with a newly inserted node having data X
at the end of the list.
Data structures: A single linked list whose address of the starting
node is known from the HEADER.
Steps: 1. new = GetNode(NODE) // Get a memory block of type NODE
and return its pointer as new 2. If (new = NULL) then // Unable to allocate memory for a node 3. Print “Memory is insufficient: Insertion is not
possible” 4. Exit // Quit the program 5. Else // Move to the
end of the given list and then insert 6. ptr = HEADER // Start from the HEADER node 7. While (ptr®LINK
ą NULL) do //
Move to the end 8. ptr = ptr®LINK // Change pointer to the next
node 9. EndWhile 10. ptr®LINK = new // Change the link field of
last node: 11. new®DATA = X // Copy the content X into
the new node 12. EndIf 13. Stop |
Figure 5.5(b) Inserting
a node at the end of a single linked list.
Inserting a node into a single
linked list at any position in the list
The algorithm InsertAny_SL is used to
insert a node into a single linked list at any position in the list.
Algorithm InsertAny_SL
Input: HEADER is the pointer to the header node, X is the
data of the node to be inserted, and KEY being the data of the key node
after which the node has to be inserted.
Output: A single linked list enriched with newly inserted node having data
X after the node with data KEY.
Data structures: A single linked list whose address of the starting node is known
from the HEADER.
Steps: 1. new = GetNode(NODE) //
Get a memory block of type NODE and 2. If (new = NULL) then //
Unable to allocate memory for a node 3. Print “Memory is insufficient: Insertion is not
possible” 4. Exit // Quit the program 5. Else 6. ptr = HEADER //Start
from the HEADER node 7. While (ptr®DATA
ą KEY) and (ptr®LINK
ą NULL) do //
Move to the node 8. ptr = ptr®LINK 9. EndWhile 10. If (ptr®LINK = NULL) then // Search fails to find the KEY 11. Print “KEY is not available in the list” 12. Exit 13. Else 14. new®LINK = ptr®LINK // Change the pointer 1 as shown in
Figure 3.5(c) 15. new®DATA = X // Copy the content into
the new node 16. ptr®LINK = new // Change the pointer 2 as
shown in Figure 3.5(c) 17. EndIf 18. EndIf 19. Stop |
Figure 5.5(c) Inserting
a node at any position in a single linked list.
Deleting a node from a single linked
list
Like insertions, there are also three cases of deletions:
(i) Deleting from the
front of the list
(ii) Deleting from the end of the list
(iii) Deleting from any position in the list
Let us
consider the procedure for various cases of deletion. We will assume a
procedure, namely ReturnNode(ptr) which returns a node
having pointer ptr to the free pool of
storage. The procedure ReturnNode(ptr) is defined as
follows:
Procedure ReturnNode
Input: PTR is the pointer of a node to be returned to a list
pointed by the pointer AVAIL.
Output: The node is inserted into the list AVAIL at the end.
Steps: 1. ptr1 = AVAIL // Start from
the beginning of the free pool 2. While (ptr1®LINK ą NULL) do 3. ptr1 = ptr1®LINK 4. EndWhile 5. ptr1®LINK = PTR
// Insert the node at the end 6. PTR®LINK = NULL
// Node inserted is the last node 7. Stop |
Note: The
procedure ReturnNode( ) inserts the free node at the end of the pool of free
storage whose header address is AVAIL. Alternatively, we can insert the
free node at the front or at any position of the AVAIL list which is
left as an exercise for the student. In C and C++ this procedure is realized as
a library routine free().
Deleting the node at the front of a
single linked list
The algorithm DeleteFront_SL is used to
delete the node at the front of a single linked list. Such a deletion operation
is explained in Figure 3.6(a).
Algorithm DeleteFront_SL
Input: HEADER is the pointer to the header node.
Output: A single linked list after eliminating the node at the front
of the list.
Data structures: A single linked list whose address of the starting node is
known from the HEADER.
Steps: 1. ptr = HEADER®LINK // Pointer to the first
node 2. If (ptr = NULL) then //
If the list is empty 3. Print “The list is empty: No deletion” 4. Exit // Quit
the program 5. Else // The list
is not empty 6. ptr1 = ptr®LINK // ptr1 is
the pointer to the second node, if any 7. HEADER®LINK = ptr1 // Next node becomes the
first node // as in Figure 3.6(a) 8. ReturnNode(ptr) //
Deleted node is freed to the memory bank for future use 9. EndIf 10. Stop |
Figure 5.6(a) Deleting
the node at the front of a single linked list.
Deleting the node at the end of a
single linked list
The algorithm DeleteEnd_SL is used to
delete the node at the end of a single linked list. This is shown in Figure
3.6(b).
Algorithm DeleteEnd_SL
Input: HEADER is the pointer to the header node.
Output: A single linked list after eliminating the node at
the end of the list.
Data structures: A single linked list whose address of the starting node is
known from the HEADER.
Steps: 1. ptr = HEADER
// Move from the header node 2. If (ptr®LINK = NULL) then 3. Print “The list is empty: No deletion possible” 4. Exit
// Quit the
program 5. Else 6. While (ptr®LINK
ą NULL) do // Go
to the last node 7. ptr1 = ptr
// To store the previous pointer 8. ptr = ptr®LINK
// Move to the next 9. EndWhile 10. ptr1®LINK = NULL
// Last but one node becomes the last node as in Figure 3.6(b) 11. ReturnNode(ptr) //
Deleted node is returned to the memory bank for future use 12. EndIf 13. Stop |
Figure 3.6(b) Deleting
the node at the end of a single linked list.
Deleting the node from any position
of a single linked list
The algorithm DeleteAny_SL is used to
delete a node from any position in a single linked list. This is illustrated in
Figure 3.6(c).
Algorithm DeleteAny_SL
Input: HEADER is the pointer to the header node, KEY is the
data content of the node to be deleted.
Output: A single linked list except the node with data content as KEY.
Data structures: A single linked list whose address of the starting node is
known from the HEADER.
Steps: 1. ptr1 = HEADER // Start
from the header node 2. ptr = ptr1®LINK // This
points to the first node, if any 3. While (ptr ą NULL) do 4. If (ptr®DATA ą KEY) then // If
not found the key 5. ptr1 = ptr // Keep a track
of the pointer of the previous node 6. ptr = ptr®LINK // Move to the next 7. Else 8. ptr1®LINK = ptr®LINK // Link field of the predecessor is to
point the
// successor of node under deletion, see Figure 3.6(c) 9. ReturnNode(ptr) // Return the deleted node to the
memory bank 10. Exit // Exit
the program 11. EndIf 12. EndWhile 13. If (ptr = NULL) then // When the desired node is
not available in the list 14. Print “Node with KEY does not exist: No
deletion” 15. EndIf 16. Stop |
Figure 3.6(c) Deleting
a node from any position of a single linked list.
Copying a single linked list
For a given list we can copy it into another list
by duplicating the content of each node into the newly allocated node. The
following is an algorithm to copy an entire single linked list.
Algorithm Copy_SL
Input: HEADER is the pointer to the header node of the list.
Output: HEADER1 is the pointer to the duplicate list.
Data structures: Single linked list structure.
Steps: 1. ptr = HEADER //
Current position in the master list 2. HEADER1 = GetNode(NODE) // Get a node for the
header of the duplicate list 3. ptr1 = HEADER1 // ptr1 is
the current position in the duplicated list 4. ptr1®DATA = NULL //
Header node does not contain any data 5. While (ptr ą NULL) do //
Till the traversal of master node is finished 6. new = GetNode(NODE) //
Get a new node from memory bank 7. new®DATA = ptr®DATA //
Copy the content 8. ptr1®LINK = new //
Insert the node at the end of the duplicate list 9. new®LINK = NULL 10. ptr1 = new 11. ptr = ptr®LINK //
Move to the next node in the master list 12. EndWhile 13. Stop |
Merging two single linked lists into
one list
Suppose two single linked lists, namely L1 and L2
are available and we want to merge the list L2 after L1. Also assume that, HEADER1
and HEADER2 are the header nodes of the lists L1 and L2, respectively.
Merging can be done by setting the pointer of the link field of the last node
in the list L1 with the pointer of the first node in L2. The header node in the
list L2 should be returned to the pool of free storage. Merging two single
linked lists into one list is illustrated in Figure 5.7.
Figure 5.7 Merging
two single linked lists into one single linked list.
Following
is the algorithm Merge_SL to merge two single
linked lists into one single linked list:
Algorithm Merge_SL
Input: HEADER1 and HEADER2 are the pointers to the
header nodes of lists (L1 and L2, respectively) to be merged.
Output: HEADER is the pointer to the resultant list.
Data structures: Single linked list structure.
Steps: 1. ptr = HEADER1 2. While (ptr®LINK ą NULL) do //
Move to the last node in the list L1 3. ptr = ptr®LINK 4. EndWhile 5. ptr®LINK = HEADER2®LINK // Last
node in L1 points to the first node in L2 6. ReturnNode(HEADER2) // Return the
header node to the memory bank 7. HEADER = HEADER1 //
HEADER becomes the header node of the merged list 8.
Stop |
Searching for an element in a single
linked list
The algorithm Search_SL( ) is given below to search an item in a single linked
list.
Algorithm Search_SL
Input: Key, the item to be searched.
Output: Location,
the pointer to a node where the KEY belongs to or an error message.
Data structures: A single linked list whose address of the starting node is
known from HEADER.
Step: 1. ptr = Header®Link //
Start from the first node 2. flag = 0, Location =
Null 3. While (ptr ą NULL) and (flag = 0) do 4. If (ptr®DATA
= KEY) then 5. flag = 1 //
Search is finished 6. Location
= ptr 7. Print “Search is successful” 8. Return (Location) 9. Else 10. ptr = ptr®LINK //
Move to the next node 11. EndIf 12. EndWhile 13. If (ptr = NULL) then 14. Print “Search is unsuccessful” 15. EndIf 16.
Stop |
Circular Linked List
In our previous discussion, we have noticed that in
a single linked list, the link field of the last node is null (hereafter a
single linked list may be read as ordinary linked list), but a number of
advantages can be gained if we utilize this link field to store the pointer of
the header node. A linked list where the last node points the header node is
called the circular linked list. Figure 5.8 shows a pictorial
representation of a circular linked list.
Figure 5.8 A
circular linked list.
Circular linked
lists have certain advantages over ordinary linked lists. Some advantages of
circular linked lists are discussed below:
Accessibility of a member node in
the list
In an ordinary list, a member node is accessible
from a particular node, that is, from the header node only. But in a circular
linked list, every member node is accessible from any node by merely chaining
through the list.
Example: Suppose, we are manipulating some information which
is stored in a list. Also, think of a case where for a given data, we want to
find the earlier occurrence(s) as well as post occurrence(s). Post
occurrence(s) can be traced out by chaining through the list from the current
node irrespective of whether the list is maintained as a circular linked or an
ordinary linked list.
In order
to find all the earlier occurrences, in case of ordinary linked lists, we have
to start our traversing from the header node at the cost of maintaining the
pointer for the header in addition to the pointers for the current node and
another for chaining. But in the case of a circular linked list, one can trace
out the same without maintaining the header information, rather maintaining
only two pointers. Note that in ordinary linked lists, one can chain through
left to right only whereas it is virtually in both the directions for circular
linked lists.
Null link problem
The null value in the link field may create some problem during the
execution of programs if proper care is not taken. This is illustrated below by
mentioning two algorithms to perform search on ordinary linked lists and
circular linked lists.
Algorithm Search_SL
Input: KEY the item to be searched.
Output: Location, the pointer to a node where KEY belongs or
an error message.
Data structures: A single linked list whose address of the starting node is
known from the HEADER.
Steps: 1. ptr = HEADER®LINK 2. While (ptr ą NULL) do 3. If (ptr®DATA ą KEY) then 4. ptr = ptr®LINK 5. Else 6. Print “Search is successful” 7. Return(ptr) 8. EndIf 9. EndWhile 10. If (ptr = NULL) then 11. Print “The entire list has traversed but KEY is not
found” 12. EndIf 13. Stop |
Note that in the algorithm Search_SL,
two tests in step 2 (which control the loop of searching) cannot be placed
together as While (ptr ą NULL) AND (ptr®DATA ą KEY) do because
in that case there will be an execution error for ptr®DATA since it is not defined when ptr
= NULL. But with a circular linked list very simple implementation is possible
without any special care for the NULL pointer. As an illustration the searching
of an element in a circular linked list is given below:
Algorithm Search_CL
Input: KEY the item of search.
Output: Location, the pointer to a node where KEY belongs or
an error message.
Data structures: A circular linked list whose address to the starting node
is known from the HEADER.
Steps: 1. ptr = HEADER®LINK 2. While (ptr®DATA ą KEY) and (ptr ą HEADER) do 3. ptr = ptr®LINK
4. EndWhile 5. If (ptr®DATA = KEY) 6. Return (ptr) 7. Else 8. Print “Entire list is searched: KEY node is not
found” 9. EndIf 10. Stop |
Some easy-to-implement operations
Some operations can more easily be implemented with a circular linked
list than with an ordinary linked list. Operations like merging
(concatenation), splitting (decatenation), deleting, disposing of an entire
list, etc. can easily be performed on circular linked list. The merging
operation, as in Figure 5.9, is explained in the algorithm Merge_CL
as follows:
Algorithm Merge_CL
Input: HEADER1 and HEADER2 are the two pointers to
header nodes.
Output: A larger circular linked list containing all the nodes from lists HEADER1
and HEADER2.
Data
structures: Circular
linked list structure.
Steps: 1. ptr1 = HEADER1®LINK 2. ptr2 = HEADER2®LINK 3. HEADER1®LINK = ptr2 //
Pointer assignment 1 (Figure 3.9) 4. While (ptr2®LINK ą HEADER2) do //
Move to the node just preceding the node HEADER2 5. ptr2 = ptr2®LINK 6. EndWhile 7. ptr2®LINK = ptr1 //
Pointer assignment 2 (Figure 3.9) 8. ReturnNode(HEADER2) // Return the
HEADER2 to the free storage pool 9. Stop |
Figure 5.9 Concatenation of two circular linked lists.
One can
easily compare the algorithm Merge_CL with the
algorithm Merge_SL. In the algorithm Merge_SL, the entire list is needed to be traversed
in order to locate the last node, which is not required in the algorithm Merge_CL. This implies that Meger_CL
works faster than Merge_SL.
Circular
linked lists have some disadvantages as well. One main disadvantage is that
without adequate care in processing, it is possible to get trapped into an
infinite loop! This problem occurs when we are unable to detect the end of the
list while moving from one node to the next. To get rid of this problem, we
have to maintain a special node whose data content is possibly NULL, as such a
node does not contain any valid information, so it is nothing but just a
wastage of memory space.
Double Linked Lists
In a single linked list one can move beginning from
the header node to any node in one direction only (from left to right). This is
why a single linked list is also termed a one-way list. On the other
hand, a double linked list is a two-way list because one can move in
either direction, either from left to right or from right to left. This is
accomplished by maintaining two link fields instead of one as in a single
linked list. A structure of a node for a double linked list is represented as
in Figure 5.10.
Figure 5.10 Structure
of a node and a double linked list.
From the figure, it can be noticed that two links,
viz. RLINK and LLINK, point to the nodes on the right side and left side of the
node, respectively. Thus, every node, except the header node and the last node,
points to its immediate predecessor and immediate successor.
Operations on a Double Linked
List
All the operations as mentioned for a single linked
list can be implemented more efficiently using a double linked list. In this
section, only the insertion and deletion operations are discussed. Other
operations like traversing, copying, merging, etc. are kept for the reader as
exercises.
Inserting a node into a double
linked list
Figure 5.11 shows a schematic representation of
various cases of inserting a node into a double linked list. Let us consider
the algorithms of various cases of insertion.
Inserting a node in the front
The algorithm InsertFront_DL is used to
define the insertion operation in a double linked list.
Algorithm InsertFront_DL
Input: X is the data content of the node to be inserted.
Output: A double linked list enriched with a node in the front
containing data X.
Data structure: Double linked list structure whose pointer to the header
node is the HEADER.
Steps: 1. ptr = HEADER®RLINK //
Points to the first node 2. new = GetNode(NODE) //
Avail a new node from the memory bank 3. If (new ą NULL) then //
If new node is available 4. new®LLINK = HEADER // Newly inserted node points the
header as 1 in Figure 3.11(a) 5. HEADER®RLINK = new //
Header now points to then new node as 2 in Figure 3.11(a) 6. new®RLINK = ptr // See the
change in pointer shown as 3 in Figure 3.11(a) 7. ptr®LLINK = new // See the
change in pointer shown as 4 in Figure 3.11(a) 8. new®DATA = X //
Copy the data into the newly inserted node 9. Else 10. Print “Unable to allocate memory: Insertion is
not possible” 11. EndIf 12. Stop |
Figure 5.11 Inserting
a node at various positions in a double linked list.
Inserting a node at the end
The algorithm InsertEnd_DL is to insert
a node at the end into a double linked list.
Algorithm InsertEnd_DL
Input: X is the data content of the node to be inserted.
Output: A double linked list enriched with a node containing data X
at the end of the list.
Data structure: Double linked list structure whose pointer to the
header node is the HEADER.
Steps: 1. ptr = HEADER 2. While (ptr®RLINK ą NULL) do //
Move to the last node 3. ptr = ptr®RLINK 4. EndWhile 5. new = GetNode(NODE) //
Avail a new node 6. If (new ą NULL) then //
If the node is available 7. new®LLINK = ptr // Change the
pointer shown as 1 in Figure 3.11(b) 8. ptr®RLINK = new // Change the
pointer shown as 2 in Figure 3.11(b) 9. new®RLINK = NULL //
Make the new node as the last node 10. new®.DATA = X //
Copy the data into the new node 11. Else 12. Print “Unable to allocate memory: Insertion is not
possible” 13. EndIf 14. Stop |
Inserting a node at any position in
the list
The algorithm InsertAny_DL is used to
insert a node at any position into a double linked list.
Algorithm InsertAny_DL
Input: X is the data content of the node to be inserted, and
KEY the data content of the node after which the new node is to be
inserted.
Output: A double linked list enriched with a node containing data X
after the node with data KEY, if KEY is not present in the list
then it is inserted at the end.
Data structure: Double linked list structure whose pointer to the header
node is the HEADER.
Steps: 1. ptr = HEADER 2. While (ptr®DATA ą KEY) and (ptr®RLINK ą NULL) //
Move to the key node if the //
current node is not the KEY node or if the list reaches the end 3. ptr = ptr®RLINK 4. EndWhile 5. new = GetNode(NODE) //
Get a new node from the pool of free storage 6. If (new = NULL) then //
When the memory is not available 7. Print (Memory is not available) 8. Exit //
Quit the program 9. EndIf 10. If (ptr®RLINK = NULL) then //
If the KEY is not found in the list 11. new®LLINK = ptr 12. ptr®RLINK = new //
Insert at the end 13. new®RLINK = NULL 14. new®DATA = X //
Copy the information to the newly inserted node 15. Else // The KEY
is available 16. ptr1 = ptr®RLINK //
Next node after the key node 17. new®LLINK = ptr //
Change the pointer shown as 2 in Figure 3.11(c) 18. new®RLINK = ptr1 //
Change the pointer shown as 4 in Figure 3.11(c) 19. ptr®RLINK = new //
Change the pointer shown as 1 in Figure 3.11(c) 20. ptr1®LLINK = new //
Change the pointer shown as 3 in Figure 3.11(c) 21. ptr = new //
This becomes the current node 22. new®DATA = X //
Copy the content to the newly inserted node 23. EndIf 24. Stop |
Note: Observe that the algorithm InsertAny_DL will
insert a node even the key node
does not exist. In that case, the node will be inserted at the end of the list.
Also, note that the algorithm will work even if the list is empty.
Deleting a node from a double linked
list
Deleting a node from a double linked list may take
place from any position in the list, as shown in Figure 5.12. Let us consider
each of those cases separately.
Deleting a node from the front of a
double linked list
The algorithm DeleteFront_DL is defined
below to delete a node from the front end of a double liked list.
Algorithm DeleteFront_DL
Input: A double linked list with data.
Output: A reduced double linked list.
Data structure: Double linked list structure whose pointer to the header node
is the HEADER.
Steps: 1. ptr = HEADER®RLINK //
Pointer to the first node 2. If (ptr = NULL) then //
If the list is empty 3. Print “List is empty: No deletion is made” 4. Exit 5. Else 6. ptr1 = ptr®RLINK //
Pointer to the second node 7. HEADER®RLINK = ptr1 //
Change the pointer shown as 1 in Figure 3.12(a) 8. If (ptr1 ą NULL) // If the
list contains a node after the first node of deletion 9. ptr1®LLINK =
HEADER //
Change the pointer shown as 2 in Figure 3.12(a) 10. EndIf 11. ReturnNode (ptr)
// Return the deleted node to the memory bank 12. EndIf 13. Stop |
Note that the algorithm DeleteFront_DL
works even if the list is empty.
Deleting a node at the end of a
double linked list
The algorithm is as follows:
Algorithm DeleteEnd_DL
Input: A double linked list with data.
Output: A reduced double linked list.
Data structure: Double linked list structure whose pointer to the header
node is the HEADER.
Steps: 1. ptr = HEADER 2. While (ptr®RLINK ą NULL) do //
Move to the last node 3. ptr = ptr®RLINK 4. EndWhile 5. If (ptr = HEADER) then //
If the list is empty 6. Print “List is empty: No deletion is made” 7. Exit //
Quit the program 8. Else 9. ptr1 = ptr®LLINK //
Pointer to the last but one node 10. ptr1®RLINK = NULL //
Change the pointer shown as 1 in Figure 3.12(b) 11. ReturnNode (ptr) //
Return the deleted node to the memory bank 12. EndIf 13. Stop |
Deleting a node from any position in
a double linked list
The algorithm is as follows:
Algorithm DeleteAny_DL
Input: A double linked list with data, and KEY, the data
content of the key node to be deleted.
Output: A double linked list without a node having data
content KEY, if any.
Data structure: Double linked list structure whose pointer to the header
node is the HEADER.
Steps: 1. ptr = HEADER®RLINK //
Move to the first node 2. If (ptr = NULL) then 3. Print “List is empty: No deletion is made” 4. Exit 5. EndIf // Quit the program 6. While (ptr®DATA ą KEY) and (ptr®RLINK ą NULL) do //
Move to the desired node 7. ptr = ptr®RLINK 8. EndWhile 9. If (ptr®DATA = KEY) then //
If the node is found 10. ptr1 = ptr®LLINK //
Track the predecessor node 11. ptr2 = ptr®RLINK //
Track the successor node 12. ptr1®RLINK = ptr2 //
Change the pointer shown as 1 in Figure 3.12(c) 13. If (ptr2 ą NULL) then //
If the deleted node is the last node 14. ptr2®LLINK =
ptr1 //
Change the pointer shown as 2 in Figure 3.12(c) 15. EndIf 16. ReturnNode(ptr) //
Return the free node to the memory bank 17. Else 18. Print “The node does not exist in the given list” 19. EndIf 20. Stop |
Figure 5.12 Deleting
a node from various positions in a double linked list.
Circular Double Linked List
The advantages of both double linked list and
circular linked list are incorporated into another type of list structure
called circular double linked list and it is known to be the best of its kind.
Figure 5.13 shows a schematic representation of a circular double linked list.
Figure 5.13 A
circular double linked list.
Here,
note that the RLINK (right link) of the rightmost node and LLINK (left link) of
the leftmost node contain the address of the header node; again the RLINK and
LLINK of the header node contain the address of the rightmost node and the
leftmost node, respectively. An empty circular double linked list is
represented as shown in Figure 5.14. In case of an empty list, both LLINK and
RLINK of the header node point to itself.
Figure 5.14 An
empty circular double linked list.
Operations on Circular Double
Linked List
All the regular operations like inserting,
deleting, traversing, searching, merging, splitting, disposing, etc. can be
implemented very easily with a circular linked list. Implementations of the
said operations are left as an exercise for the reader. Here, only the sorting
operation is illustrated.
Sorting operation with a circular
double linked list
The algorithm Sort_CDL shows the
sorting of elements stored in a circular double linked list.
Algorithm Sort_CDL( )
Input: A circularly double linked with elements.
Output: Sorted version of the circularly double linked list.
Data structures: Circular double linked list structure with HEADER
being the pointer to the header node.
Steps: 1. ptrBeg = HEADER®LLINK //
Pointer to the first node—the beginning node 2. ptrEnd = HEADER®RLINK //
Pointer to the last node — the ending node 3. While (ptrBeg ą ptrEnd) do // To traverse
the entire list — outer loop 4. ptr1 = ptrBeg // ptr1 and ptr2 are two variable pointers 5. ptr2 = ptr1®RLINK
6. While (ptr2 ą ptrEnd) do //
For compare and interchange — inner loop 7. If Order(ptr1®DATA, ptr2®DATA) = FALSE then 8. Swap (ptr1, ptr2) //
Interchange the data content at ptr1 and ptr2 9. EndIf 10. ptr1 = ptr1®RLINK //
Move the first pointer to the next 11. ptr2 = ptr2®RLINK //
Move the second pointer to the next 12. EndWhile 13. ptrEnd = ptrEnd®LLINK //
Rightmost node is now sorted out 14. EndWhile 15. Stop |
In the above algorithm, we have assumed the
procedure Order(data1, data2) to
test whether two data are in a desired order or not; it will return TRUE if
they are in order else FALSE. We also assume another procedure Swap(ptr1, ptr2) to interchange the
data content at the nodes pointed by the pointers ptr1 and ptr2.
These procedures are simple to implement and their implementation is left to
students.
The above
algorithm uses the bubble sorting technique. The execution of each outer loop
bubbles up the largest node towards the right end of sorting (say, in ascending
order) and each inner loop is used to compare the successive nodes and push up
the largest towards the right if they are not in order. Figure 3.15 illustrates
the sorting procedure. Students may see whether the algorithm Sort_CDL is also applicable to the double linked
list data structure or not.
Figure 5.15 Sorting
operation and use of various pointers.
Applications of Linked Lists
In order to store and process data, linked lists
are very popular data structures. These types of data structures hold certain
advantages over arrays. First, in the case of an array, data are stored in
contiguous memory locations, so insertion and deletion operations are quite
time-consuming. In insertion we have to make room for the new element at a
desired location by shifting down the trailing elements; similarly, in case of
deletion, in order to fill the location of the deleted element, all the
trailing elements are required to shift upwards. But, in linked lists, it is a
matter of only change in pointers. Second, an array is based on the static
allocation of memory: the amount of memory required for an array must be known
before hand, once it is allocated we cannot expand its size. This is why for an
array, the general practice is to allocate memory, which is much more than the
memory that actually will be used. But this is simply a wastage of memory
space. This problem is not there in linked lists. A linked list uses a dynamic
memory management scheme; memory allocation is decided during the run-time as
and when required. Also if a memory is no more required, it can be returned to
the free storage space, so that any other module or program can utilize it.
Third, a program using an array may not execute itself even though the memory
required for the data is available, but not in contiguous locations rather
dispersed. As link structures do not necessarily require to have data stored in
adjacent memory locations, so the program of that kind, using linked lists can
then be executed.
However,
there are of course some disadvantages: one is the pointer business. Pointers,
if not managed carefully, may lead to serious errors in execution. Next, linked
lists consume more space than the space required for actual data as we have to
maintain the links among the nodes. Frankly speaking, these drawbacks are
insignificant compared to gains achieved. This is why the use of this structure
is preferred, wherever it is required to manipulate data. In the next few
sections, we illustrate how linked lists can be applied in various
applications.
Sparse Matrix Manipulation
During the discussion on sparse matrices, it was
mentioned that linked lists are the best solution to store matrices. Let us
first decide what should be the node structure so that using that kind of node
we can represent any sparse matrix. Figure 5.16 shows a node structure for the
same.
In Figure
5.16, the fields i and j store the row
and column numbers for a matrix element, respectively. DATA field stores the
matrix element at the ith row and the jth column, i.e. aij. The ROWLINK points the next node in the same row
and COLLINK points the next node in the same column. The principle is that all
the nodes particularly in a row (column) are circular linked with each other;
each row (column) contains a header node. Thus, for a sparse matrix of order m
× n, we have to maintain m headers for all rows and n
headers for all columns, plus one extra node the use of which would be evident
from Figure 3.17(b). For an illustration, a sparse matrix of order 6 × 5 is
assumed, as shown in Figure 3517(a).
Figure 5.16 Structure
of a node to represent sparse matrices.
Figure 5.17(b)
describes the representation of a sparse matrix. Here, CH1, CH2, ..., CH5 are the 5 headers heading 5 columns and RH1, RH2,
..., RH6 are the 6 headers heading 6 rows. HEADER is one additional header node
keeping the starting address of the sparse matrix. Carefully observe the links
among various nodes and compare them with the sparse matrix assumed.
Figure 5.17 A
sparse matrix and its linked list representation.
Polynomial Representation
An important application of linked lists is to
represent polynomials and their manipulations. The main advantage of a linked
list for polynomial representation is that it can accommodate a number of
polynomials of growing sizes so that their combined size does not exceed the
total memory available. The methodology of representing polynomials and the
operations on them are discussed in this section. First, let us consider the case
of representation of polynomials.
Polynomial having a single variable
Let us consider the general form of a polynomial
having a single variable:
where is a term in the polynomial so that ai is a
non-zero coefficient and ei is the
exponent. we will assume an
ordering of the terms in the polynomial such that en > en–1 > ...
> e2 > e1 ł 0. The structure of a node in order to represent a term can be decided
as shown below:
Considering
the single linked list representation, a node should have three fields: COEFF
(to store the coefficient ai), EXP (to store the exponent ei) and a LINK (to store the pointer to the next node representing the
next term). It is evident that the number of nodes required to represent a
polynomial is the same as the number of terms in the polynomial. An additional
node may be considered for a header. As an example, let us consider that the
single linked list representation of the polynomial P(x) = 3x8 – 7x6 + 14x3 + 10x
– 5 would be stored as shown in Figure 5.18.
Figure 5.18 Linked list
representation of a polynomial (single variable).
Note that the terms whose coefficients are zero are
not stored here. Next let us consider two basic operations, namely the addition
and multiplication of two polynomials using this representation.
Polynomial addition
In order to add two polynomials, say P and Q, to get a resultant
polynomial R, we have to compare their terms starting at their first nodes and
moving towards the end one by one. Two pointers Pptr
and Qptr are used to move along the terms of P and Q.
There may arise three cases during the comparison between the terms of two
polynomials.
(i) Case 1: The exponents of
two terms are equal. In this case the coefficients in the two nodes are added
and a new term is created with the values
Rptr®COEFF
= Pptr®COEFF + Qptr®COEFF
and
Rptr®EXP
= Pptr®EXP
(ii) Case
2: Pptr®EXP > Qptr®EXP,
i.e. the exponent of the current term in P is greater than the exponent of the
current term in Q. Then, a duplicate of the current term in P is created and
inserted in the polynomial R.
(iii) Case
3: Pptr®EXP < Qptr®.EXP, i.e. the case when the exponent of the current term in P is less
than the exponent of the current term in Q. In this case, a duplicate of the
current term of Q is created and inserted in the polynomial R.
Polynomial multiplication
Suppose, we have to multiply two polynomials P and
Q so that the result will be stored in R, another polynomial. The method is
quite straightforward: let Pptr denote the current
term in P and Qptr be that of in Q. For each term of
P we have to visit all the terms in Q; the exponent values in two terms are
added (R®EXP = P®EXP + Q®EXP), the coefficient values are multiplied (R®COEFF = P®COEFF × Q®COEFF),
and these values are included into R in such a way that if there is no term in
R whose exponent value is the same as the exponent value obtained by adding the
exponents from P and Q, then create a new node and insert it to R with
the values so obtained (that is, R®COEFF,
and R®EXP); on the other hand, if a node is found in R
having same exponent value R®EXP, then update the coefficient value of it by
adding the resultant coefficient (R®COEFF)
into it.
Polynomials having multiple
variables
So far we have considered the case of a polynomial
of a single variable. The idea now can be extended to represent any polynomial
with two variables, three variables, and so on. Below is a structure of a node
that will be suitable to represent a polynomial with three variables x, y
and z using a single linked list.
Writing procedures to manipulate such polynomials
is as simple as the earlier procedures for polynomials with single variables.
These are left as an assignment to the reader.
The LinkedList class
The LinkedList class is another class member in the Java Collections Framework to support sequential access of a list of items unlike ArrayList, which provides indexed access. The class hierarchy of the LinkedLust class is shown in Fig. 5.19. It inherits the AbstractSequentialList class and implements the List and Deque interfaces.
Figure 5.19: The class implementation hierarchy of LinkedList class
Following are the few salient features of this collection.
·
It provides a linked-list data
structure
·
The class can contain duplicate elements.
·
The class uses a doubly linked
list to store the elements.
·
The class maintains insertion order.
·
The class is non-synchronized.
·
In Java LinkedList class, manipulation is fast
because no shifting needs to occur.
·
Java LinkedList class can be used as a list,
stack or queue.
Constructors in the LinkedList class
The LinkedList class consists of two constructors, which are
summarized in Table 5.1.
Constructor |
Description |
LinkedList() |
It is used to create an empty
list. |
LinkedList(Collection<?
extends E> c) |
It is used to construct a list
containing the elements of the specified collection. The ordering of the
element in the list and collection is same. |
Table 5.1:
Constructors in LinkedList class
Methods in the LinkedList class
A large number of methods are there in the class LinkedList, which
are briefly mentioned in Table 5.2.
Method |
Description |
boolean add(E e) |
It is used to append the
specified element to the end of a list. |
void add(int index, E element) |
It is used to insert the
specified element at the specified position index in a list. |
boolean addAll(Collection<?
extends E> c) |
It is used to append all of the
elements in the specified collection to the end of this list, in the order that
they are returned by the specified collection's iterator. |
boolean addAll(int
index, Collection<? extends E> c) |
It is used to append all the
elements in the specified collection, starting at the specified position of
the list. |
void addFirst(E
e) |
It is used to insert the given
element at the beginning of a list. |
void addLast(E
e) |
It is used to append the given
element to the end of a list. |
void clear() |
It is used to remove all the
elements from a list. |
Object clone() |
It is used to return a shallow
copy of an ArrayList. |
boolean contains(Object o) |
It is used to return true if a
list contains a specified element. |
Iterator<E> descendingIterator() |
It is used to return an iterator
over the elements in a deque in reverse sequential order. |
E element() |
It is used to retrieve the first
element of a list. |
E get(int index) |
It is used to return the element
at the specified position in a list. |
E getFirst() |
It is used to return the first
element in a list. |
E getLast() |
It is used to return the last
element in a list. |
int indexOf(Object
o) |
It is used to return the index
in a list of the first occurrence of the specified element, or -1 if the list
does not contain any element. |
int lastIndexOf(Object
o) |
It is used to return the index
in a list of the last occurrence of the specified element, or -1 if the list
does not contain any element. |
ListIterator<E> listIterator(int
index) |
It is used to return a
list-iterator of the elements in proper sequence, starting at the specified position
in the list. |
boolean offer(E e) |
It adds the specified element as
the last element of a list. |
boolean offerFirst(E
e) |
It inserts the specified element
at the front of a list. |
boolean offerLast(E
e) |
It inserts the specified element
at the end of a list. |
E peek() |
It retrieves the first element
of a list |
E peekFirst() |
It retrieves the first element
of a list or returns null if a list is empty. |
E peekLast() |
It retrieves the last element of
a list or returns null if a list is empty. |
E poll() |
It retrieves and removes the
first element of a list. |
E pollFirst() |
It retrieves and removes the
first element of a list, or returns null if a list is empty. |
E pollLast() |
It retrieves and removes the
last element of a list, or returns null if a list is empty. |
E pop() |
It pops an element from the
stack represented by a list. |
void push(E e) |
It pushes an element onto the
stack represented by a list. |
E remove() |
It is used to retrieve and
removes the first element of a list. |
E remove(int index) |
It is used to remove the element
at the specified position in a list. |
boolean remove(Object o) |
It is used to remove the first
occurrence of the specified element in a list. |
E removeFirst() |
It removes and returns the first
element from a list. |
boolean removeFirstOccurrence(Object
o) |
It is used to remove the first
occurrence of the specified element in a list (when traversing the list from
head to tail). |
E removeLast() |
It removes and returns the last
element from a list. |
boolean removeLastOccurrence(Object
o) |
It removes the last occurrence
of the specified element in a list (when traversing the list from head to
tail). |
E set(int index, E element) |
It replaces the element at the
specified position in a list with the specified element. |
Object[] toArray() |
It is used to return an array
containing all the elements in a list in proper sequence (from first to the
last element). |
<T> T[] toArray(T[] a) |
It returns an array containing
all the elements in the proper sequence (from first to the last element); the
runtime type of the returned array is that of the specified array. |
int size() |
It is used to return the number
of elements in a list. |
Table 5.2: Methods defined in the LinkedList class
The following operations with LinkedList are usually frequent and mentioned below.
· Creating a linked-list
· Traversing a linked-list
· Insertion of an item in a linked-list
· Deletion of an item from a linked-list
All these four operations are discussed with a number of example programs.
Creating
linked lists
Example 5.1:
This program shows how to create an empty linked list and then to store name of a set of cities in it.
import java.util.*;
public class CreateEmptyLLandAddItems
{
public static void main(String
args[]) {
// Creating an empty ll of class LinkedList
LinkedList<String> ll = new LinkedList<String>();
//
Adding elements to the linked list using a number of
add methods
ll.add("Mumbai");
ll.add("Chennai");
ll.add("Kolkata");
ll.add("Delhi");
ll.add("Bangalore");
ll.add("Guwahati");
ll.add("Hyderabad");
// System.out.println("Linked list : " + ll); // Simple printing
// Printing the list using an iterator
Iterator<String> itr = ll.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
}
Example 5.2:
First, let us consider the declaration and definition of a user defined class Student. This program shows how to create an array of objects of type Students and then add the array into a linked list and then printing the same.
import java.util.*;
//Declaration of a user defined
class
class Student {
String name;
Float marks;
// Constructor
Student(String s, flaot
m) {
name = s;
marks = m;
}
// To parant
a reacord
void printData
() {
System.out.print(“Name : “ + name);
System.out.println(“
Marks : “ + marks);
}
}
// The main class is defined
below.
public class CreateLLofCollection
{
public static void main(String
args[]) {
// Create an aaray of objects
Student sArray =
new sArray[5];
// To store 5 objects
// Create the array sArray
sArray[0] = new Student(“Ram”, 79.6);
sArray[1] = new Student(“Rahim”, 85.5);
sArray[2] = new Student(“John”, 90.1);
sArray[3] = new Student(“Lisa”, 69.4);
sArray[4] = new Student(“Ana”, 59.8);
// Creating a linked-list with sArray
collection
LinkedList<Student> sList =
new LinkedList<Student>(sArray);
Student temp;
// Printing the list using an iterator
Iterator<String> itr = ll.iterator();
while(itr.hasNext()){
temp = itr.next();
temp.printData(); //
Print the current record.
}
}
}
Insertion of elements into a
linked list.
There are many ways that an item can
be inserted into a linked list.
Example 5.3.
This program shows how items can be
inserted at different locations in a linked list. For this purpose, there are
methods like add(), addFirst(),
addLast() are defined in the LinkedList class.
import java.util.*;
public class LLinsertionDemo
{
public static void main(String
args[]) {
// Creating an empty ll of class LinkedList
LinkedList<String> ll = new LinkedList<String>();
//
Adding elements to the linked list using a number of
add methods
ll.add("Mumbai");
// Add an initial item
ll.add("Chennai");
// Add another item
ll.addLast("Kolkata");
// Add at the end
ll.addFirst("Delhi");
// Add at the front
ll.add(2,
"Bangalore"); // Add in the
specific loacation
ll.add("Guwahati");
// Sequential add goes at the end
ll.add("Hyderabad");
// Another sequential insertrtion
// System.out.println("Linked list : " + ll); // Simple printing
// Printing the list using an iterator
Iterator<String> itr = ll.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
}
Example 5.4.
A sublist
can be inserted into a linked list in adition to a
single item. This program shows how a sublist can be
inserted at different locations in a linked list. For this purpose, the addAll()
method is used.
import java.util.*;
public class InsertSubListToLL{
public static void main(String args[]){
LinkedList<String> ll1=new
LinkedList<String>();
System.out.println("Initial list of elements:
"+ll);
Ll1.add("MP Allahabad");
Ll1.add("MP Lucknow");
Ll1.add("MP Varanasi");
System.out.println("Initia;
list: "+ll);
// Create
another linked list, say ll2
LinkedList<String> ll2 = new
LinkedList<String>();
ll2.add("MLA
Nadia");
ll2.add("MLA
Kharagpur");
//Adding second list ll2 to the
first list ll1
Ll1.addAll(ll2);
System.out.println("After adding ll2 to ll1: "+ll1);
// Create
another linked list, say ll3
LinkedList<String> ll3 = new
LinkedList<String>();
ll3.add("MLA Durgapur");
ll3.add("MLA Howrah");
// Inserrt
ll3 at a specific position of ll1
ll1.addAll(3, ll3); //
Insert ll3 at location 3 of ll1
System.out.println("After insetting ll3: "+ll1);
//Do some normal insertions
Ll1.addFirst("President");
ll1.addLast("Prime Minister");
ll1.add(“MP Chennai”);
System.out.println("The final list "+ll1);
}
}
// Note: Index of a
linked list starts from 0, that is, the first item at location 0
Deletion of elements from a
linked list.
Like insertion, deletion operation on
a linked list can be carried our many ways. Following few examples illustrates
the deletion operation with methods remove(), removeFirst(), removeLast(), etc.
Example 5.5.
This program shows how items can be
inserted at different locations in a linked list. For this purpose, there are
methods like add(), addFirst(),
addLast() are defined in the LinkedList class.
import java.util.*;
public class DeletionFromLL
{
public
static void main(String [] args) {
// Creating a
linked list
LinkedList<String> ll
= new LinkedList<String>();
ll1.add("A");
ll1.add("E");
ll1.add("I");
ll1.add("O");
ll1.add("U");
ll1.add("H");
System.out.println("List of vowels: "+ll);
//Removing specific element from the
linked list
ll1.remove("H"); // Rmovee the vowel H
System.out.println("After deletion of H :
"+ll1);
//Removing element on the basis of
specific position
lll.remove(0);
// This will remove A from the
list
System.out.println("After invoking remove(index)
method: "+ll1);
// Let’s
create another list of semi-vowels
LinkedList<String> ll2=new
LinkedList<String>();
ll2.add("M");
ll2.add("N");
// Adding
new elements to the list of vowels
ll1.addAll(ll2); // Append ll2 after ll1
System.out.println("Updated list : "+ll);
//Removing first element from the
list
Ll1.removeFirst();
System.out.println("After invoking removeFirst() method: "+ll);
//Removing last element from the
list
Ll1.removeLast();
System.out.println("After invoking removeLast() method: "+ll);
// Removing
all elements from ll2
Ll1.removeAll(ll2);
System.out.println("After removing semi-vowels:
"+ll1);
ll1.add(“A”);
ll1.add(”“B”);
ll1.add(“A”);
//Removing first occurrence of
element from the list
Ll1.removeFirstOccurrence("A");
System.out.println("After removing first
occurrence of A: "+ll1);
//Removing the last occurrence of B
ll1.removeLastOccurrence("B");
System.out.println("After invoking removeLastOccurrence() method: "+ll1);
//Removing all the elements
available in the list
ll1.clear();
System.out.println("After invoking clear() method:
"+ll1);
}
}
Traversing a linked list
We have learned
how to print a linked-list in sequential order starting from the first item in
the list. The LinkedList class allow you to traverse a linked in reverse order
as well. For this purpose, you should use the method descendingIterator(). This
can be applied to a list storing of any type of items. The following program
illustrates how to traverse two different type of lists in reverse order, that
is, from the end to the front.
Example 5.6.
import java.util.*;
public class TravserseReverseLL{
public static void main(String args[]){
// Case 1: a linked list of countries
LinkedList<String> lCountries
= new LinkedList<String>();
lCountries.add("Australia");
lCountries.add("India");
lCountries.add("South Africa");
lCountries.add("Zimbabwe");
//Traversing the list of countries in reverse order
Iterator itr1
=lCountries.descendingIterator();
while(itr1.hasNext()) {
System.out.println(itr1.next());
}
// Case 2: a linked list of numbers
LinkedList<Integer> lNumbers
= new LinkedList<Integer>();
lNumbers.add(123);
lNumbers.add(345);
lNumbers.add(567);
lNumbers.add(789);
//Traversing the list of numbers in reverse order
Iterator itr2
= lNumbers.descendingIterator();
while(itr2.hasNext()) {
System.out.println(itr2.next());
}
}
}
Some miscellaneous operations
with linked list
The LinkedList class is loaded with several other
methods like get(), contain(), size(), set(), etc. The following program
illustrates those methods and their utilities in Java programming.
Example 5.7.
import java.util.*;
public class OtherMethodsOfLL{
public static void main(String args[]){
// Creating a
linked list
LinkedList<String> lLetters
= new LinkedList<String>();
lLetters.add("J");
lLetters.add("O");
lLetters.add("Y");
lLetters.add("W");
lLetters.add("I");
lLetters.add("T");
lLetters.add("H");
lLetters.add("J");
lLetters.add("A");
lLetters.add("V");
lLetters.add("A");
lLetters.add(2020);
System.out.println("List : "+ll);
//
Finding an elements in the linked list
boolean status = lLetters.contains("Z");
if(status)
System.out.println("List contains the element 'Z' ");
else
System.out.println("List doesn't contain 'Z'");
// Finding the number of elements in the linked
list
int size = lLetters.size();
System.out.println("Number of letters = " + size);
// Get and set elements from the linked list
lLetters
element = lLetters.get(11);
System.out.println("Element returned by get() : " +
element);
lLetters.set(11, "The fun");
System.out.println("Linked list after change : " + lLetters);
}
}
A linked list with user defined
class
In the following, a program is shown,
which list a number of books, insertion of another books in the list, deletion
of books and then traversing of the books, etc..
Example 5.8.
import java.util.*;
//
Defining a calls Book
class Book {
int accnNo;
String title, author, publisher;
float cost;
// Constructor of the class
Book(int id, String t, String auth, String pub, float val) {
accnNo = id;
title = t;
author = aut;
publisher = pub;
cost = val;
}
}
//
The main class maintaining a library of books
public class LibraryLL {
public static void main(String[] args) {
//Creating list of Books
List<Book> library
= new LinkedList<Book>();
//Creating Books
Book b1
= new Book(101,"Oracle Java","Leslie
Lamport","Oxford",88.5);
Book b2
= new w Book(102,"Complete Java","McGraw
Hill”,94);
Book b3
= new Book(103,"Joy with Java","Samanta","Prentice Hall",69.6);
//Adding Books to list
library.add(b1);
library.add(b2);
library.add(b3);
//Traversing the list
for(Book b:library){
System.out.println(“Book ID: “ + b.accnNo);
System.out.print(b.title+" "
+ b.author+" "+b.publisher+" "+b.cost);
System.out.println();
}
}
}