A linked list is a linear collection of self-referential class objects, called nodes, connected by pointer linkshence, the term "linked" list. A linked list is accessed via a pointer to the first node of the list. Each subsequent node is accessed via the link-pointer member stored in the previous node. By convention, the link pointer in the last node of a list is set to null (0) to mark the end of the list. Data is stored in a linked list dynamicallyeach node is created as necessary. A node can contain data of any type, including objects of other classes. If nodes contain base-class pointers to base-class and derived-class objects related by inheritance, we can have a linked list of such nodes and use virtual function calls to process these objects polymorphically. Stacks and queues are also linear data structures and, as we will see, can be viewed as constrained versions of linked lists. Trees are nonlinear data structures.
Lists of data can be stored in arrays, but linked lists provide several advantages. A linked list is appropriate when the number of data elements to be represented at one time is unpredictable. Linked lists are dynamic, so the length of a list can increase or decrease as necessary. The size of a "conventional" C++ array, however, cannot be altered, because the array size is fixed at compile time. "Conventional" arrays can become full. Linked lists become full only when the system has insufficient memory to satisfy dynamic storage allocation requests.
Performance Tip 21.1
![]() |
An array can be declared to contain more elements than the number of items expected, but this can waste memory. Linked lists can provide better memory utilization in these situations. Linked lists allow the program to adapt at runtime. Note that class template vector (introduced in Section 7.11) implements a dynamically resizable array-based data structure. |
Linked lists can be maintained in sorted order by inserting each new element at the proper point in the list. Existing list elements do not need to be moved.
Performance Tip 21.2
![]() |
Insertion and deletion in a sorted array can be time consumingall the elements following the inserted or deleted element must be shifted appropriately. A linked list allows efficient insertion operations anywhere in the list. |
Performance Tip 21.3
![]() |
The elements of an array are stored contiguously in memory. This allows immediate access to any array element, because the address of any element can be calculated directly based on its position relative to the beginning of the array. Linked lists do not afford such immediate "direct access" to their elements. So accessing individual elements in a linked list can be considerably more expensive than accessing individual elements in an array. The selection of a data structure is typically based on the performance of specific operations used by a program and the order in which the data items are maintained in the data structure. For example, it is typically more efficient to insert an item in a sorted linked list than a sorted array. |
Linked list nodes are normally not stored contiguously in memory. Logically, however, the nodes of a linked list appear to be contiguous. Figure 21.2 illustrates a linked list with several nodes.
Figure 21.2. A graphical representation of a list.

