Module 3: Linked List: Data Structures and Applications (15CS33)
Module 3: Linked List: Data Structures and Applications (15CS33)
Module 3: Linked List: Data Structures and Applications (15CS33)
DEFINITION
A linked list, or one-way list, is a linear collection of data elements, called nodes, where the
linear order is given by means of pointers. That is, each node is divided into two parts:
The first part contains the information of the element, and
The second part, called the link field or nextpointer field, contains the address of the
next node in the list.
A pointer variable called START or FIRST which contains the address of the first node.
A special case is the list that has no nodes, such a list is called the null list or empty list and is
denoted by the null pointer in the variable START.
Let LIST be a linked list. Then LIST will be maintained in memory as follows.
1. LIST requires two linear arrays such as INFO and LINK-such that INFO[K] and
LINK[K] contains the information part and the nextpointer field of a node of LIST.
2. LIST also requires a variable name such as START which contains the location of the
beginning of the list, and a nextpointer sentinel denoted by NULL-which indicates the
end of the list.
3. The subscripts of the arrays INFO and LINK will be positive, so choose NULL = 0,
unless otherwise stated.
The following examples of linked lists indicate that the nodes of a list need not occupy adjacent
elements in the arrays INFO and LINK, and that more than one list may be maintained in the
same linear arrays INFO and LINK. However, each list must have its own pointer variable
giving the location of its first node.
START=9 INFO[9]=N
LINK[3]=6 INFO[6]=V
LINK[6]=11 INFO[11]=E
LINK[11]=7 INFO[7]= X
LINK[7]=10 INFO[10]= I
LINK[10]=4 INFO[4]= T
LINK[4]= NULL value, So the list has ended
REPRESENTING CHAIN IN C
The maintenance of linked lists in memory assumes the possibility of inserting new
nodes into the lists and hence requires some mechanism which provides unused memory
space for the new nodes.
Mechanism is required whereby the memory space of deleted nodes becomes available
for future use.
Together with the linked lists in memory, a special list is maintained which consists of
unused memory cells. This list, which has its own pointer, is called the list of available
space or the free storage list or the free pool.
Suppose linked lists are implemented by parallel arrays and insertions and deletions are to be
performed linked lists. Then the unused memory cells in the arrays will also be linked together
to form a linked list using AVAIL as its list pointer variable. Such a data structure will be
denoted by
LIST (INFO, LINK, START, AVAIL)
Garbage Collection
Suppose some memory space becomes reusable because a node is deleted from a list or
an entire list is deleted from a program. So space is need to be available for future use.
One way to bring this is to immediately reinsert the space into the free-storage list.
However, this method may be too time-consuming for the operating system of a
computer, which may choose an alternative method, as follows.
The operating system of a computer may periodically collect all the deleted space onto the
freestorage list. Any technique which does this collection is called garbage collection.
Garbage collection takes place in two steps.
1. First the computer runs through all lists, tagging those cells which are currently in use
2. And then the computer runs through the memory, collecting all untagged space onto the
free-storage list.
The garbage collection may take place when there is only some minimum amount of space or
no space at all left in the free-storage list, or when the CPU is idle and has time to do the
collection.
Overflow
Sometimes new data are to be inserted into a data structure but there is no available
space, i.e., the free-storage list is empty. This situation is usually called overflow.
The programmer may handle overflow by printing the message OVERFLOW. In such a
case, the programmer may then modify the program by adding space to the underlying
arrays.
Overflow will occur with linked lists when AVAIL = NULL and there is an insertion.
Underflow
The term underflow refers to the situation where one wants to delete data from a data
structure that is empty.
The programmer may handle underflow by printing the message UNDERFLOW.
The underflow will occur with linked lists when START = NULL and there is a deletion.
Algorithm: (Traversing a Linked List) Let LIST be a linked list in memory. This algorithm
traverses LIST, applying an operation PROCESS to each element of LIST.
The variable PTR points to the node currently being processed.
1. Set PTR = START
2. Repeat Steps 3 and 4 while PTR ≠ NULL
3. Apply PROCESS to PTR→INFO
4. Set PTR = PTR→LINK
5. Exit.
Example:
The following procedure prints the information at each node of a linked list. Since the procedure
must traverse the list.
Let LIST be a linked list in memory. Suppose a specific ITEM of information is given.
If ITEM is actually a key value and searching through a file for the record containing ITEM,
then ITEM can appear only once in LIST.
LIST Is Unsorted
Suppose the data in LIST are not sorted. Then search for ITEM in LIST by traversing through
the list using a pointer variable PTR and comparing ITEM with the contents PTR→INFO of
each node, one by one, of LIST. Before updating the pointer PTR by
PTR = PTR→LINK
It requires two tests.
First check whether we have reached the end of the list, i.e.,
PTR == NULL
If not, then check to see whether
PTR→INFO == ITEM
The complexity of this algorithm for the worst-case running time is proportional to the
number n of elements in LIST, and the average-case running time is approximately
proportional to n/2 (with the condition that ITEM appears once in LIST but with equal
probability in any node of LIST).
LIST is Sorted
Suppose the data in LIST are sorted. Search for ITEM in LIST by traversing the list using a
pointer variable PTR and comparing ITEM with the contents PTR→INFO of each node, one
by one, of LIST. Now, searching can stop once ITEM exceeds PTR→INFO.
The complexity of this algorithm for the worst-case running time is proportional to the number
n of elements in LIST, and the average-case running time is approximately proportional to n/2
Let LIST be a linked list with successive nodes A and B, as pictured in Fig. (a). Suppose a node
N is to be inserted into the list between nodes A and B. The schematic diagram of such an
insertion appears in Fig. (b). That is, node A now points to the new node N, and node N points
to node B, to which A previously pointed.
The above figure does not take into account that the memory space for the new node N will
come from the AVAIL list.
Specifically, for easier processing, the first node in the AVAIL list will be used for the new
node N. Thus a more exact schematic diagram of such an insertion is that in below Fig.
Insertion Algorithms
Algorithms which insert nodes into linked lists come up in various situations.
1. Inserts a node at the beginning of the list,
2. Inserts a node after the node with a given location
3. Inserts a node into a sorted list.
The following is an algorithm which inserts ITEM into LIST so that ITEM follows node A or,
when LOC = NULL, so that ITEM is the first node.
Let N denote the new node. If LOC = NULL, then N is inserted as the first node in LIST.
Otherwise, let node N point to node B by the assignment NEW→LINK:= LOC→LINK and let
node A point to the new node N by the assignment LOC→LINK:= NEW
1. [List empty?] If START = NULL, then: Set LOC: = NULL, and Return.
2. [Special case?] If ITEM < START→INFO, then: Set LOC: = NULL, and Return.
3. Set SAVE: = START and PTR: = START→LINK. [Initializes pointers.]
4. Repeat Steps 5 and 6 while PTR ≠ NULL.
5. If ITEM < PTR→INFO, then:
Set LOC: = SAVE, and Return.
[End of If structure.]
6. Set SAVE: = PTR and PTR: = PTR→LINK. [Updates pointers.]
[End of Step 4 loop.]
7. Set LOC: = SAVE.
8. Return.
Below algorithm which inserts ITEM into a linked list. The simplicity of the algorithm comes
from using the previous two procedures.
Let LIST be a linked list with a node N between nodes A and B, as pictured in below
Fig.(a). Suppose node N is to be deleted from the linked list. The schematic diagram of
such a deletion appears in Fig.(b).
The deletion occurs as soon as the nextpointer field of node A is changed so that it
points to node B.
Linked list is maintained in memory in the form
LIST (INFO, LINK, START, AVAIL)
The above figure does not take into account the fact that, when a node N is deleted from our
list, immediately return its memory space to the AVAIL list. So for easier processing, it will
be returned to the beginning of the AVAIL list. Thus a more exact schematic diagram of suc h
a deletion is the one in below Fig.
1. The difficulties with single linked lists is that, it is possible to traversal only in one
direction, ie., direction of the links.
2. The only way to find the node that precedes p is to start at the beginning of the list. The
same problem arises when one wishes to delete an arbitrary node from a singly linked
list. Hence the solution is to use doubly linked list
Doubly linked list: It is a linear collection of data elements, called nodes, where each node N
is divided into three parts:
1. An information field INFO which contains the data of N
2. A pointer field LLINK (FORW) which contains the location of the next node in the list
3. A pointer field RLINK (BACK) which contains the location of the preceding node in
the list
A header linked list is a linked list which contains a special node, called the header node, at the
beginning of the list.
The following are two kinds of widely used header lists:
1. A grounded header list is a header list where the last node contains the null pointer.
2. A circular header list is a header list where the last node points back to the header node.
Observe that the list pointer START always points to the header node.
If START→LINK = NULL indicates that a grounded header list is empty
If START→LINK = START indicates that a circular header list is empty.
The first node in a header list is the node following the header node, and the location of the first
node is START→LINK, not START, as with ordinary linked lists.
Below algorithm, which uses a pointer variable PTR to traverse a circular header list
1. Begins with PTR = START→LINK (not PTR = START)
2. Ends when PTR = START (not PTR = NULL).
Algorithm: (Traversing a Circular Header List) Let LIST be a circular header list in memory.
This algorithm traverses LIST, applying an operation PROCESS to each node of LIST.
1. Set PTR: = START→LINK. [Initializes the pointer PTR.]
2. Repeat Steps 3 and 4 while PTR ≠ START:
3. Apply PROCESS to PTR→INFO.
4. Set PTR: = PTR→LINK. [PTR now points to the next node.]
[End of Step 2 loop.]
5. Exit.
Linked Stack
The representation of n ≤ MAX_STACKS stacks, below is the declarations:
Functions push and pop add and delete items to/from a stack.
Function push creates a new node, temp, and places item in the data field and top in the link
field. The variable top is then changed to point to temp. A typical function call to add an element
to the ith stack would be push (i,item).
element pop(int i)
{ /* remove top element from the ith stack */
stackPointer temp = top[i];
element item;
if (! temp)
return stackEmpty();
item = temp→data;
top[i] = temp→link;
free (temp) ;
return item;
}
Program: Delete from a linked stack
Function pop returns the top element and changes top to point to the address contained in its
link field. The removed node is then returned to system memory. A typical function call to
delete an element from the ith stack would be item = pop (i);
Linked Queue
The representation of m ≤ MAX_QUEUES queues, below is the declarations:
#define MAX-QUEUES 10 /* maximum number of queues */
typedef.struct queue *queuePointer;
typedef struct {
element data;
queuePointer link;
} queue;
queuePointer front[MAX_QUEUES], rear[MAX_QUEUES];
Functions addq and deleteq implement the add and delete operations for multiple queues.
if (front[i])
rear[i] →link = temp;
else
front[i] = temp;
rear[i] = temp;
}
Program: Add to the rear of a linked queue
Function addq is more complex than push because we must check for an empty queue. If the
queue is empty, then change front to point to the new node; otherwise change rear's link field
to point to the new node. In either case, we then change rear to point to the new node.
element deleteq(int i)
{ /* delete an element from queue i */
queuePointer temp = front[i];
element item;
if (! temp)
return queueEmpty();
item = temp→data;
front[i]= temp→link;
free (temp) ;
return item;
}
Program: Delete from the front of a linked queue
Function deleteq is similar to pop since nodes are removing that is currently at the start of the
list. Typical function calls would be addq (i, item); and item = deleteq (i);
where the ai are nonzero coefficients and the e i are nonnegative integer exponents such that
em-l > e m-2 > ... > e1 > e0 ≥ 0.
Present each term as a node containing coefficient and exponent fields, as well as a pointer to
the next term.
Assuming that the coefficients are integers, the type declarations are:
Adding Polynomials
To add two polynomials, examine their terms starting at the nodes pointed to by a and b.
If the exponents of the two terms are equal, then add the two coefficients and cre ate a
new term for the result, and also move the pointers to the next nodes in a and b.
If the exponent of the current term in a is less than the exponent of the current term in
b, then create a duplicate term of b, attach this term to the result, called c, and advance
the pointer to the next term in b.
If the exponent of the current term in b is less than the exponent of the current term in
a, then create a duplicate term of a, attach this term to the result, called c, and advance
the pointer to the next term in a
Analysis of padd:
To determine the computing time of padd, first determine which operations contribute to the
cost. For this algorithm, there are three cost measures:
(l) Coefficient additions
(2) Exponent comparisons
(3) Creation of new nodes for c
In data representation, each column of a sparse matrix is represented as a circularly linked list
with a header node. A similar representation is used for each row of a sparse matrix.
Each node has a tag field, which is used to distinguish between header nodes and entry nodes.
Header Node:
Each header node has three fields: down, right, and next as shown in figure (a).
The down field is used to link into a column list and the right field to link into a row list.
The next field links the header nodes together.
The header node for row i is also the header node for column i, and the total number of
header nodes is max {number of rows, number of columns}.
Element node:
Each element node has five fields in addition in addition to the tag field: row, col, down,
right, value as shown in figure (b).
The down field is used to link to the next nonzero term in the same column and the right
field to link to the next nonzero term in the same row. Thus, if aij ≠ 0, there is a node
with tag field = entry, value = aij, row = i, and col = j as shown in figure (c).
We link this node into the circular linked lists for row i and column j. Hence, it is
simultaneously linked into two different lists.
Figure (3) shows the linked representation of this matrix. Although we have not shown the
value of the tag fields, we can easily determine these values from the node structure.
For each nonzero term of a, have one entry node that is in exactly one row list and one column
list. The header nodes are marked HO-H3. As the figure shows, we use the right field of the
header node list header to link into the list of header nodes.
To represent a numRows x numCols matrix with numTerms nonzero terms, then we need max
{numRows, numCols} + numTerms + 1 nodes. While each node may require several words of
memory, the total storage will be less than numRows x numCols when numTerms is
sufficiently small.
There are two different types of nodes in representation, so unions are used to create the
appropriate data structure. The C declarations are as follows:
typedef struet {
int row;
int eol;
int value;
} entryNode;
typedef struet {
matrixPointer down;
matrixPointer right;
tagfield tag;
union {
matrixPointer next;
entryNode entry;
} u;
} matrixNode;
matrixPointer hdnode[MAX-SIZE];