24.4. Linked ListsA linked list is a linear collection (i.e., a sequence) of self-referential class objects called nodes, connected by reference linksthus the term "linked" list. A program accesses a linked list via a reference to the first node of the list. Each subsequent node is accessed via the link-reference member stored in the previous node. By convention, the link reference in the last node of a list is set to Nothing to mark the end of the list. Data is stored in a linked list dynamicallythat is, each node is created as necessary. You can declare a node's class to store data of any type, including references to objects of other classes. Stacks and queues are also linear data structuresin fact, they are constrained versions of linked lists. Trees are non-linear 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 in the data structure is unpredictable. Unlike a linked list, the size of a conventional Visual Basic array cannot be altered, because the array size is fixed at creation time. Conventional arrays can become full, but linked lists become full only when the system has insufficient memory to satisfy dynamic memory allocation requests. Performance Tip 24.1
Performance Tip 24.2
Programmers can maintain linked lists in sorted order simply by inserting each new element at the proper point in the list (locating the proper insertion point does take time). Existing list elements do not need to be moved. Performance Tip 24.3
Performance Tip 24.4
Normally, linked-list nodes are not stored contiguously in memory. Rather, the nodes are said to be logically contiguous. Figure 24.3 illustrates a linked list with several nodes. Figure 24.3. Linked list graphical representation.
Performance Tip 24.5
Linked List ImplementationThe program in Fig. 24.4 and 24.5 uses an object of class List to manipulate a list of miscellaneous object types. The Main method of class ListTest (Fig. 24.5) creates a list of objects, inserts objects at the beginning of the list using List method InsertAtFront, inserts objects at the end of the list using List method InsertAtBack, deletes objects from the front of the list using List method RemoveFromFront and deletes objects from the end of the list using List method RemoveFromBack. After each insertion and deletion operation, the program invokes List method Print to display the current list contents. If an attempt is made to remove an item from an empty list, an EmptyListException is thrown. A detailed discussion of the program follows. Figure 24.4. ListNode, List and EmptyListException classes.
Figure 24.5. Linked list demonstration.
The program consists of four classesListNode (Fig. 24.4, lines 4-37), List (lines 40-153), EmptyListException (lines 156-160) and ListTest (Fig. 24.5). The classes in Fig. 24.4 create a linked-list library that can be reused throughout this chapter. You should place the code from Fig. 24.4 in its own class library project as described in Section 9.13. Name the project LinkedListLibrary. Encapsulated in each List object is a linked list of ListNode objects. Class ListNode (Fig. 24.4, lines 4-37) contains instance variables dataValue and nextNodeReference. Member dataValue can refer to any object. [Note: Typically, a data structure will contain data of only one type, or data of any type derived from one base type.] In this example, we use data of various types derived from Object to demonstrate that our List class can store data of any type. Member nextNodeReference stores a reference to the next ListNode object in the linked list. The ListNode constructors (lines 10-12 and 16-19) enable us to initialize a ListNode that will be placed at the end of a List or before a specific ListNode in a List, respectively. Class List (lines 40-153) contains Private instance variables firstNode (a reference to the first ListNode in a List) and lastNode (a reference to the last ListNode in a List). The constructors (lines 46-48 and 51-55) initialize both references to Nothing and enable us to specify the List's name for output purposes. InsertAtFront (lines 60-67), InsertAtBack (lines 72-80), RemoveFromFront (lines 83-99) and RemoveFromBack (lines 102-127) are the primary methods of class List. Method IsEmpty (lines 130-132) is a predicate method that determines whether the list is empty (i.e., the reference to the first node of the list is Nothing). Predicate methods typically test a condition and do not modify the object on which they are called. If the list is empty, method IsEmpty returns true; otherwise, it returns False. Method Print (lines 135-153) displays the list's contents. Class EmptyListException (lines 156-160) defines an exception class that we use to indicate illegal operations on an empty List. Class ListTest (Fig. 24.5) uses the linked-list library to create and manipulate a linked list. [Note: In the project containing Fig. 24.5, you must add a reference to the class library containing the classes in Fig. 24.4. If you use our existing example, you may need to update this reference.] Line 8 creates a new Listobject and assigns it to variable list. Lines 11-14 create data of various types to add to the list. Lines 17-24 use the List insertion methods to insert these values and use List method Print to output the contents of the list object after each insertion. Note that the values of the primitive-type variables are implicitly boxed in lines 17, 19 and 21 where Object references are expected. The code inside the try block (lines 30-45) removes objects via List deletion methods, outputs each removed object and displays the list after every deletion. If there is an attempt to remove an object from an empty list, the Catch at lines 46-48 catches the EmptyListException and displays an error message. Method InsertAtFrontOver the next several pages, we discuss each of the methods of class List in detail. Method InsertAtFront (Fig. 24.4, lines 60-67) places a new node at the front of the list. The method consists of three steps:
In Fig. 24.6, part (a) shows a list and a new node during the InsertAtFront operation and before the new node is linked into the list. The dashed lines and arrows in part (b) illustrate Step 3 of the InsertAtFront operation, which enables the node containing 12 to become the new list front. Figure 24.6. InsertAtFront operation.(a)
(b)
Method InsertAtBackMethod InsertAtBack (Fig. 24.4, lines 72-80) places a new node at the back of the list. The method consists of three steps:
In Fig. 24.7, part (a) shows a list and a new node during the InsertAtBack operation and before the new node has been linked into the list. The dashed lines and arrows in part (b) illustrate Step 3 of method InsertAtBack, which enables a new node to be added to the end of a list that is not empty. Figure 24.7. InsertAtBack operation.(a)
(b)
Method RemoveFromFrontMethod RemoveFromFront (Fig. 24.4, lines 83-99) removes the front node of the list and returns a reference to the removed data. The method throws an EmptyListException (line 85) if an attempt is made to remove a node from an empty list. Otherwise, the method returns a reference to the removed data. After determining that a List is not empty, the method consists of four steps to remove the first node:
In Fig. 24.8, part (a) illustrates a list before a removal operation. The dashed lines and arrows in part (b) show the reference manipulations. Figure 24.8. RemoveFromFront operation.
(b)
Method RemoveFromBackMethod RemoveFromBack (Fig. 24.4, lines 102-127) removes the last node of a list and returns a reference to the removed data. The method throws an EmptyListException (line 104) if the program attempts to remove a node from an empty list. The method consists of several steps:
In Fig. 24.9, part (a) illustrates a list before a removal operation. The dashed lines and arrows in part (b) show the reference manipulations. Figure 24.9. RemoveFromBack operation.(a)
(b)
Method PrintMethod Print (Fig. 24.4, lines 135-152) first determines whether the list is empty (line 136). If so, Print displays a String consisting of "Empty" and the list's name, then terminates. Otherwise, Print outputs the data in the list. The method prints a String consisting of "The", the list's name and "is:". Then line 143 creates ListNode variable current and initializes it with firstNode. While current is not Nothing, there are more items in the list. Therefore, the method displays current.Data (line 147), then assigns current.NextNode to current (line 148) to move to the next node in the list. Linear and Circular Singly Linked and Doubly Linked ListsThe kind of linked list we have been discussing is a singly linked listthe list begins with a reference to the first node, and each node contains a reference to the next node "in sequence." This list terminates with a node whose reference member has the value Nothing. A singly linked list may be traversed in only one direction. A circular, singly linked list (Fig. 24.10) begins with a reference to the first node, and each node contains a reference to the next node. The "last node" does not contain a Nothing reference. Instead the reference in the last node points back to the first node, thus closing the "circle." Figure 24.10. Circular, singly linked list.
A doubly linked list (Fig. 24.11) allows traversals both forward and backward. Such a list is often implemented with two "start references"one that refers to the first element of the list to allow front-to-back traversal of the list, and one that refers to the last element to allow back-to-front traversal. Each node has both a forward reference to the next node in the list and a backward reference to the previous node in the list. 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. A search for someone whose name begins with a letter near the end of the alphabet might begin from the back of the list. Figure 24.11. Doubly linked list.
In a circular, doubly linked list (Fig. 24.12), the forward reference of the last node refers to the first node, and the backward reference of the first node refers to the last node, thus closing the "circle." Figure 24.12. Circular, doubly linked list.
|