11 Linked List
11 Linked List
Joe
Sue
Bill
Alice
Pat
door
Chapter 11
Linked Lists
Page 2
When Joe finishes with his haircut, he gets up and tells Sue to take his place in the chair. Note that
only Sue has to move; none of the other customers needs to change seats in order to maintain the
ordering. If all customers follow this discipline, the correct ordering will be maintained even
though no one person knows the complete ordering. For example, Alice knows that Pat follows
her, and that Sue and Bill were in the shop before she arrived, but she does not know the ordering
of Sue and Bill.
2 Linked Lists
A sequence whose structure is maintained as a collection of items, each of which contains a pointer
to the next item, is called a linked list. The most common form of a linked list has three important
features:
1. There is a provision for finding the first entry of the list. In the barbershop, the first entry
in the current list is the one in the barber chair. Anyone entering the shop would know very
little about the complete ordering of customers, but would know that the person in the
chair is at the head of the list.
2. Each entry in the list keeps track explicitly of its successor. Thus, some local information
about the structure of the list is kept with each entry of the list. The information specifying
a successor is called a pointer or a link.
3. The last entry of the list has no successor. This is represented by a special pointer value
-- a pointer value that doesn't point to anything.
Each element of a linked list is called a node, and each node contains at least two kinds of data: an
entry of the data sequence (e.g., the customers in a queue) and the structure information that
specifies (for each entry of the data sequence) which node comes next. The global structure of the
list need not be (and generally is not) stored anywhere, it is distributed throughout the list and
consists of the collection of local information in the form of pointers.
The distribution of the list structure information through the list has some advantages and
disadvantages. For example, getting to the tenth element of an array is easy because we can access
it directly using the array index. But getting to the tenth element of a linked list requires that we
start at the beginning and trudge down ten entries, following the link at each one. On the other
hand, insertion into the middle of a list stored in an array requires that we shift some of the array
entries, while an insertion into the middle of a linked list requires only changes to adjacent entries
in the list. In this chapter, we will look at various forms of linked lists, at their implementations,
and at the algorithms to maintain and manipulate them.
Arrays are structures that give immediate access to any component. Thus, to access any entry of an
array, one needs only know the address of the array, the description of its structure (the number of
dimensions, and the number of index values along each dimension) and the size of each entry. This
information is all specified in the definition of an array, and is used to provide (1) access to each
entry of the array. Arrays are called implicit data structures because the structure of an array (that
is, how to access its component parts) is specified by the array definition; no structure information
appears in the array itself. Implicit data structures have the advantage of providing quick access to
the parts of a data structure, but the disadvantage that the structure is fixed.
Chapter 11
Linked Lists
Page 3
In contrast, data in which structure information is itself part of the data is called an explicit data
structure. Explicit data structures generally require more storage because of the structure
information they include, but they provide a flexibility that is lacking in the implicit structures.
Explicit data structures are commonly used in combination with dynamic storage allocation,
which provides additional storage on demand during program execution. The combination of
linked structures and dynamic storage allocation makes possible data structures that are not fixed
and, in fact, can grow and change during program execution. An example of such an explicit data
structure is the linked list, in which each entry in a sequence is stored in a node along with
information about which node contains the next sequence entry; thus, the information that
determines the list structure is distributed throughout the data structure.
Linked lists and other linked structures can be implemented in a variety of ways, including with
arrays. But array implementation of linked list is not truly dynamic; the list can grow and shrink,
but only within the bounds of the array. We will focus instead on truly dynamic data structures.
Two programming language features are needed in order to implement linked lists. First is a data
structure that can be dynamically allocated whenever and in whatever quantity needed. And
second is a pointer to link one list node to another. In some programming languages, there are
special dynamically allocated data structures and a special pointer data type, but in Java we use
features that we already know and love: classes and references. Linked list nodes will be objects
of a Node class; pointers will be references to other Node objects. In this chapter, well see how to
build explicit data structures of any description. We'll first describe the simplest of these structures,
the linked list, which itself comes in a variety of forms.
Chapter 11
Linked Lists
Page 4
linked list is irrelevant to the list of names; the structure of the list is completely determined by the
pointers.
Sue
Joe
head
Alice
Bill
Pat
nul
l
head
null
Chapter 11
Linked Lists
Page 5
Advance p to point to the next node by assigning p the value of the pointer in the
node it is pointing to.
Pointer p is initially given the value of the list head, called head in the figures above. Thus p is
initially equal to null (in which case the list is empty), or it starts out pointing at the first node. This
completes the algorithm. The complete informal algorithm to display the elements of a linked list is
shown below. As with a sequential scan of an array, scanning a linked list of size n is a (n)
operation.
//Displaythecontentsofthelistpointedtobyhead.
//Pre:true
//Post:Alltheelementsofthelisthavebeendisplayed,inorder.
//Setptopointatfirstnodeifitexists,andnullotherwise.
p=head;
while(true)
{
//Inv:Thelistofvaluesinnodesthatprecedethenode
//pointedtobyphavebeendisplayed.
if(p==null)break;
displaythevalueofthedatafieldofthenodecurrently
pointedtobyp.
//Advanceptopointatthenextnode
p= thevalueinthepointerfieldofthenodecurrently
pointedtobyp
}
The ADT Sequence operation elem(i) returns the value of the ith entry in the sequence. If a
sequence is implemented as an array, elem(i) is both simple and fast (its a (1) operation).
Accessing the ith element of a linked list neither so easy nor so quick. Since our only entry into a
linked list is through the head, getting to the ith element requires that we start at the head and go
sequentially through the first i-1 elements to get to the ith. The informal algorithm is shown below.
//Displaytheithentryofthelistpointedtobyhead.
//Pre:0<=iandthelisthasatleasti+1entries.
//Post:Theithelementofthelisthasbeendisplayed.
//Setptopointatfirstnode.
p=head;
for(intj=0;j<i;j++)
{
//Advanceptopointatthenextnode.
p=thevalueinthepointerfieldofthenodecurrently
pointedtobyp.
//Inv:ppointstothejthelementofthelist.
}
displaythedatafieldofthenodecurrentlypointedtobyp.
Displaying the contents of the ith node of a linked list with n entries is on average a (n) operation,
hence the elem(i) operation is far less efficient in a linked list than in an array. The situation is
even worse for some other operations because, while moving down a linked list is easy, moving up
(toward the head) is not. Later we'll look at variations of linked lists that make it possible to move
easily in either direction.
Printed January 18, 2017 01:40 PM
Chapter 11
Linked Lists
Page 6
Insertion and deletion operations are simple for linked lists if the appropriate pointers are readily
available. In the example below, we add a node containing the new value Tom to the list between
Bill and Alice; the changed pointer values are indicated by bold arrows. The simplest way to do
this is to first set Toms pointer to point to Alice, and then change Bills pointer to point to Tom.
Nothing else in the list need be changed; the changes are entirely local. Notice that the order in
which we set these two pointers was critical. If we had changed Bills pointer first, then Alice and
Pat would have been lost.
Sue
Joe
Head
Alice
Bill
Pat
Tom
null
;
Head
Alice
Bill
Tom
Pat
null
Chapter 11
Linked Lists
Page 7
Sue
Joe
Head
Alice
Bill
Tom
Pat
nil
Chapter 11
Linked Lists
Page 8
publicclassList
{
privateNodehead;//Referencetofirstnodeonthelist.
List()//Constructor:setheadtonull.
{
head=null;
}
//SetheadtopointtoaNode.
publicvoidsetHead(Noden)
{
head=n
}
}
Chapter 11
Linked Lists
Page 9
publicclassNode
{
privateStringdata;
privateNodenext;
Node()//Constructor:setdatatoempty;nexttonull;
{
data="";
next=null;
}
Node(Strings)//Constructor:setdataasindicated.
{
data=s;
next=null;
}
Node(Strings,Noden)//Constructor:setdata
//andnextasindicated.
{
data=s;
next=n;
}
publicStringgetData()//Reader:returndatavalue
{
returndata;
}
publicNodegetNext()//Reader:returnnextvalue.
{
returnnext;
}
publicvoidsetData(Strings)//Writer:setdatavalue.
{
data=s;
}
publicvoidsetNext(Noden)//Writer:setnextvalue.
{
next=n;
}
}
The code below creates a simple linked list called list1with two elements: "Linda" is first; "Jim"
is second.
Chapter 11
Linked Lists
Page 10
//Createlisthead
Listlist1=newList();
//Create"Linda"node.
Noden1=newNode("Linda");
//Create"Jim"node.
Noden2=newNode("Jim");
//SetheadtopointtoLinda.
list1.setHead(n1);
//ConnectLindatoJim.
n1.setNext(n2);
list1
"Jm"
"Linda"
null
If we were to change the head pointer to point to Jim thereby bypassing Linda, then Linda's node
would become garbage (assuming it were not addressed by some other reference) and would be
reclaimed by Java's automatic garbage collection.
//Setheadtopointtothesecondnodeonthelist,
//bypassingthefirst.
list1.setHead(n2);
The following code does the same thing, but without having to have an explicit name ( n2) for Jim's
node. This is a complex statement and dissecting it to understand exactly what it does is
important. First, list1.getHead() returns a reference to the first node object on the list (Linda).
Then we call getNext() in that object to retrieve the reference to the second node (Jim). And
finally, we call the setHead method for list1 to set that list's head pointer.
//Setheadtopointtothesecondnodeonthelist,
//bypassingthefirst.
list1.setHead((list1.getHead()).getNext());
list1
"Linda"
"Jm"
nul
l
Chapter 11
Linked Lists
Page 11
size()
insert(Strings,inti)
delete(inti)
Chapter 11
Linked Lists
Page 12
publicclassList
{
privateNodehead;//Referencetothefirstnodeonthelist.
privateintlistSize=0;//Currentlistsize.
List()//Constructor:setheadtonull.
{
head=null;
}
//Setheadtopointtospecifiednode.
publicvoidsetHead(Noden)
{
head=n;
}
//Returnthecurrentlistsize.
publicintsize()
{
//Pre:true
//Post:currentsizereturned.
returnlistSize;
}
//Returntheithelementofthelist.
publicStringelem(inti)
{
//Pre:0<=i<size()
//Post:Elementinpositionihasbeenreturned.
Nodep=head;
for(intj=0;j<i;j++)
//Moveptothenextnodeinthelist.
p=p.getNext();
//Inv:ppointstothejthelementinthelist.
returnp.getData();
}
//Insertstringsaselementi.
publicvoidinsert(Strings,inti)
{
Chapter 11
Linked Lists
Page 13
//Pre:0<=i<=size()
//Post:shasbeeninsertedaselementi;therestofthe
// listisunchanged.
//Createanewnodewiththedesireddatavalue.
NodenewNode=newNode(s);
//Specialcase:insertasfirstnode.
if(i==0)
{
newNode.setNext(head);
head=newNode;
}
else//Notfirstnode.
{
Nodep=head;
//Locatenodejustbeforeinsertionpoint.
for(intj=0;j<i1;j++)
{
//Advanceptothenextnode.
p.getNext();
Chapter 11
Linked Lists
Page 14
//Inv:ppointstothejthnode.
}
//pnowpointstothenodejustbeforethe
//insertionpoint.
//Insertnewnode.
newNode.setNext(p.getNext());
p.setNext(newNode);
}
//Updatesize.
listSize++;
}//Endofinsert.
//Deletetheithelementformthelist.
publicvoiddelete(inti)
{
//Pre:0<=i<size()
//Post:ithelementhasbeendeleted.Nootherchanges.
//Specialcase:deletethefirstelement.
if(i==0)
{
head=head.getNext();
}
else//i>0
{
//Locatethenodejustbeforetheonetobedeleted.
for(intj=0;j<i1;j++)
//Advanceptothenextnode.
p=p.getNext();
//Inv:ppointstothejthnode.
//pnowpointstothenodejustbeforetheonetobedeleted.
//Bypassthenodetobedeleted.
p.setNext((p.getNext()).getNext())
}
//Updatesize
listSize;
}//Endofdelete.
Chapter 11
Linked Lists
Page 15
7.1 Insertion
Recall from the List implementation code that inserting a new node into a linked list is easy if we
have a pointer to the node that precedes the insertion point. For example, the following code
inserts the new node with data B into the linked list following the node pointed to by p.
NodenewNode=newNode(B);//Createnewnode.
newNode.setNext(p.getNext());//ConnectnewnodetoC(step1)
p.setNext(newNode);
//ConnectAnodetoB(step2)
p
p
A
A
2
newNode
newNode
Before
After
Inserting a list entry after that pointed to by p
Note that the order of the last two instructions is crucial. We've labeled the pointers in the diagram
to indicate the order in which the pointer values must be assigned. Reversing the last two
instructions would make the node containing C (and everything after C) inaccessible.
The preceding code is admirably straightforward, but it requires that we have a pointer to the node
preceding the place of insertion. In many algorithms, when we search for the place of insertion, the
pointer used for the search naturally ends up pointing to the node that should follow the value to
be inserted. For example, if we are inserting a new node into a list that is sorted in ascending order,
we recognize the insertion point only when we have found a data value that is larger than the value
to be inserted. A common solution to this problem is to search using a pair of pointers, one of that
finds the insertion spot, while the second pointer is kept pointing to the node preceding the first.
The second pointer can then be used for the insertion, using the algorithm above. This technique
requires an extra pointer, and about i assignment statements if the insertion is to be made at
position i.
The following code inserts the new entry before the entry stored in the node referenced by p. This
eliminates the need for a second pointer, but at the additional cost of copying the contents of the
node pointed to by p. If the data part of a node is large, this cost can outweigh the cost of the
pointer manipulations it saves, but this is usually not the case.
We begin with a similar situation as before, except that the new value A is to be inserted prior to
the list entry indicated by the pointer p. This strategy works in all cases except that it cannot be
used to insert a new node at the end of a list.
Chapter 11
Linked Lists
Page 16
NodenewNode=newNode(p.getData(),p.getNext());//Createnewnode
//copyingdataandpointer.
p.setData(A);
p.setNext(newNode);
p
C
2
newNode
newNode
Before
After
7.2 Deletion
As with insertion, deleting a node is straightforward if we begin with a pointer to the node
preceding the one to be deleted. This can be done as follows:
p.setNext((p.getNext()).getNext());//Bypassthenodefollowing
//thenodepointedtobyp.
p
p
A
A
Before
Chapter 11
Linked Lists
Page 17
p.setData((p.getNext()).getData()); //Copydatafromnextnode.
p.setNext((p.getNext()).getNext());//Bypassthenodefollowing
//thenodepointedtobyp.
p
p
B
A
Before
This deletion technique cannot be performed if p points to the last node of a simple linked list. That
might make this technique seem useless, but in the next section we'll show how to overcome the
problem by modifying the form of a linked list.
8.1 Eliminating the special cases: dummy head and tail nodes
Its nearly always good programming practice to eliminate or reduce the number of special cases.
In previous sections, we've seen that insertions and deletions at the beginning or end of a standard
linked list must often be treated differently from insertions and deletions elsewhere. How can we
avoid these special case?
One solution is to put dummy first and last nodes on the list. These nodes can be assigned sentinel
values; for example, if the list is sorted in ascending order, we can give the dummy head node a
data value that is less than any legal data value, and similarly, give the dummy last node a data
value that is larger than any legal data value. Then all insertions and deletions must take place in
the interior of the list and there are no special cases. Moreover, by keeping a pointer to the dummy
tail node, we can store sentinel values in its data field, thus simplifying and speeding up searches in
many instances. This additional structure would have its own costs; for example, we would have to
modify the procedure that creates the list so that it initializes the empty list to a list with the two
dummy nodes.
To illustrate the coding rewards of using dummy nodes, below are pairs of insertion and deletion
methods for updating a sorted linked list. The first method in each case assumes a conventional
linked list implementation; the second method is written for an implementation with a dummy tail
node that holds a sentinel value. In each case, the sentinel value could be the value being inserted
or deleted. The sentinel guarantees that insertions and deletions do not occur at the very end of the
list, and hence the copy method for insertions and deletions will always work. Assume that these
Chapter 11
Linked Lists
Page 18
methods are part of a SortedList class in which the variable head points to the first node of the
list and where each node contains a String data field and a next field (a reference to another Node
object or null).
publicvoidinsert(Stringkey)
//Addkeytothesortedlistinitsproperplace.
//Aconventionallinkedlistimplementationisassumed.
//Pre:thelistsissortedby<=.
//Post:keyhasbeeninsertedintos;sissortedby<=.
{
Chapter 11
Linked Lists
Page 19
//Createanewnodeandinsertdata.
NodenewNode=newNode(key);
//Specialcase:listisemptyorkeyislessthanthesmallest
//datavalueonthelist.Newnodegoesfirst.
if(head==null||key.compareTo(head.getData())<0)//SCeval
{
newNode.setNext(head);
head=newNode;
}
else//Newnodegoesfurtherdownthelist.
{
//Setqtopointtotheelement0;ptoelement1.
Nodeq=head;
Nodep=head.getNext();
while(true)
{
//Inv:alllistnodesuptoandincludingthenode
//pointedtobyqhavedatavalueslessthankey.
//Andeitherpisnullandqpointstothelast
Chapter 11
Linked Lists
Page 20
//nodeorppointstothenodethatfollowsq.
if(p==null||key.compareTo(p.getData())<=0)break;//SCeval
//Advancethetwopointers.
q=p;
p=p.getNext();
}
//Newnodegoesafterqandbeforep.
newNode.setNext(p);
q.setNext(newNode);
}
//Updatesize.
listSize++;
}//Endinsert.
publicvoidinsert(Stringkey)
//Addkeytoasortedsequenceinitsproperplace.Alinked
//listimplementationwithadummytailnodeisassumed.
//Asentinelvalueisstoredinthetailnodethatis>=key.
//Pre:listissortedby<=.Lastlistelementis>=key.
//Post:keyhasbeeninsertedintolist;listissortedby<=.
{
//Locateinsertionpoint
Nodep=head; //ppointstofirstdatanodeordummytail
//nodewithsentinelvalue.
while(true)
{
//Inv:alllistnodesprecedingthenodepointedtobyp
//havedatavalueslessthankey.
if(key.compareTo(p.getData())<=0)break;
//Advancepointer.
p=p.getNext();
}
//Insertkeyintolistbeforenodepointedtobyp.
//Createnewnodewithdatavalueofnodepointedtobyp.
NodenewNode=newNode(p.getData(),p.getNext());
//Putnewdatavalueintonodepointedtobyp.
p.setData(key);
//Insertnewnodeintolistafternodepointedtobyp.
p.setNext(newNode);
//Updatesize.
listSize++;
}Endofinsert.
This second method has a number of attractive features when compared to the first:
The basic structure of the first method is a conditional that treats two cases: insert first
and insert somewhere else. This entire structure is unnecessary in the second procedure.
Chapter 11
Linked Lists
Page 21
The search loop in the first method uses a compound exit condition and relies on shortcircuit evaluation. In contrast, the loop exit condition of the second procedure is simple.
The difference in complexity of the loop structures is reflected in the loop invariants.
The loop body of the first procedure moves two pointers in tandem; the second procedure
moves a single pointer.
Storage allocation in the first procedure must be done at the top to cover both conditions,
or the allocation statement must be repeated for each case. In the second procedure, the
allocation statement can be naturally grouped with the insertion of the new node.
On the other hand, the second procedure copies the contents of one node into another. If nodes
are very large, this copying may be prohibitively expensive. In that case, the use of the "twopointer walk" might be advisable.
Similar differences hold for the following two deletion methods for sorted linked lists.
Chapter 11
Linked Lists
//Deletethefirstelementofthelistwithdatavalue==key.
//Donothingifkeyisnotinthelist.
//Thelinkedlistisstoredasaconventionalsortedlinkedlist.
Page 22
Chapter 11
Linked Lists
Page 23
publicvoiddelete(Stringkey)
{
//Pre:Thelistissortedby<=.
//Post:Thefirstoccurrenceofkeyhasbeendeleted.Ifkeyisnotfound,
//nochangeismade.
if(head!=null&&key.compareTo(head.getData())>=0)//SCeval
//Don'tbothertosearchifthelistisemptyorifkey<firstelement.
{
if(key.compareTo(head.getData())==0)//keyisfirstelement.
{
head=head.getNext(); //Bypassfirstelement.
}
else//Lookfartherdownthelist.
{
Nodeq=head;
//Pointstofirstnode.
Nodep=head.getNext();//Pointstosecondnode.
while(true)
{
//Inv:Allentriesuptoandincludingtheelement
Chapter 11
Linked Lists
Page 24
//pointedtobyqare<key.
if(p==null||
key.compareTo(p.getData())<=0)break;//SCeval
//Advancethe2pointers.
q=p;
p=p.getNext();
}
//Deleteonlyifkeywasfound.
if(p!=null&&key.compareTo(p.getData())==0)//SCeval
{
//Bypassnodepointedtobyp.
q.setNext(p.getNext());
}
}
//Updatesize.
listSize;
}
}//Endofdelete.
//Deletethefirstelementofthelistwithdatavalue==key.
//Donothingifkeyisnotinthelist.
//Thelinkedlisthasadummytailnodewithvalue>=key.
publicvoiddelete(Stringkey)
{
//Pre:Thelistissortedby<=withdummytailnode>=key.
//Post:Thefirstoccurrenceofkeyhasbeendeleted.Ifkeyisnotfound,
//nochangeismade.
Nodep=head;
//Searchforkey.
while(true)
{
//Inv:allnodesprecedingtheonepointedtobyphavedatavalues<key.
if(key.compareTo(p.getData())<=0)break;
//Advancepointer.
p=p.getNext();
}
//Deleteifkeywasfoundanditwasnotthedummy.
if(p.getNext()!=null&&key.compareTo(p.getData())==0)
{
p.setData((p.getNext()).getData());//Copydatafromnextnode.
p.setNext((p.getNext()).getNext());//Bypassnextnode.
}
}//Enddelete.
The second delete method is not only shorter than the first, it also has no special cases, a much
simpler loop exit condition and does not rely on short circuit evaluation. It does require, however,
a dummy tail node containing a sentinel data value that is greater than or equal to the key.
Assigning such a sentinel value may be problematic, but a slightly different approach circumvents
this difficulty. If the list is constructed so that a pointer to the dummy tail node is available, then
each time a search is performed (as is done with insert and delete), the key value can be inserted
into the data field of the dummy tail node to serve as a sentinel.
Chapter 11
Linked Lists
Page 25
The use of dummy nodes represents another example of a classic trade-off. We spend a little more
on space (for an extra node or two and a pointer to it) and a little more time in some of the
operations (such as initializing the list) in exchange for substantial simplification of the insert and
delete procedures.
list
list
null
Circular lists are advantageous in many circumstances, but their advantages are increased if they
are constructed with a dummy head node. The following diagram illustrates this structure for the
same three lists. The nodes with the '?' are the dummy head nodes.
list
list
list
?
?
Chapter 11
Linked Lists
Page 26
C
The circular list with dummy head node provides advantages similar to that of the non-circular list
with a dummy tail node; the head node is readily accessible and can be used to hold sentinel values
for searches.
The circular list requires no more space than the corresponding linear list (with or without a
dummy node). An unsuccessful search will cycle around from the end of the list back to the
beginning, and the programmer must be careful to write loops that terminate. The procedures for
insertion into and deletion from a circular linked list are left as an exercise.
Tom
nil
Linda
Bill
Barbara nil
Atwowaylinkedlist
Chapter 11
Linked Lists
Page 27
sentinels. Other uses for tail pointers exist. There are certain kinds of lists where changes occur
only at the ends. In a stack, for example, all changes occur at the top. And a standard linked list is
a very efficient way to implement a stack. If the front of a linked list corresponds to the top of the
stack, the stack operations of push, pop and top can all be performed in constant time without any
auxiliary access points. In a queue, however, changes occur at both ends: additions occur at one
end, deletions at the other. If we used a standard linked list to implement a queue with n elements,
one of the two operations would be (1), while the other would be (n). We can make both
operations (1) by providing both a head pointer to the front of the list and a tail pointer to the
back of the list. Adding a new node at the end of the list is now an easy matter of adding a new
successor to the tail node and adjusting the tail pointer, as is shown in the example below.
head
head
Tom
Dick
Dick
head
Tom
tail
Harry
Tom
tail
Harry
Betty
null
Dick
tail
Harry
null
Headandtailpointers
null
Betty
Newnodetobeaddedtoend
Pointersadjusted
Alteredpointersinbold
A circular linked list can also be used to implement a queue with (1) complexity for the principle
operations.
8.4.2 Thumb index
Lets say we had a long sorted linked list of names and were searching for the name LION. We
would have to start at the head and plow through the aardvarks, beavers, cats, dogs, etc. to get to
LION. What would be preferable is if we could enter the linked list somewhere closer to our
destination. One way to do this is to have an array of 26 list references, one for each letter of the
alphabet. Each reference would point to the first entry in the linked list that begins with that letter.
Then to find LION, we would first access the 12 th element of the reference array ('L' is the 12th
letter of the alphabet), which would give us the starting place of the 'L' words. We would still have
to look at lamb, lemur, and limpet, but the overall search would be much quicker. This strategy is
called a thumb index since it resembles the way in which we use a dictionary that has a thumb
index. Here again, we have paid in space and in some additional complexity in creating and
maintaining the list, and received an increase in search speed.
List[]thumbIndex=newList[26];
Chapter 11
Linked Lists
Page 28
thumbIndex[i]wouldbesettopointtothefirstelementofthesorted
linkedlistthatbeginswiththeithletterofthealphabet.
8.5 Summary
The variations on linked lists -- dummy nodes, circular, doubly linked and multiple entry -- can be
used in any combination. There is no clear best choice for all problems; the programmer's best
strategy is to review the requirements of a particular problem and choose the form that appears to
be most appropriate. Additional storage requirements are imposed by using dummy nodes and
two-way linking, but this is not usually a strong factor in the choice, and the advantages brought
by these embellishments often makes them a good choice. The addition of multiple access facilities
is another way of using additional space to save substantial time for some algorithms. In each case,
we add structure to the basic linked list, and maintaining that structure will incur at least a small
cost in the algorithms that use the list. But if the sum of those small costs is outweighed by the
savings in performing other tasks, the result is a win.
Chapter 11
Linked Lists
Page 29
Analogy____________________________________________________________1
LinkedLists_________________________________________________________2
Descriptionofalinkedlist______________________________________________3
Workingwithlinkedlists_______________________________________________4
Javaimplementationofalinkedlist______________________________________7
5.1
Commonpitfallsoflinkedstructures________________________________________9
TheADTList_______________________________________________________10
Usingcopyingtosimplifyinsertionsanddeletions_________________________12
7.1
Insertion_______________________________________________________________12
7.2
Deletion_______________________________________________________________14
Variationsonthebasiclinkedlist_______________________________________15
8.1
Eliminatingthespecialcases:dummyheadandtailnodes_____________________15
8.2
Circularlinkedlist______________________________________________________20
8.3
DoublyLinkedLists_____________________________________________________21
8.4
Multipleaccess_________________________________________________________22
8.4.1
8.4.2
8.5
Tailpointers_______________________________________________________________22
Thumbindex______________________________________________________________23
Summary______________________________________________________________23
Nonlinearlinkedstructures:treesandgraphs____________________________24