Вы находитесь на странице: 1из 40

CHAPTER-3

Linked Lists
The term List refers to a linear collection of data items. The property of being sequential is basic to its definition and use. The sequentially of a linear list is diagrammed below, which shows a shopping list. 2il)"ggs /utterApples3ranges444444bread "lement ! "lement % 444 444 444 444 444
"lement n

5igure!: A Shopping list A Linear Structure has two properties: roperty !: "ach element is #followed by$ at most one other element roperty %: &o two elements are #followed by$ the same element. 'e A*( generally write linear (ata types li)e this *ounter e+ample ! ,-iolating roperty !. : A points to / and * /A* *ounter e+ample % ,-iolating roperty %.: A and / both points to *, A*/

*ounter e+ample ! is a tree, but counter e+ample % is not. 0f we drop both constraints we get a graph. 0n a graph there is no constraints on the relations we can define. Linear lists can be di-ided into two categories: general and restricted. 0n a general list , data can be inserted and deleted anywhere and there are no restrictions on the operations that can be used to process the list. 1eneral structures can be further described by their data, as either random or ordered lists. 0n a random or unordered list, there is no ordering of the data. 0n an ordered list , the data are arranged according to a )ey. A key is one or more

Chapter 3- 1

fields within a structure that are used to identify the data. 0n the simple array, the data are also the )eys. 0n an array of records structure, the )ey is a field, such as employee number, that identifies the record. Linear Lists

1eneral

7estricted

6nordered

3rdered

L053 Stac) 5igure: %

5053 que

("86"

0n a restricted list, data can only be added or deleted at the ends of the structure and processing is restricted to operations on the data at the ends of the list. Three restricted list structures are: the last in4first out ,L053. Stac), the 5irst in45irst out ,5053. queue, and deque structures. 5our operations are generally associated with linear lists: insertion, deletion, retrie-al and tra-ersal. The first three apply to all lists, list tra-ersal is not applicable to restricted lists.

Linked Lists
A linear lin)ed list ,or lin)ed list, for short. or one4way list or singly lin)ed list, is a linear sequence of data elements in which each node, e+cept the last, lin)s to a successor node by means of a pointer. 5igure 9 shows a lin)ed list, p:ead, that contains four elements. The lin) in the last element contains null pointer, indicating the end of the list. 'e define an empty linked list to be null head pointer. (ata elements in a lin)ed list are traditionally called nodes. A node in a lin)ed list is a structure that has at least two fields: one contains the data, the other lin) or pointer or address of the ne+t node in the sequence. ointers are used to establish lin)s between nodes in sequence. Therefore pointer is a data -alue that references a unit of storage.
Chapter 3- 2

p:ead

data

lin)

data

lin)

data

lin)

data

lin)

(a A linked list !it" a "ead pointer# pHead

p:ead ($ An empty linked list

(C

is same as

&6LL

5igure 9: A Lin)ed List Singly Lin)ed lists permit insertion, deletion of nodes at any point in the list. Lin)ed lists do not allow random access. Singly lin)ed lists allow sequential access to elements only in one direction. 7etrie-al and tra-ersal are also allowed in lists. The combination of lin)ed data structures with dynamic memory allocation is e+tremely beneficial because it utili;es memory with -ery high efficiency. 3n contrary a simple array representation of list will allocate memory statically, where the nodes are physically contiguous, and the allocation si;e cannot be altered during runtime. 0t is also relati-ely e+pensi-e to insert and delete elements in an array structure.

3.2

Linked List Concepts

A Linked List is an ordered collection of data in which each element contains the location of the ne+t element< that is, each element contains two parts: data and link. The data part holds the useful information, the data to be processed. The lin) is used to chain the data together. 0t contains a pointer that identifies the ne+t element in the list. 0n addition, a pointer -ariable identifies the first element in the list. The name of the list is the same as the name of this pointer -ariable. This pointer -ariable to the beginning of the list is )nown as a head pointer. The simple lin)ed list we describe here is
Chapter 3- 3

commonly )nown as a singly linked list because it contains only one lin) to a single successor. The ma=or ad-antage of the general lin)ed list o-er other restricted list structures is that data are easily inserted and deleted. 0t is not necessary to shift elements of a lin)ed list to ma)e room for a new element or to delete an element. 3n the other hand, because the elements are no longer physically sequenced, we are limited to sequential searches: we cannot use a binary search. 5igure 9 shows a lin)ed list, p:ead ,for pointer to the head of the list., that contains four elements. The lin) in each element e+cept the last points to its successor. The lin) in the last element contains a null pointer, indicating the end of the list. 'e define an empty linked list to be a null head pointer. 5igure 9 also contains an e+ample of an empty lin)ed list.

%odes
The elements in a lin)ed list are traditionally called nodes. A node in a lin)ed list is a structure that has at least two fields: one contains the (ata, the other the address of the ne+t node in the sequence. 5igure > shows three different node structures. The first node contains a single field, number, and a lin). The second node contains three data fields, a name, id, and grade points ,grdpts., and a lin). The third e+ample is the one we recommend. The fields are defined in their own structure, which is then put into the definition of a node structure. The one common element in all e+amples is the lin) field. The nodes in a lin)ed list are called sel&-re&erential structures. 0n a self4 referential structure, each instance of the structure contains a pointer to another instance of the same structural type. 0n 5igure >, the shaded bo+es with arrows are the lin)s that ma)e the lin)ed list a self4referential structure.

Chapter 3- 4

A node with one data field

A node with three data field

number
Lin)

name

id

grdPts
Lin)

A structure in a node
name address grdPts
Lin) 5igure >: &odes

Linked List 'ata (tructure


3ne of the attributes of a lin)ed list is that there is not a physical relationship between the nodes, 'ithout a physical relationship between the nodes, we need pointers to distinguish the beginning of the list that is, to identify the first logical node in the list and the location of any gi-en node$s immediate successor. The pointer to the beginning of the list is )nown as a "ead pointer because it points to the node at the head of the list. 0n 5igure 9, we call the head p:ead and the pointer that identified a node$s immediate successor lin).