Performance Tip 21.4
![]() |
Using dynamic memory allocation (instead of fixed-size arrays) for data structures that grow and shrink at execution time can save memory. Keep in mind, however, that pointers occupy space and that dynamic memory allocation incurs the overhead of function calls. |
Linked List Implementation
The program of Figs. 21.321.5 uses a List class template (see Chapter 14 for information on class templates) to manipulate a list of integer values and a list of floating-point values. The driver program (Fig. 21.5) provides five options: 1) Insert a value at the beginning of the list, 2) insert a value at the end of the list, 3) delete a value from the beginning of the list, 4) delete a value from the end of the list and 5) end the list processing. A detailed discussion of the program follows. Exercise 21.20 asks you to implement a recursive function that prints a linked list backward, and Exercise 21.21 asks you to implement a recursive function that searches a linked list for a particular data item.
Figure 21.3. ListNode class-template definition.
(This item is displayed on pages 1003 - 1004 in the print version)
1 // Fig. 21.3: Listnode.h
2 // Template ListNode class definition.
3 #ifndef LISTNODE_H
4 #define LISTNODE_H
5
6 // forward declaration of class List required to announce that class
7 // List exists so it can be used in the friend declaration at line 13
8 template< typename NODETYPE > class List;
9
10 template< typename NODETYPE>
11 class ListNode
12 {
13 friend class List< NODETYPE >; // make List a friend
14
15 public:
16 ListNode( const NODETYPE & ); // constructor
17 NODETYPE getData() const; // return data in node
18 private:
19 NODETYPE data; // data
20 ListNode< NODETYPE > *nextPtr; // next node in list
21 }; // end class ListNode
22
23 // constructor
24 template< typename NODETYPE>
25 ListNode< NODETYPE >::ListNode( const NODETYPE &info )
26 : data( info ), nextPtr( 0 )
27 {
28 // empty body
29 } // end ListNode constructor
30
31 // return copy of data in node
32 template< typename NODETYPE >
33 NODETYPE ListNode< NODETYPE >::getData() const
34 {
35 return data;
36 } // end function getData
37
38 #endif
|
The program uses class templates ListNode (Fig. 21.3) and List (Fig. 21.4). Encapsulated in each List object is a linked list of ListNode objects. Class template ListNode (Fig. 21.3) contains private members data and nextPtr (lines 1920), a constructor to initialize these members and function getdata to return the data in a node. Member data stores a value of type NODETYPE, the type parameter passed to the class template. Member nextPtr stores a pointer to the next ListNode object in the linked list. Note that line 13 of the ListNode class template definition declares class List< NODETYPE > as a friend. This makes all member functions of a given specialization of class template List friends of the corresponding specialization of class template ListNode, so they can access the private members of ListNode objects of that type. Because the ListNode template parameter NODETYPE is used as the template argument for List in the friend declaration, ListNodes specialized with a particular type can be processed only by a List specialized with the same type (e.g., a List of int values manages ListNode objects that store int values).
Figure 21.4. List class-template definition.
(This item is displayed on pages 1004 - 1007 in the print version)
1 // Fig. 21.4: List.h
2 // Template List class definition.
3 #ifndef LIST_H
4 #define LIST_H
5
6 #include
7 using std::cout;
8
9 #include "listnode.h" // ListNode class definition
10
11 template< typename NODETYPE >
12 class List
13 {
14 public:
15 List(); // constructor
16 ~List(); // destructor
17 void insertAtFront( const NODETYPE & );
18 void insertAtBack( const NODETYPE & );
19 bool removeFromFront( NODETYPE & );
20 bool removeFromBack( NODETYPE & );
21 bool isEmpty() const;
22 void print() const;
23 private:
24 ListNode< NODETYPE > *firstPtr; // pointer to first node
25 ListNode< NODETYPE > *lastPtr; // pointer to last node
26
27 // utility function to allocate new node
28 ListNode< NODETYPE > *getNewNode( const NODETYPE & );
29 }; // end class List
30
31 // default constructor
32 template< typename NODETYPE >
33 List< NODETYPE >::List()
34 : firstPtr( 0 ), lastPtr( 0 )
35 {
36 // empty body
37 } // end List constructor
38
39 // destructor
40 template< typename NODETYPE >
41 List< NODETYPE >::~List()
42 {
43 if ( !isEmpty() ) // List is not empty
44 {
45 cout << "Destroying nodes ...
";
46
47 ListNode< NODETYPE > *currentPtr = firstPtr;
48 ListNode< NODETYPE > *tempPtr;
49
50 while ( currentPtr != 0 ) // delete remaining nodes
51 {
52 tempPtr = currentPtr;
53 cout << tempPtr->data << '
';
54 currentPtr = currentPtr->nextPtr;
55 delete tempPtr;
56 } // end while
57 } // end if
58
59 cout << "All nodes destroyed
";
60 } // end List destructor
61
62 // insert node at front of list
63 template< typename NODETYPE >
64 void List< NODETYPE >::insertAtFront( const NODETYPE &value )
65 {
66 ListNode< NODETYPE > *newPtr = getNewNode( value ); // new node
67
68 if ( isEmpty() ) // List is empty
69 firstPtr = lastPtr = newPtr; // new list has only one node
70 else // List is not empty
71 {
72 newPtr->nextPtr = firstPtr; // point new node to previous 1st node
73 firstPtr = newPtr; // aim firstPtr at new node
74 } // end else
75 } // end function insertAtFront
76
77 // insert node at back of list
78 template< typename NODETYPE >
79 void List< NODETYPE >::insertAtBack( const NODETYPE &value )
80 {
81 ListNode< NODETYPE > *newPtr = getNewNode( value ); // new node
82
83 if ( isEmpty() ) // List is empty
84 firstPtr = lastPtr = newPtr; // new list has only one node
85 else // List is not empty
86 {
87 lastPtr->nextPtr = newPtr; // update previous last node
88 lastPtr = newPtr; // new last node
89 } // end else
90 } // end function insertAtBack
91
92 // delete node from front of list
93 template< typename NODETYPE >
94 bool List< NODETYPE >::removeFromFront( NODETYPE &value )
95 {
96 if ( isEmpty() ) // List is empty
97 return false; // delete unsuccessful
98 else
99 {
100 ListNode< NODETYPE > *tempPtr = firstPtr; // hold tempPtr to delete
101
102 if ( firstPtr == lastPtr )
103 firstPtr = lastPtr = 0; // no nodes remain after removal
104 else
105 firstPtr = firstPtr->nextPtr; // point to previous 2nd node
106
107 value = tempPtr->data; // return data being removed
108 delete tempPtr; // reclaim previous front node
109 return true; // delete successful
110 } // end else
111 } // end function removeFromFront
112
113 // delete node from back of list
114 template< typename NODETYPE >
115 bool List< NODETYPE >::removeFromBack( NODETYPE &value )
116 {
117 if ( isEmpty() ) // List is empty
118 return false; // delete unsuccessful
119 else
120 {
121 ListNode< NODETYPE > *tempPtr = lastPtr; // hold tempPtr to delete
122
123 if ( firstPtr == lastPtr ) // List has one element
124 firstPtr = lastPtr = 0; // no nodes remain after removal
125 else
126 {
127 ListNode< NODETYPE > *currentPtr = firstPtr;
128
129 // locate second-to-last element
130 while ( currentPtr->nextPtr != lastPtr )
131 currentPtr = currentPtr->nextPtr; // move to next node
132
133 lastPtr = currentPtr; // remove last node
134 currentPtr->nextPtr = 0; // this is now the last node
135 } // end else
136
137 value = tempPtr->data; // return value from old last node
138 delete tempPtr; // reclaim former last node
139 return true; // delete successful
140 } // end else
141 } // end function removeFromBack
142
143 // is List empty?
144 template< typename NODETYPE >
145 bool List< NODETYPE >::isEmpty() const
146 {
147 return firstPtr == 0;
148 } // end function isEmpty
149
150 // return pointer to newly allocated node
151 template< typename NODETYPE >
152 ListNode< NODETYPE > *List< NODETYPE >::getNewNode(
153 const NODETYPE &value )
154 {
155 return new ListNode< NODETYPE >( value );
156 } // end function getNewNode
157
158 // display contents of List
159 template< typename NODETYPE >
160 void List< NODETYPE >::print() const
161 {
162 if ( isEmpty() ) // List is empty
163 {
164 cout << "The list is empty
";
165 return;
166 } // end if
167
168 ListNode< NODETYPE > *currentPtr = firstPtr;
169
170 cout << "The list is: ";
171
172 while ( currentPtr != 0 ) // get element data
173 {
174 cout << currentPtr->data << ' ';
175 currentPtr = currentPtr->nextPtr;
176 } // end while
177
178 cout << "
";
179 } // end function print
180
181 #endif
|
Figure 21.5. Manipulating a linked list.
(This item is displayed on pages 1007 - 1010 in the print version)
1 // Fig. 21.5: Fig21_05.cpp
2 // List class test program.
3 #include
4 using std::cin;
5 using std::cout;
6 using std::endl;
7
8 #include
9 using std::string;
10
11 #include "List.h" // List class definition
12
13 // function to test a List
14 template< typename T >
15 void testList( List< T > &listObject, const string &typeName )
16 {
17 cout << "Testing a List of " << typeName << " values
";
18 instructions(); // display instructions
19
20 int choice; // store user choice
21 T value; // store input value
22
23 do // perform user-selected actions
24 {
25 cout << "? ";
26 cin >> choice;
27
28 switch ( choice )
29 {
30 case 1: // insert at beginning
31 cout << "Enter " << typeName << ": ";
32 cin >> value;
33 listObject.insertAtFront( value );
34 listObject.print();
35 break;
36 case 2: // insert at end
37 cout << "Enter " << typeName << ": ";
38 cin >> value;
39 listObject.insertAtBack( value );
40 listObject.print();
41 break;
42 case 3: // remove from beginning
43 if ( listObject.removeFromFront( value ) )
44 cout << value << " removed from list
";
45
46 listObject.print();
47 break;
48 case 4: // remove from end
49 if ( listObject.removeFromBack( value ) )
50 cout << value << " removed from list
";
51
52 listObject.print();
53 break;
54 } // end switch
55 } while ( choice != 5 ); // end do...while
56
57 cout << "End list test
";
58 } // end function testList
59
60 // display program instructions to user
61 void instructions()
62 {
63 cout << "Enter one of the following:
"
64 << " 1 to insert at beginning of list
"
65 << " 2 to insert at end of list
"
66 << " 3 to delete from beginning of list
"
67 << " 4 to delete from end of list
"
68 << " 5 to end list processing
";
69 } // end function instructions
70
71 int main()
72 {
73 // test List of int values
74 List< int > integerList;
75 testList( integerList, "integer" );
76
77 // test List of double values
78 List< double > doubleList;
79 testList( doubleList, "double" );
80 return 0;
81 } // end main
|
Lines 2425 of the List class template (Fig. 21.4) declare private data members firstPtr (a pointer to the first ListNode in a List) and lastPtr (a pointer to the last ListNode in a List). The default constructor (lines 3237) initializes both pointers to 0 (null). The destructor (lines 4060) ensures that all ListNode objects in a List object are destroyed when that List object is destroyed. The primary List functions are insertAtFront (lines 6375), insertAtBack (lines 7890), removeFromFront (lines 93111) and removeFromBack (lines 114141).
Function isEmpty (lines 144148) is called a predicate functionit does not alter the List; rather, it determines whether the List is empty (i.e., the pointer to the first node of the List is null). If the List is empty, true is returned; otherwise, false is returned. Function print (lines 159179) displays the List's contents. Utility function getNewNode (lines 151156) returns a dynamically allocated ListNode object. This function is called from functions insertAtFront and insertAtBack.
Error-Prevention Tip 21.1
![]() |
Assign null (0) to the link member of a new node. Pointers should be initialized before they are used. |
The driver program (Fig. 21.5) uses function template testList to enable the user to manipulate objects of class List. Lines 74 and 78 create List objects for types int and double, respectively. Lines 75 and 79 invoke the testList function template with these List objects.
Member Function insertAtFront
Over the next several pages, we discuss each of the member functions of class List in detail. Function insertAtFront (Fig. 21.4, lines 6375) places a new node at the front of the list. The function consists of several steps:
Figure 21.6 illustrates function insertAtFront. Part (a) of the figure shows the list and the new node before the insertAtFront operation. The dashed arrows in part (b) illustrate Step 4 of the insertAtFront operation that enables the node containing 12 to become the new list front.
Figure 21.6. Operation insertAtFront represented graphically.

