Linked Lists Yay
Linked Lists Yay
From Data Structures and Algorithms (Alfred V. Aho, John E. Hopcroft, Jeffrey D. Ullman)
Our second implementation of lists, singly-linked cells, uses pointers to link successive list elements.
This implementation frees us from using contiguous memory for storing a list and hence from
shifting elements to make room for new elements or to close up gaps created by deleted elements.
However, one price we pay is extra space for pointers.
In this representation, a list is made up of cells, each cell consisting of an element of the list and a
pointer to the next cell on the list. If the list is a1, a2, . . . , an, the cell holding a i has a pointer to
the cell holding a i+1, for i = 1, 2 , . . . , n-1. The cell holding an has a nil pointer. There is also a
header cell that points to the cell holding a1; the header holds no element. † In the case of an empty
list, the header's pointer is nil, and there are no other cells. Figure 2.4 shows a linked list of this
form.
For singly-linked lists, it is convenient to use a definition of position that is somewhat different than
the definition of position in an array implementation. Here, position i will be a pointer to the cell
holding the pointer to ai for i = 2, 3 , . . . , n. Position 1 is a pointer to the header, and position
END(L) is a pointer to the last cell of L.
The type of a list happens to be the same as that of a position -- it is a pointer to a cell, the header
in particular. We can formally define the essential parts of a linked list data structure as follows.
type
celltype = record
element: elementtype;
next: celltype
end;
LIST = celltype;
position = celltype;
The function END(L) is shown in Fig. 2.5. It works by moving pointer q down the list from the
header, until it reaches the end, which is detected by the fact that q points to a cell with a nil
pointer. Note that this implementation of END is inefficient, as it requires us to scan the entire list
every time we need to compute END(L). If we need to do so frequently, as in the PURGE program
of Fig. 2.i, we could either
1. Use a representation of lists that includes a pointer to the last cell, or
2. Replace uses of END(L) where possible. For example, the condition p <> END(L) in line
(2) of Fig. 2.1 could be replaced by p . next <> nil, at a cost of making that program dependent
on one particular implementation of lists.
Figure 2.6 contains routines for the four operations INSERT, DELETE, LOCATE, and MAKENULL
using this pointer implementation of lists. The other operations can be implemented as one-step
routines, with the exception of PREVIOUS, which requires a scan of the list from the beginning. We
leave these other routines as exercises. Note that many of the commands do not use parameter L,
the list, and we omit it from those that do not.
The mechanics of the pointer manipulations of the INSERT procedure in Fig. 2.6 are shown in Fig.
2.7. Figure 2.7(a) shows the situation before executing INSERT. We wish to insert a new element in
front of the cell containing b, so p is a pointer to the list cell that contains the pointer to b. At line
(1), temp is set to point to the cell containing b. At line (2) a new list cell is created and the next
field of the cell containing a is made to point to this cell. At line (3) the element field of the newly-
created cell is made to hold x, and at line (4) the next field is given the value of temp, thus making
it point to the cell containing b. Figure 2.7(b) shows the result of executing INSERT. The new
pointers are shown dashed, and marked with the step at which they were created.
procedure INSERT ( x elementtype; p: position);
var
temp : position;
begin
(1) temp := p .next;
(2) new(p next);
(3) p .next .element := x;
(4) p .next .next := temp
end; { INSERT }
2
begin
new(L); L . next := nil;
return (L)
end; { MAKENULL }
Fig. 2.6. Some operations using the linked-list implementation.
The DELETE procedure is simpler. Figure 2.8 shows the pointer manipulations of the DELETE
procedure in Fig. 2.6. Old pointers are solid and the new pointers dashed.
We should note that a position in a linked-list implementation of a list behaves differently from a
position in an array implementation. Suppose we have a list with three elements a, b, c and a
variable p, of type position, which currently has position 3 as its value; i.e., it points to the cell
holding b, and thus represents the position of c. If we execute a command to insert x at position 2,
the list becomes a, x, b, c, and element b now occupies position 3. If we use the array
implementation of lists described earlier, b and c would be moved down the array, so b would
indeed occupy the third position.
However, if the linked-list implementation is used, the value of p, which is a pointer to the cell
containing b, does not change because of the insertion, so afterward, the value of p is "position 4,"
not 3. This position variable must be updated, if it is to be used subsequently as the position of b. †
Doubly-Linked Lists
In a number of applications we may wish to traverse a list both forwards and backwards efficiently.
Or, given an element, we may wish to determine the preceding and following elements quickly. In
such situations we might wish to give each cell on a list a pointer to both the next and previous cells
on the list, as suggested by the doubly-linked list in Fig. 2.13. Chapter 12 mentions some specific
situations where doubly-linked lists are essential for efficiency.
A procedure for deleting an element at position p in a doubly-linked list is given in Fig. 2.14. Figure
2.15 shows the changes in pointers caused by Fig. 2.14, with old pointers drawn solid and new
pointers drawn dashed, on the assumption that the deleted cell is neither first nor last. † We first
locate the preceding cell using the previous field. We make the next field of this cell point to the cell
following the one in position p. Then we make the previous field of this following cell point to the
cell preceding the one in position p. The cell pointed to by p becomes useless and should be reused
automatically by the Pascal run-time system if needed.