Head %ode (tructure


Although only a single pointer is needed to identify the list, we often find it con-enient to create a structure that stores the head pointer and other data about the list itself. 'hen a node contains data about a list, the data are )nown as metadata) that is, they are data about data in the list. 5or e+ample, the head structure in 5igure ? contains one piece of metadata: count, an integer that contains the number of nodes currently in the list. 3ther metadata, such as the ma+imum number of nodes during the processing of

Chapter 3- 5

the list and other additional pointers etc are often included when they ser-e a useful purpose.
l ist c ount h ead e nd list @integerA @pointerA

count

head

(a Head structure n ode d ata l in) e nd node @dataTypeA @pointerA

data ($ 'ata node

lin) structure

5igure ?: Lin)ed list head B data nodes Structure

'ata %ode (tructure


The data type for the list depends entirely on the application. &ode data structure is shown in 5igure ? in detail. 0t contains the data type and lin). Lin) is a pointer to another data structure of its own type. 'e can thus create our lin)ed list structure in which one instance of a node points to another instance of the node, ma)ing it a self4referential list.

*mplementation o& Lists


There are two ways to implement lin)ed lists, one is array implementation and the other using dynamic -ariables with pointers. a Array *mplementation o& Linked Lists

Since a list is a collection of nodes in sequence, an array of nodes can be used for its implementation. :owe-er, the nodes cannot be ordered by the array ordering, i.e the nodes of a list need not occupy ad=acent elements in arrays. "ach node must contain within its structure a pointer to its successor. 5urthermore, more than one list may be maintained in the same array, with each list ha-ing its own pointer -ariable gi-ing the location of its first node.

Chapter 3- 6

0n *, a group of ?CC nodes might be declared as an array node as follows: Ddefine &62&3("S ?CC struct nodetype E int data, lin)< F< Struct nodetype node G&62&3("SH< 0n this scheme a pointer to a node is represented by an array inde+. That is, a pointer is an integer between C and NUMNODES ! that references a particular element of the array node. The null pointer is used to show the end of list. 6nder this implementation, the * e+pression nodeGpH is used to reference node,p., data(p) is referenced by nodeGpH.data, and link,p. is referenced by nodeGpH.link. 5or e+ample, suppose that the -ariable list1 represents a pointer to a list. 0f list1 has the -alue !I, nodeG!IH is the first node on the list, and nodeG!IH.data is the first data item on the list. The second node of the list is gi-en by nodeG!IH.lin). Suppose the nodeG!IH.link equals %C. The nodeG%CH.data is the second data item on the list and nodeG%CH.link pointer to the third node. The nodes of a list may be scattered throughout the array node in any arbitrary order. Each node carries within itself the location of its successor until the last node in the list, whose link field contains Null, which is the null pointer. There is no relation between the contents of a node and the pointer to it. The pointer, p, to a node merely specifies which element of the array node is being referenced< it is nodeGpH.data that represents the information contained within that node. 5igure I illustrates a portion of an array node that contains four lin)ed lists. The list list! starts at nodeG!IH and contains the integers 9, J, !>, I, ?, 9J, !%. The nodes that contain these integers in their data fields are scattered throughout the array. The link field of each node contains the inde+ within the array of the nodes containing the ne+t element of the list. The last node on the

Chapter 3- 7

list is nodeG%9H, which contains the integer !% in its data field and the null pointer in its link field, to indicate that it is last on the list. Similarly, list% begins at nodeG>H and contains the integers !J and %I, list9 begins at nodeG!!H and contains the integers 9!, !L and 9% and list> begins at nodeG9H and contains the integers !, !K, !9, !!, >, and !?. The -ariables list1, list%, list9, and list> are integers representing e+ternal pointers to the four lists. Thus, the fact that the -ariable list% has the -alue > represents the fact that the list to which it points begins at nodeG>H.

data

lin)

List > 9 List % >

List 9 !!

List ! !I

C 26&ull119515124170131191814124213176 ! 2372332032 &ull7815&ull12&ull185 % 9 > ? I J K L !C !! !% !9 !> !? !I !J !K !L %C %! %% %9 %> %? %I %J

5igure I Array of nodes containing four lin)ed lists.

Limitations o& t"e Array *mplementation

Chapter 3- 8

6nder the array implementation, a fi+ed set of nodes represented by an array is established at the start of e+ecution. A pointer to a node is represented by the relati-e position of the node within the array. The disad-antage of that approach is twofold. 5irst, the number of nodes that are needed often cannot be predicted when a program is written. 6sually, the data with which the program is e+ecuted determines the number of nodes necessary. Thus no matter how many elements the array of nodes contains, it is always possible that the program will be e+ecuted with input that requires a larger number. The second disad-antage of the array approach is that whate-er number of nodes are declared must remain allocated to the program throughout its e+ecution. 5or e+ample, if ?CC nodes of a gi-en type are declared, the amount of storage required for those ?CC nodes is reser-ed for that purpose. 0f the program actually uses only !CC or e-en !C nodes in its e+ecution the additional nodes are still reser-ed and their storage cannot be used for any other purpose. The solution to this problem is to allow nodes that are dynamic, rather than static. That is, when a node is needed, storage is reser-ed for it, and when it is no longer needed, the storage is released. Thus the storage for nodes that are no longer in use is a-ailable for another purpose. Also, no predefined limit on the number of nodes is established. As long as sufficient storage is a-ailable to the =ob as a whole, part of that storage can be reser-ed for use as a node. (ynamic nodes use notion of pointers intensi-ely. ointers allow us to build

and manipulate lin)ed lists of -arious types. The concept of a pointer introduces the possibility of assembling a collection of building bloc)s, called nodes, into fle+ible structures. /y altering the -alues of pointers, nodes can be attached, detached, and reassembled in patterns that grow and shrin) as e+ecution of a program progresses.

Linked Lists *mplementation +sing 'ynamic ,aria$les


Chapter 3- 9