Member Function insertAtBack
Function insertAtBack (Fig. 21.4, lines 7890) places a new node at the back of the list. The function consists of several steps:
Figure 21.7 illustrates an insertAtBack operation. Part (a) of the figure shows the list and the new node before the operation. The dashed arrows in part (b) illustrate Step 4 of function insertAtBack that enables a new node to be added to the end of a list that is not empty.
Figure 21.7. Operation insertAtBack represented graphically.
(This item is displayed on page 1013 in the print version)

Member Function removeFromFront
Function removeFromFront (Fig. 21.4, lines 93111) removes the front node of the list and copies the node value to the reference parameter. The function returns false if an attempt is made to remove a node from an empty list (lines 9697) and returns TRue if the removal is successful. The function consists of several steps:
Figure 21.8 illustrates function removeFromFront. Part (a) illustrates the list before the removal operation. Part (b) shows the actual pointer manipulations for removing the front node from a nonempty list.
Figure 21.8. Operation removeFromFront represented graphically.
(This item is displayed on page 1014 in the print version)

Member Function removeFromBack
Function removeFromBack (Fig. 21.4, lines 114141) removes the back node of the list and copies the node value to the reference parameter. The function returns false if an attempt is made to remove a node from an empty list (lines 117118) and returns true if the removal is successful. The function consists of several steps:
Figure 21.9 illustrates removeFromBack. Part (a) of the figure illustrates the list before the removal operation. Part (b) of the figure shows the actual pointer manipulations.
Figure 21.9. Operation removeFromBack represented graphically.
(This item is displayed on page 1015 in the print version)

