Maintaining a Large Ordered List
5.4 LINKED LISTS WITH POINTERS TO POINTERS
Some programming languages support pointers to pointers. Fortran does not permit a pointer whose target is a pointer, but it permits a pointer to a structure whose only component is a pointer. (Compare arrays of pointers, described in Chap. 2). Such a structure is here called a BOX: its type is BOX_Type and its only component is a node pointer named Next_NP, as shown in Fig. 5.3. A pointer to a BOX provides a Fortran substitute for a pointer to the node pointer inside the BOX. This permits iterative linked list search with insertion and deletion of nodes.
Here each linked list node has two components, one of which holds data, as before. The second component is now a BOX whose node pointer component Next_NP designates the remainder of the list:
The target of Next_NP is the linked list successor node, if there is one; otherwise, Next_NP is null. The root of the linked list is a BOX named Root; if the linked list is empty, the node pointer component of Root, designated as Root%Next_NP, is null.
type, private :: BOX_Type
type (Node_Type), pointer :: Next_NP => Null( ) end type BOX_Type
type, private :: Node_Type type (Info_Type) :: Info type (BOX_Type) :: BOX end type Node_Type
type (BOX_Type), pointer, private :: Trav_BP ! "traveling" BOX pointer type (BOX_Type), target, private, save :: Root
FIGURE 5.3. Fortran Pointer-to-Pointer technique
The traveling BOX pointer Trav_BP points to a BOX in the predecessor node; the node pointer in this BOX is Trav_BP % Next_NP, the forward pointer to the current node. If Trav_BP % Next_NP is null, no current node is the “target of the target” of Trav_BP. Otherwise (with Fortran’s automatic dereferencing), the components of the current node are referred to as Trav_BP % Next_NP % Info and Trav_BP % Next_NP % BOX. The latter is a BOX that contains a pointer to the remainder of the linked list; Trav_BP advances when this BOX is assigned as its new target: Trav_BP => Trav_BP % Next_NP % BOX.
5.4 LINKED LISTS WITH POINTERS TO POINTERS
Fig. 5.4 illustrates insertion of a new node ahead of the current node. The subroutine Insert_Target is called with argument Trav_BP%Next_NP to insert the new node as the target of the forward node pointer in the predecessor node. The only change from the version shown at the beginning of this chap-ter appears in the poinchap-ter assignment Temp_NP%BOX%Next_NP=>Arg_NP that links the current target of Arg_NP to the forward pointer Temp_NP%BOX%Next_NP in the new node.
subroutineInsert_Target(Arg_NP,Item) type(Node_Type),pointer::Arg_NP type(Info_Type),intent(in)::Item type(Node_Type),pointer::Temp_NP
! startsubroutineInsert_Target allocate(Temp_NP)
Temp_NP%Info=Item
Temp_NP%BOX%Next_NP=>Arg_NP Arg_NP=>Temp_NP
return
endsubroutineInsert_Target
Fig. 5.5 illustrates the procedure Delete_Target, which is called with the actual argument Trav_BP
%Next_NP, the forward node pointer in the predecessor node. The only change from the earlier version appears in the pointer assignment Arg_NP=>Arg_NP%BOX%Next_NP.
functionDelete_Target(Arg_NP)result(Item) type(Node_Type),pointer::Arg_NP
type(Info_Type)::Item
type(Node_Type),pointer::Temp_NP
! startfunctionDelete_Target
FIGURE 5.4. Inserting a new node ahead of the current node
115
5.4 LINKED LISTS WITH POINTERS TO POINTERS
Remainder of List Johnson
Kennedy
Lincoln Current Node Predecessor Node
Traveling Pointer Temporary Pointer
Root Pointer
Remainder of List
Johnson Kennedy Lincoln
Current Node Predecessor Node
Traveling Pointer Temporary Pointer
Root Pointer
FIGURE 5.5. Deleting the current node
Item=Arg_NP%Info
Arg_NP=>Arg_NP%BOX%Next_NP deallocate(Temp_NP)
return
endfunctionDelete_Target
The first step of the procedure Look_Up assigns Root (which has been declared in the module Linked_List_Ops) as the initial target of Trav_BP.
type(BOX_Type),target::Root :
subroutineLook_Up(Item)
type(Info_Type),intent(in)::Item
! startsubroutineLook_Up
Trav_BP=>Root !MakeRootthetargetofTrav_BP do
if(associated(Trav_BP%Next_NP))then
if(Item%Key==Trav_BP%Next_NP%Info%Key)then callModify_Target(Trav_BP%Next_NP)
elseif(Item%Key<Trav_BP%Next_NP%Info%Key)then callInsert_Target(Trav_BP%Next_NP,Item)
else
Trav_BP=>Trav_BP%Next_NP%BOX !Movetosuccessornode.
cycle !Keeplooking.
endif else
callInsert_Target(Trav_BP%Next_NP,Item) !Insertatend.
endif return enddo
endsubroutineLook_Up
This iterative procedure is equivalent to the earlier recursive version, modified by tail recursion removal and with the introduction of pointers to pointers.
Say It with Fortran
Example 15.
Shown here is a slightly modified version of the subroutine Look_Up for the Pointer to Pointer technique, along with a main program. The BOX named Root is declared in module. Procedures that operate at the target level are the same as with recursion.! Example 15. Linked list with pointers to pointers.
module D15_M implicit none
public :: Look_Up, Print_List, Delete_List integer, parameter :: S_LEN = 20
type, public :: Info_Type
character (len = S_LEN) :: Key
integer, dimension(2) :: Data = (/ 0, 1 /) end type Info_Type
type, private :: BOX_Type
type (Node_Type), pointer :: Next_NP => Null( ) end type BOX_Type
type, private :: Node_Type type (Info_Type) :: Info type (BOX_Type) :: BOX end type Node_Type
type (BOX_Type), pointer, private :: Trav_BP !"traveling" BOX pointer type (BOX_Type), target, private, save :: Root
contains
subroutine Look_Up( Item )
type (Info_Type), intent(in) :: Item
! start subroutine Look_Up
Trav_BP => Root ! Make Root the target of Trav_BP The if constructs have been rearranged to agree with the recursive Fortran program shown earlier. Note that the enddo statement is never reached except from the cycle statement, which transfers control to the top of the do block.
do
if(associated(Trav_BP%Next_NP))then
if(Item%Key==Trav_BP%Next_NP%Info%Key)then callModify_Target(Trav_BP%Next_NP)
elseif(Item%Key<Trav_BP%Next_NP%Info%Key)then callInsert_Target(Trav_BP%Next_NP,Item)
else
Trav_BP=>Trav_BP%Next_NP%BOX !Movetosuccessornode.
cycle !Keeplooking.
endif else
callInsert_Target(Trav_BP%Next_NP,Item) !Insertatend.
endif return enddo contains
subroutineModify_Target(Arg_NP) :
endsubroutineModify_Target
117 subroutineInsert_Target(Arg_NP,Item)
:
endsubroutineInsert_Target endsubroutineLook_Up
subroutinePrint_List()
! startsubroutinePrint_List Trav_BP=>Root
dowhile(associated(Trav_BP%Next_NP)) callPrint_Target(Trav_BP)
Trav_BP=>Trav_BP%Next_NP%BOX !Advancetonextnode enddo
return contains
subroutinePrint_Target(Arg_BP) :
endsubroutinePrint_Target endsubroutinePrint_List subroutineDelete_List()
! startsubroutineDelete_List
dowhile(associated(Root%Next_NP))
print*,"Deleted:",Delete_Target(Root) enddo
return contains
functionDelete_Target(Arg_NP)result(Item) :
endfunctionDelete_Target endsubroutineDelete_List endmoduleD15_M
Section 5.4 Exercises
1. Write a recursive subroutine Print_List that prints information from the nodes of a linked list in reverse order: If the argument pointer is not null, call Print_List with the forward pointer (i.e., the remainder of the list) as its actual argument and then call Print_Target to print the current node.
Calling Print_List with the root node as the actual argument will print the entire list in reverse order.
2. Write a recursive subroutine Delete_List that deletes nodes from a linked list in reverse order: If the argument pointer is not null, call Delete_List with the forward pointer (i.e., the remainder of the list) as its actual argument and then call Delete_Target to delete the current node. (Print the Info component of each node as it is deleted.) Because the remainder of the list has already been deleted, the current node is the last node at the time it is deleted, and its deletion nullifies the argu-ment pointer. Calling Delete_List with the root node as the actual argument will delete the entire list in reverse order.
4. Implement the operation Look_Up with the data structure Item declared as a module variable;
modify the module procedures to inherit Item and modify the main program to import it.
5.4 LINKED LISTS WITH POINTERS TO POINTERS