There are two limitations or disad-antages when array structures are implemented with lin)ed lists. 5irst the number of nodes that are needed cannot be predicted when a program is written. The second disad-antage is that whate-er number of nodes are declared must remain allocated to the program throughout its e+ecution. The program actually use only a few of them in its e+ecution, the additional nodes are still reser-ed and their storage cannot be used for any other purpose. 6sing dynamic nodes rather than static can circum-ent these problems. That is when a node is needed, storage is reser-ed for it, and when it is no longer needed, the storage is released. /efore using dynamic -ariables to implement lin)ed list, let us understand how storage can be allocated and freed dynamically and how it is accessed in *. Allocating and .reeing 'ynamic ,aria$les 0n * a pointer -ariable to an integer can be created by the declaration int Mp< 3nce a -ariable p has been declared as a pointer to a specific type of ob=ect, it must be possible to dynamically create an ob=ect of that specific type and assign its address to p. This may be done in * by calling the standard library function malloc,si;e.: malloc dynamically allocates a portion of memory of si;e size and returns a pointer to an item of specified type. *onsider the declarations int *p; float *pr; The statements p N ,int ! malloc,sizeof ,int..< pr N ,&loat M. malloc,sizeof ,float..<

Chapter 3- 10

dynamically create the integer -ariable p and the float -ariable pr. These -ariables are called dynamic variables. 0n e+ecuting these statements, the operator sizeof returns the si;e, in bytes, of its operand. This is used to maintain machine independence. malloc can then create an ob=ect of that si;e. Thus malloc,sizeof"int.. allocate storage for an integer, whereas malloc,sizeof"float.. allocate storage for a floating4point number. malloc also returns a pointer to the storage it allocates. This pointer is to the first byte ,for e+ample, integer. of that storage and is of type int # To coerce this pointer so that it points to an integer or real, we use the cast operator "int ! or ,float !# As an e+ample of the use of pointers and the function malloc, consider the following statements. ! 2 3 4 5 6 7 8 9 10 11 12 int p$ %& int ' p ( "int ! mallo)"si*eof "int!!& p ( 3& % ( p& printf "+,d ,d -n. $ p$ %!& ' ( 7& % ( '& printf"/,d ,d -n.$ p %!& p ( "int ! mallo) "sizeof "int!!& p ( 5& printf"/,d ,d -n.$ p %!&

0n line 9, an integer -ariable is created and its address is placed in p. Line > sets the -alue of that -ariable to 9. Line ? sets q to the address of that -ariable. The assignment statement in line ? is perfectly -alid, since one pointer -ariable ,q. is being assigned the -alue of another , p.. 5igure Ja illustrates the situation after line ?. &ote that at this point, M p and /q refer to the same -ariable. Line I therefore prints the contents of this -ariable ,which is 9. twice. Line J set the -alue of an integer -ariable +, to J. Line K changes the -alue of Mq to the -alue of +. :owe-er, since p and q both point to the same -ariable, Mp and Mq both ha-e the -alue J. This is illustrated in 5igure Jb. Line L therefore prints the number J twice.

Chapter 3- 11

Line !C creates a new integer -ariable and places its address in p. The results are illustrated in 5igure Jc. M p now refers to the newly created integer -ariable
p q p q J q p q q ' p ? J ,d. J ,b. ' J ,c. J

9 ,a. ' J

5igure J that has not yet been gi-en a -alue, q has not been changed: therefore the -alue of Mq remains J. &ote that Mp does not refer to a single, specific -ariable. 0ts -alue changes as the -alue of p changes. Line !! set the -alue of this newly created -ariable to ?, as illustrated in 5igure d, and line !% prints the -alues ? and J. The function free is used in * to free storage of a dynamically allocated -ariable. The statement free,p.< ma)es any future references to the -ariable p illegal ,unless, of course, a new -alue is assigned to p by an assignment statement or by a call to malloc. *alling free"p! ma)es the storage occupied by necessary. p a-ailable for reuse, if

Chapter 3- 12

To illustrate the use of the free function, consider the following statements: ! % 9 > ? I J K L p N ,int /. malloc ,si0eo& ,int..< Mp N ?< q N ,int M. malloc ,si0eo& ,int..< Mq N K< free,p.< p N q< q N ,int M. malloc ,si0eo& ,int..< Mq N I< printf,Od Od Pn, Mp Mq.<

The -alues K and I are printed. 5igure Ka illustrates the situation after line >, where Mp and Mq ha-e both been allocated and gi-en -alues. 5igure Kb illustrates the effect of line ?, in which the -ariable to which p points has been freed. 5igure Kc illustrates line I, in which the -alue of p is changed to point to the -ariable Mq. 0n lines J and K, the -alue of q is changed to point to a newly created -ariable which is gi-en the -alue I in line K ,5igure Kd.. &ote that if malloc is called twice in succession and its -alue is assigned to the same -ariable, as in: p N ,int M. malloc ,si0eof ,int..< Mp N 9< p N ,int M. malloc ,si0eo& ,int..< Mp N J< the first copy of Mp is lost since its address was not sa-ed. The space allocated for dynamic -ariables can be accessed only through a pointer. 6nless the pointer to the first -ariable is sa-ed in another pointer, that -ariable will be lost. 0n fact, its storage cannot e-en be freed since there is no way to reference it in a call to free. This is an e+ample of storage that is allocated but cannot be referenced.

Chapter 3- 13

q ,a.

q ,b.

q p ,c.

p ,d.

5igure K The -alue C ,;ero. can be used in a * program as the null pointer. Any pointer -ariable may be set to this -alue. 6sually, a standard header to a * program includes the definition 1de&ine &6LL C to allow the ;ero pointer -alue to be written as NULL. This NULL pointer -alue does not reference a storage location but instead denotes the pointer that does not point to anything. The -alue NULL ,;ero. may be assigned to any pointer -ariable p, after which a reference to Mp is illegal. :owe-er, the actual effects of a call to free are not defined by the * language each implementation of * is free to de-elop its own -ersion of this function. 0n most * implementations, the storage for M p is freed but the -alue of p is left unchanged. This means that although a reference to p becomes illegal, there may be no way of detecting the illegality. The -alue of p is a -alid address and the ob=ect at that address of the proper type may be used as the -alue of p# p is called a dangling pointer. 0t is the programmer$s