Member Function print
Function print (lines 159179) first determines whether the list is empty (line 162). If so, it prints "The list is empty" and returns (lines 164165). Otherwise, it iterates through the list and outputs the value in each node. The function initializes currentPtr as a copy of firstPtr (line 168), then prints the string "The list is: " (line 170). While currentPtr is not null (line 172), currentPtr->data is printed (line 174) and currentPtr is assigned the value of currentPtr->nextPtr (line 175). Note that if the link in the last node of the list is not null, the printing algorithm will erroneously print past the end of the list. The printing algorithm is identical for linked lists, stacks and queues (because we base each of these data structures on the same linked list infrastructure).
Linear and Circular Singly Linked and Doubly Linked Lists
The kind of linked list we have been discussing is a singly linked listthe list begins with a pointer to the first node, and each node contains a pointer to the next node "in sequence." This list terminates with a node whose pointer member has the value 0. A singly linked list may be traversed in only one direction.
A circular, singly linked list (Fig. 21.10) begins with a pointer to the first node, and each node contains a pointer to the next node. The "last node" does not contain a 0 pointer; rather, the pointer in the last node points back to the first node, thus closing the "circle."
Figure 21.10. Circular, singly linked list.

A doubly linked list (Fig. 21.11) allows traversals both forward and backward. Such a list is often implemented with two "start pointers"one that points to the first element of the list to allow front-to-back traversal of the list and one that points to the last element to allow back-to-front traversal. Each node has both a forward pointer to the next node in the list in the forward direction and a backward pointer to the next node in the list in the backward direction. If your list contains an alphabetized telephone directory, for example, a search for someone whose name begins with a letter near the front of the alphabet might begin from the front of the list. Searching for someone whose name begins with a letter near the end of the alphabet might begin from the back of the list.
Figure 21.11. Doubly linked list.