Chapter 3- 14

responsibility ne-er to use such a pointer in a program. 0t is good practice to e+plicitly set p to NULL after e+ecuting free,p.. 3ne other dangerous feature associated with pointers should be mentioned. 0f p and q are pointer with the same -alue, the -ariables p and q are identical. /oth p and q refer to the same ob=ect. Thus, an assignment to p changes the -alue of q, despite the fact that neither q nor q are e+plicitly mentioned in the assignment statement to recogni;e the occurrence of such implicit results. p. 0t is the programmer$s responsibility to )eep trac) of which pointers are pointing where and to

(2

Linked Lists +sing 'ynamic ,aria$les

Suppose we ha-e a lin)ed list that consists of a set of nodes, each of which has two fields: a data field and a pointer to the ne+t node in the list. 0n addition, an e+ternal pointer points to the first node in the list. 'e use pointer -ariables to implement list pointers. Thus, we define the type of a pointer and a node by struct node E int data< struct node Mlin)< F< typede& struct node M&3(" T7< A node of this type is identical to the nodes of the array implementation e+cept that the link field is a pointer ,containing the address of the ne+t node in the list. rather than an integer ,containing the inde+ within an array where the ne+t node in the list is )ept.. Let us employ the dynamic allocation features to implement lin)ed lists. 0nstead of declaring an array to represent an aggregate collection of nodes, nodes are allocated and freed as necessary. The need for a declared collection of nodes is eliminated in dynamic allocation. The structures for head node and data node in * are gi-en below.

Chapter 3- 15

(truct node E int data < struct node Mlin) < F< (truct list E int count < struct node Mhead < F list!<

QMdeclare structure type MQ QMdeclare a pointer to structure of node typeMQ QM node is a self4referential structure MQ QMdeclare structure type MQ

QMdeclare a structure -ariable MQ

0n abo-e statements, pointer -ariable head will contain the address of first element of the list, list1. And pointer -ariable lin) will contain address of ne+t node in the list, list1.

Linked List 2perations


'e define ten operations. 5or each operation we define its name and pro-ide brief description and its calling parameters. 'e then present algorithms for each operation.

2peration -# Create a Linked List


"arlier, we ha-e defined the head structure list and the node structure node. 5ields of both structures are not initiali;ed yet i.e, the member -ariables are not assigned -alues. 3nly structures e+ists, as shown in figure a B b. The following algorithm will initiali;e the list to an empty state. Later on nodes can be added or deleted as the need arises. After the e+ecution of the algorithm, the situation is shown in figure b.

Chapter 3- 16

count

head

(a Head structure

data

lin)

( $ 'ata node structure


5igure L: Lin)ed list head and data nodes structures *reate List recei-es the head structure and initiali;es the metadata for the list. At this time, there are only two metadata entries< later we can add more to e+pand the capabilities of the lin)ed list. 5igure shows the header before and after it is initiali;ed by create list.

1
)o0nt (a) Before create

1
head

list1-head list1-)o0nt

( n0ll ( 0

0 )o0nt

head (b) After create

5igure !C: *reate List

Chapter 3- 17

Algorit"m 3 - Create linked list algorithm creatList (ref list <metadata>) Initializes metadata for a linked list. Pre 1 list list is metadata structure passed by reference list1; " null; Post Metadata initialized, and list is empty list1#!ead % return; end creatList 2peration 2 *nsert %ode # 0nsert &ode adds data to a lin)ed list. 'e need only its logical predecessor to insert a node into the list. 1i-en the predecessor, there are three steps to the insertion : !. %. 9. Allocate memory for the new node and insert data. oint the new node to its successor. oint the new node$s predecessor to the new node. a little analysis is needed to fully

# list1#count " $;

These steps appear to be simple, but

understand how to implement them. To insert a node into a list we need to )now the location of the node that precedes the new node. This node is identified by a pointer that can be in one of two states : it can contain the address of a node or it can be null. 'hen the predecessor is null, it means that there is no predecessor to the data being added. The logical conclusion is that we are either adding to an empty list or are at the beginning of the list. 0f the predecessor is no null, then we are adding somewhere after the first node R that
is, in the middle of the list or at the end of the list. Let$s discuss each of these situations in turn.

2peration 2a *nsert into Empty List # 'hen the head pointer of the list is null, then the list is empty. This situation is shown in 5igure !!. All that is necessary to add a node to an empty list is to assign the list head pointer the address of the new node and ma)e sure that its lin) field is a null pointer. Although we could use a constant to set the lin)
Chapter 3- 18

field of the new node, we use the null pointer contained in the list head. 'hy we use the list head null pointer will become apparent in the ne+t section. The pseudocode statements to insert a node into an empty list are shown below in the inset of figure !!. (a) Before add 75 head p2ew p2ew-3lin4 list# head ( list#head 5et lin4 to n0ll pointer ( p2ew Point list to first node

list

0 )o0nt

list

1 )o0nt

75 head p2ew

(b) After add 5igure !!: Add node to empty list

&ode the order of these two statements. 'e must first point the new node to its successor< then we can change the head pointer. 0f we re-erse these statements, we will end up with the new node pointing to itself, which would put our program into a ne-er4ending loop when we process the list. 2peration 2$ *nsert at 4eginning # 'e add at the beginning of the list anytime we need to insert a node before the first node of the list. 'e determine that we are adding at the beginning of the list by testing the predecessor pointer. 0f it is a null pointer, then there is no predecessor so we are at the beginning of the list. To insert a node at the beginning of the list we simply point the new node to the first node of the list and then set the head pointer to point to the new first node. 'e )now the address of the new node. The question at this point is how we can find the address of the first node currently in the list so we can

Chapter 3- 19

point the new node to it. The answer is simple. The first node$s address is stored in the head (a) Before add list 1 )o0nt 75 head 39 p2ew p2ew-3lin4 list# head
list