In a circular, doubly linked list (Fig. 21.12), the forward pointer of the last node points to the first node, and the backward pointer of the first node points to the last node, thus closing the "circle."
Figure 21.12. Circular, doubly linked list.

Introduction to Computers, the Internet and World Wide Web
Introduction to C++ Programming
Introduction to Classes and Objects
Control Statements: Part 1
Control Statements: Part 2
Functions and an Introduction to Recursion
Arrays and Vectors
Pointers and Pointer-Based Strings
Classes: A Deeper Look, Part 1
Classes: A Deeper Look, Part 2
Operator Overloading; String and Array Objects
Object-Oriented Programming: Inheritance
Object-Oriented Programming: Polymorphism
Templates
Stream Input/Output
Exception Handling
File Processing
Class string and String Stream Processing
Web Programming
Searching and Sorting
Data Structures
Bits, Characters, C-Strings and structs
Standard Template Library (STL)
Other Topics
Appendix A. Operator Precedence and Associativity Chart
Appendix B. ASCII Character Set
Appendix C. Fundamental Types
Appendix D. Number Systems
Appendix E. C Legacy Code Topics
Appendix F. Preprocessor
Appendix G. ATM Case Study Code
Appendix H. UML 2: Additional Diagram Types
Appendix I. C++ Internet and Web Resources
Appendix J. Introduction to XHTML
Appendix K. XHTML Special Characters
Appendix L. Using the Visual Studio .NET Debugger
Appendix M. Using the GNU C++ Debugger
Bibliography