( (

list#head p2ew 75 head 39

2 )o0nt

p2ew (b) After add 5igure !%: Add node at beginning pointer. The pseudocode statements to insert at the beginning of the list are shown in the inset of figure !%. 0f you compare these two statements with the statements to insert into an empty list, you will see that they are the same. They are the same because, logically, inserting into an empty list is the same as inserting at the beginning of a list. 2peration 2c *nsert in 5iddle # 'hen we add a node anywhere in the middle of the list, the predecessor contains an address. This case is shown in 5igure !9. To insert a node between two nodes, we point the new node to its successor and then point its predecessor to the new node. The address of the new node$s successor can be found in the predecessor$s lin) field.

Chapter 3- 20

The pseudocode statements to insert a node in the middle of the list are shown in the inset of figure !9.

(a) Before add list 2 )o0nt 39 head 52 pPre p2ew-3lin4 pPre-3lin4 list 3 )o0nt ( ( p2ew pPre-3lin4 p2ew 39 head 52 pPre p2ew 75 75

(b) After add 5igure !9 : Add node in middle 2peration 2d *nsert at End # 'hen we are adding at the end of the list, we only need to point the predecessor to the new node. There is no successor to point to. 0t is necessary, howe-er, to set the new node$s lin) field to a null pointer. The statements to insert a node at the end of a list are shown below in the inset of figure !>. 'e can ta)e ad-antage of the e+isting lin)ed list structure. 'e )now that the last node in the list will ha-e a null lin) pointer. 0f we use this pointer rather than a null pointer constant, then the code becomes e+actly the same as the code for inserting in the middle of the list.

Chapter 3- 21

(a) Before add list 3 )o0nt 39 head 134 pPre p2ew-3lin4 pPre-3lin4 ( ( p2ew pPre-3lin4 p2ew 52 75

list

4 )o0nt

39 head

52

75

134 pPre (b) After add 5igure !> : Add node at end 6eneral *nsert %ode Algorit"m # &ow let$s write the algorithm that puts it all together and inserts a node into the list. 'e are gi-en the head pointer, the predecessor, and the data to be inserted. 'e allocate memory for the new node and ad=ust the lin) pointers appropriately. 'hen the algorithm is complete, it returns a /oolean R true if it was successful and false if there was no memory for the insert. The pseudocode is found in Algorithm %.
Algorit"m 3 2 *nsert linked list node (ref list 'al p(re 'al dataIn Pre <metadata>, <node pointer>, <data)ype>)

p2ew

algorithm insert&ode

Inserts data into a ne* node in t!e linked list. list is metadata structure to a 'alid list p(re is pointer to data+s lo,ical predecessor dataIn contains data to be inserted Post Return data !a'e been inserted in se-uence true if successful, false if memory o'erflo*

Chapter 3- 22

allocate (p&e*) if (memory o'erflo*) 1 return false

# % /

end if p&e*.>data " dataIn if (p(re null) Adding before first node or to empty list. 1 p&e*.>link list. !ead " list. !ead " p&e*

else Adding in middle or at end. 1 p&e*.>link p(re.>link " p(re.>link " p&e*

1 2 4

end if list.count " list.count 3 1 return true

1$ end insert&ode

2peration 3 3 'elete %ode # The delete node algorithm logically remo-es a node from the lin)ed list by changing -arious lin) pointers and then physically deleting the node from dynamic memory. To logically delete a node, we must first locate the node itself. A delete node is located by )nowing its address and its predecessor$s address. 3nce we locate the node to be deleted, we can simply change its predecessor$s lin) field to point to the deleted node$s successor. 'e then recycle the node bac) to dynamic memory. 'e need to be concerned, howe-er, about deleting the only node in a list. (eleting the only node results in an empty list, so we must be careful that in this case the head is set to a null pointer. The delete situations parallel those for add. 'e can delete the only node, the first node, a node in the middle of the list, or the last node of a list. As we e+plain below, these four situations reduce to only two combinations: delete the first node and delete any other node. 0n all cases, the node to be deleted is identified by a pointer ,pLoc..

Chapter 3- 23

2peration 3a 3 'elete .irst %ode or 2nly %ode # 'hen we delete the first node, we must reset the head pointer to point to the first node$s successor and then recycle the memory for the deleted node. 'e can tell (a) Before delete list 3 )o0nt 39 head 75 134

pPre

p6o)

6ist# head ( p6o)-3 lin4& re)7)le "p6o)!

list

2 )o0nt

"8e)7)led! head

75

134

pPre

p6o)
(b) After delete

5igure !? : (elete 5irst node we are deleting the first node by testing the predecessor. 0f the predecessor is a null pointer, we are deleting the first node. This situation is diagrammed in 5igure !?. The statements to delete the first node are shown below. 7ecycle is the pseudocode command to return a node$s space to dynamic memory. 0f you e+amine this logic carefully, you will note that it also applies when we are deleting the only node in the list. 0f the first node is the only node, then its lin) field is a null pointer. /ecause we mo-e its lin) field ,a null pointer. to the head pointer, the result is by definition an empty list.

Chapter 3- 24

2peration 3$ 3 'elete 5iddle or Last %ode Case # Same logic applies to deleting any node in either the middle or at the end of the (a) Before delete list 3 )o0nt 39 head 75 134

pPre

p6o)

pPre-3lin4 ( p6o)-3 lin4 re)7)le "P69C!

list

2 )o0nt

39 head

"8e)7)led!

134

pPre

p6o)

(b) After delete 5igure !I : (elete 1eneral *ase list. 5or both of these cases, we simply point the predecessor node to the successor of the node being deleted. The logic is shown in 5igure !I. 'e delete the last node automatically. 'hen the node being deleted is the last node of the list, its null pointer is mo-ed to the predecessor$s lin) field, ma)ing the predecessor the new logical end of the list. After the pointers ha-e been ad=usted, the current node is recycled. The delete general case pseudocode is shown below in the inset of figure !I. 2peration 3c 3 'elete %ode Algorit"m # The logic to delete a node is shown in Algorithm 9. The algorithm is gi-en a pointer to the head of the list, to the node to be deleted, and to the delete node$s predecessor. 0t copies the deleted node$s data to a data out area in the

Chapter 3- 25

calling program and then ad=usts the pointers before releasing the node$s memory.
Algorit"m 3 3 Linked list delete node algorithm delete&ode (ref list <metadata>, 'al p(re <node pointer>, 'al pLoc <node pointer>, ref data5ut <data)ype>) 6eletes data from a linked list and returns it to callin, module. Pre list is metadata structure to a 'alid list p(re is a pointer to predecessor node pLoc is a pointer to node to be deleted data5ut is 'ariable to recei'e deleted data Post data !a'e been deleted and returned to caller 1 data5ut " pLoc.>data if (p(re null) Deleting first node 1 list. !ead " pLoc.>link # else Deleting other nodes 1 p(re.>link " pLoc.>link % end if / list.count " list.count 7 1 0 recycle (pLoc) 1 return end delete&ode

2peration 7 3 (earc" List # A (earc" list is used by se-eral algorithms to locate data in a list. To retrie-e data from a list, we need to search the list and find the data. 0n addition, many user applications require that lists be searched to locate data. 'e present an algorithm to search for a )ey in an unordered list. 5or simplicity, we assume that all data -alues in the list are unique that no data -alue is repeated.
Algorit"m 3 7# (earc" +nordered Linked List algorithm searc!unorderedlist ('al list ref pLoc 'al key Pre <metadata>, <node pointer>, <keytype>)

8earc!es list and passes back address of node containin, key. list is metadata structure to a 'alid list pLoc is pointer to 'ariable for current node key is t!e tar,et bein, sou,!t Post pLoc points to node *it! e-ual data

Chapter 3- 26

.or. null if key is not found Return 1 true if found, false if not found pLoc " list.!ead loop (pLoc not null 9&6 key not e-ual pLoc.>data) 1 pLoc " pLoc.>link # % / end loop Set return value if (pLoc null) 1 found " false else 1 if (key e-ual pLoc.>data) 1 found " true end if 0 1 end if return found

end searc!List

2peration 8 3 Retrie9e %ode # After )nowing how to locate a node in the list, we can retrie-e data from that node. 7etrie-e node uses search mode to locate the data in the list. 0f the data are found, it mo-es the data to the output area in the calling module and returns true. 0f they are not found, it returns false. The pseudocode is shown in Algorithm ?.
Algorit"m 3 8# Retrie9e linked list node algorithm retrie'e&ode ('al list 'al key ref data5ut :etrie'es data from a linked list. Pre list is metadata structure to a 'alid list key is tar,et data to be retrie'ed data5ut is 'ariable to recei'e retrie'ed data Post Return 1 data placed in data5ut .or. error returned if not found true if successful, false if data not found found " searc!List (list, pLoc, key) if (found) 1 data5ut " pLoc.>data # % end if return found <metadata>, <key type>, <data)ype>)

Chapter 3- 27

end retrie'e&ode

2peration : *( 3 Empty List # rocessing logic often depends on whether there are data in a list. Thus, we pro-ide empty list, a simple module that returns a /oolean indicating that there are data in the list or that it is empty ,Algorithm I..
Algorit"m 3 : Empty linked list algorithm emptyList Pre Return end emptyList ('al list <metadata>)

:eturns ;oolean indicatin, *!et!er t!e list is empty. list is metadata structure to a 'alid list true if list empty, false if list contains data

1 return (list.count e-ual to zero)

2peration ; *( 3 .ull List # At first glance, &ull list appears to be as simple as empty list. 0t turns out to be a relati-ely comple+ algorithm to implement, howe-er. Sery few languages pro-ide the programmer with the capability to test how much memory is left in dynamic memory. 1i-en this limitation, the only way we can test for a full list is to try to allocate memory. 0f we are successful, we simply recycle the memory and return false R the list is not full. 0f we are unsuccessful, then we return true R dynamic memory is full. The pseudocode is shown in Algorithm J.
Algorit"m 3 ; .ull linked list algorithm fullList ('al list <metadata>) :eturns ;oolean indicatin, *!et!er or not t!e list is full. Pre Return 1 list is metadata structure to a 'alid list false if room for ne* node; true if memory full

allocate (p&e*) if (allocation successful) 1 recycle (p&e*) return false

# %

end if return true

end fullList

Chapter 3- 28

2peration < 3 List Count=(i0e#


List count is another simple, one4line module. 0t is necessary because the calling module has no direct access to the list structure. 0ts implementation is shown in Algorithm K.
Algorit"m 3 < Linked list count algorithm list<ount Pre Return end list<ount ('al list <metadata>)

:eturns inte,er representin, number of nodes in list. list is metadata structure to a 'alid list count for number of nodes in list

1 return (list. count)

2peration > 3 Tra9erse List # Algorithm that tra9erse a list start at the first node and e+amine each node in succession until the last node has been processed. Tra-ersal logic is used by se-eral different types of algorithms, such as changing a -alue in each node, printing the list, summing a field in the list, or calculating the a-erage of a field. Any application that requires that the entire list be processed uses a tra-ersal.

)o0nt list 2

pos

head

10

15

20

95

100

5igure !J : Lin)ed list tra-ersal To tra-erse the list we need a wal)ing pointer, a pointer that mo-es from node to node as each element is processed. Assuming a lin)ed list with a head structure, the following pseudocode uses a wal)ing pointer to tra-erse the list. "ach loop modifies the pointer to mo-e to the ne+t node in sequence as we tra-erse the list.

Chapter 3- 29

pos " list.!ead loop (pos not null) process (pos.>data) pos = pos->link

'e begin by setting the wal)ing pointer to the first node in the list. Then, using a loop, we continue until all of the data ha-e been processed. "ach loop calls a process module and passes it the data and then ad-ances the wal)ing pointer to the ne+t node. 'hen the last node has been processed, the wal)ing pointer becomes null and the loop terminates. /ecause we need to remember where we are in the list from one call to the ne+t, we need to add a current position pointer to the head structure. 0t )eeps trac) of the node processed after the last call. The head structure is shown in 5igure !J. The figure also shows how the position pointer is modified to mo-e from one node to the ne+t as the tra-ersal algorithm is called. "ach call also needs to )now whether we are starting from the beginning of the list or continuing from the last node processed. This information is communicated through the parameter list. The basic logic to tra-erse a list is shown in Algorithm L. 'e name it get%e?t because it is called to get the ne+t node in the tra-ersal.
Algorit"m 3 > Tra9erse linked list algorithm ,et&e=t (ref list 'al from>!ere ref data5ut in t!e list. Pre list is metadata structure to a 'alid list from>!ere is $ to start at t!e first element from>!ere is 1 to continue from current position data5ut is 'ariable to recei'e data Post Return 1 data5ut contains data and true returned .or. if end of list, returns false true if ne=t element located, false if end of list if (from>!ere is $) Start from first 1 if (list.count is zero) <metadata>, <;oolean>, <data)ype>)

)ra'erses a linked list. ?ac! call returns t!e location of an element

Chapter 3- 30

success " false 1 list.pos " list.!ead data5ut # success " list.pos.>data " true

else

end if

else Continue from pos 1 if (list.pos.>link e-ual null) nd of !ist 1 success " false else 1 list.pos " list.pos.>link data5ut # success # end if # % end if return success " list.pos.>data " true

end ,et&e=t

2peration -2 3 'estroy List#


'hen a list is no longer needed but the application is not done, the list should be destroyed. 'estroy list deletes any nodes still in the list and recycles their memory. 0t then sets the metadata to a null list condition. The code for destroy list is shown in Algorithm !C.
Algorit"m 3 -@ 'estroy linked list algorithm destroyList 6eletes all data in list Pre Post 1 1 dlt(tr list.!ead list is metadata structure to a 'alid list 9ll data deleted " list.!ead " dlt(tr.>link (ref list <metadata>)

loop (list.count not zero)

# list.count" list.count 7 1 % recycle (dlt(tr) end loop "o data left in list. Reset metadata. # % list.!ead " null return

Chapter 3- 31

end destroyList

2peration -3 3 Print List#


3ne of the more common uses of lin) list tra-ersals is to print the entire contents of a list. 0f the user selects the print list option from the menu, we tra-erse the list and print all of the )ey fields. 0n this program, we simply print a sequential number and the data from the node 'ithin a loop, we continue until all of the data ha-e been printed. The code is shown in Algorithm !!. Algorit"m --# Print Linked List
algorithm printList Pre Post 1 ('al list <metadata>) )!is al,orit!m tra'erses a linked list and prints t!e data in node. list is metadata structure to a 'alid list 9ll data !a'e been printed in se-uence

if (list.!ead null) 1 (rint else 1 print (@@@@ ;e,in 6ata (rint @@@@) sno " $ # dataptr " list.!ead % loop (dataptr.> link not null) 1 # sno " sno 3 1 print (sno, data(tr.>data) dataptr " dataptr.> link / end loop 0 print (@@@@ ?nd 6ata (rint @@@@) (&o data in list.)

# %

end if retrun

end printList

2t"er Linked List ,ariations


'e introduce two other useful lin)ed list -ariations R the circularly lin)ed list and the doubly lin)ed list. Circularly linked list 0n the circularly lin)ed list, the last node$s lin) points to the first node of the list, as shown in 5igure !K. *ircularly lin)ed list are primarily used in structures

Chapter 3- 32

that allow access to nodes in the middle of the list without starting at the beginning. 0n circular list, nodes form a ring: The list is finite and each node has a successor.

1st 2ode list 2 )o0nt 5 data lin4

2nd 2ode 10 data lin4

6ast 2ode

##

pos

rear

head

95 data lin4

5igure !K: A circularly lin)ed list

0nsertion and deletion into a circularly lin)ed list follow the same logic used in singly lin)ed list e+cept that the last node points to the first node. Therefore, when inserting and deleting the last node, in addition to updating the rear pointer in the header, we must also point the head field to the first node. 1i-en that we can directly access a node in the middle of the list through its data structure, we are then faced with a problem when searching the list. 0f the search target lies before the current node, how do we find itT 0n a singly lin)ed list, when we arri-e at the end of the list the search is complete. 0n a circular list, howe-er we automatically continue the search from the beginning of list. The ability to continue the search presents another problem. 'hat if the target does not e+istT 0n the singly lin)ed list, if we did not find the data we are loo)ing for, we stopped when we hit the end of list. 'ith a circular list, we sa-e the starting node$s address and stop when we ha-e circled around to it, as shown code below.
Loop (key not e-ual to pLoc.>node.data 9&6 pLoc.>link not e-ual to start9ddress)

*ircular list, in which the last node is followed by the first node, is usually preferable to non4circular one, for two reasons:

Chapter 3- 33

They pro-ide easy access to both ends of a list: notice that we do not need the first pointer in the header, because we can easily reach the first node with rear -> link -> data

the code for processing a circular list is often simpler than the code for processing a non4circular list 4 mainly because you donUt ha-e to constantly chec) if the ne t node is defined 4 it is always defined.

At the implementation le-el, howe-er, circularity does create one -ery tric)y case that is a -ery common source of bugs, and that is sin!leton lists. 'hen there is only one node in a circular list, it points to itself as the ne t node.

Special attention must be gi-en to handle singleton. 'ou$ly Linked or T!o-!ay Linked Lists 3ne of the most powerful -ariations of lin)ed lists is the doubly lin)ed list. A doubly lin)ed list is a lin)ed list structure in which each node has a pointer to both its successor and its predecessor. 5igure !L doubly lin)ed list.
: list
)o0nt head rear

is a representation of a

; 10

##

95

: < :a)4ward pointer ; < ;orward pointer 5igure R !L A doubly lin)ed list There are three pieces of metadata in the head structure: a count, a head pointer, and a rear pointer. Although a rear pointer is not required in all doubly lin)ed lists, it ma)es some of the list algorithms, such as insert and search, more efficient.

Chapter 3- 34

"ach node contains two pointers, a $ack!ard pointer to its predecessor and a &or!ard pointer to its successor. 0n 5igure !L these pointers are designated / and 5, respecti-ely. Another -ariation on the doubly lin)ed list is the doubly lin)ed circularly lin)ed list. 0n this -ariation, the last forward pointer points to the first node of the list and the bac)ward pointer of the first node points to the last node. 0f there is only one node in the list, both the forward and bac)ward pointers point to the node itself. 'ou$ly Linked List *nsertion 0nserting a node into a doubly lin)ed list follows the basic pattern of inserting a node into a singly lin)ed list, but we also need to connect both the forward and bac)ward pointers. A null doubly lin)ed list$s head and rear pointers are null. To insert a node into a null list, we simply set the head and rear pointers to point to the new node and set the forward and bac)ward pointers of the new node to null. The results of inserting a node into a null list are shown in 5igure %C,a.. 5igure %C,b. shows the case for inserting between two nodes. The new node needs to be set to point to both its predecessor and its successor, and they need to be set to point to the new node. /ecause the insert is in the middle of the list, the head structure is unchanged e+cept for adding to the count. 0nserting at the end of the list requires that the new node$s bac) pointer be set to point to its predecessor. /ecause there is no successor, the forward pointer is set to null. The rear pointer in the head structure must also be set to point to the new rear node. Algorithm %% contains the code to insert a node into a doubly lin)ed list.

Chapter 3- 35

list

0
)o0nt head rear

list

1
)o0nt head rear

pPre

p2ew

20
: :efore ;

20
: After ;

(a) Insert into null list list list

2
)o0nt head rear

3
)o0nt head rear p50))

pPre

20
: ;

40
: ;

20
: ;

40
: ;

p2ew

30
: :efore ;

30
: After ;

(b) Insert between two nodes

5igure R %C (oubly lin)ed list insert


Algorit"m 3 -2 'ou$ly linked list insert algorithm insert6bl (ref list p(re p8ucc 'al dataIn <metadata>, <nodepointer>, <nodepointer>, <data)ype>)

)!is al,orit!m inserts data into a doubly linked list. Pre list is metadata structure to a 'alid list ((re is pointer 'ariable for predecessor (8ucc is pointer 'ariable for successor dataIn contains t!e data to be inserted Post )!e data !a'e been inserted in se-uence Return <inte,er> $ A failedBdynamic memory o'erflo* 1 A successful 1 if (full list) 1 return $ end if # allocate (p&e*)

Chapter 3- 36

% /

p&e*.>data " dataIn if (p(re is null) $nserting before first node or into empty list 1 # p&e*.>back p&e*.>fore list.!ead " null " list.!ead " p&e*

!o#ate insertion point in list

else $nserting into middle or end of list 1 # p&e*.>back p&e*.>fore " p(re " p(re.>fore

p(re.>fore " p&e*

1 2

end if %est for insert at end of list and also #he#k not empty list if (p(re.>fore null and p(re not null) $nserting at end of list &set rear pointer 1 list.rear " p&e* $nserting in middle of list ' point su##essor to ne( 1 if (p8ucc not null) 1 p8ucc.>back " p&e* #he#k not empty list

else

1$ 11 1

end if list.count " list.count 3 1 return(1)

end insert6bl

Algorit"m 3 -2 Analysis 'e must loo) at se-eral points in this algorithm. 5irst, rather than returning a simple success or failure, we ha-e two different conditions and thus return two different -alues. 0f dynamic memory is full, we return a C, indicating memory o-erflow ,see Statement !.!... 0f we are successful, then we return a ! ,see Statement !9..

Chapter 3- 37

"to 75! list 3


)o0nt head rear pPred p=lt p50))

25
: ;

50
: ;

75
: ;

(a) Before delete

list

2
)o0nt head rear

25
: ; "8e)7)led!

75
: ;

(b) After deleting 50

5igure R %! (oubly lin)ed list delete

'ou$ly Linked List 'eletion


(eleting from a doubly lin)ed list requires that the deleted node$s predecessor, if any, be pointed to the deleted node$s successor and that the successor, if any, be set to point to the predecessor. This rather straight forward logic is shown in 5igure %!. 3nce we locate the node to be deleted, we simply change its predecessor$s and successor$s pointers and recycle the node. The pseudocode is shown in Algorithm !9. Algorit"m 3 -3 'ou$le linked list delete algorithm delete6bl (ref list 'al p6lt <metadata>, <nodepointer>,

ref dataout <datatype>) )!e al,orit!m deletes a node from a doubly linked list. Pre list is metadata structure to a 'alid list p6lt is a pointer to t!e node to be deleted
Chapter 3- 38

Post

node deleted

1 dataout " p6lt.>data Che#k if not first node if (p6lt.>back not null) Point prede#essor to su##essor 1 p(red # else " p6lt.>back )*irst node+ p(red.>fore " p6lt.>fore ,pdate head pointer 1 list.!ead " p6lt.>fore % end if Che#k if not last node / if (p6lt.>fore not null) Point su##essor to prede#essor 1 p8ucc 0 else " p6lt.>fore )!ast node+ p8ucc.>back " p6lt.>back Point rear to prede#essor 1 list.rear " p6lt.>back 1 end if 2 recycle (p6lt) 4 list.count " list.count . 1 1$ return end delete6bl Algorit"m 3 -3 Analysis Two points in this algorithm require further comment. 5irst, the search was done in the calling algorithm. 'hen we enter the doubly lin)ed list delete algorithm, we already )now the location of the node to be deleted. Second point concerns style. &ote that we created local -ariables to hold the predecessor pointer ,Statement %.!. and the successor pointer ,Statement ?.!.. 'e could ha-e used the delete pointer ,p(lt. together. To demonstrate the difference, compare the code in the algorithm with the

Chapter 3- 39

alternati-e code shown below. 6sing descripti-e pointers ma)es the logic easier to follow. 1. %. p6lt.>back.>fore " p6lt.>fore p6lt.>fore.>back " p6lt.>back

Chapter 3- 40

Вам также может понравиться