Trying to emphasize what's important. Book covers more, but not all of it is as essential. Hard topic to understand the first time.
Chapter 7 in Shiflet text.
NodeType { Item_type item; NodeType *pNodeNext; }; NodeType *pNodeHead; /* head of the linked list */ NodeType *pNode; /* general node */ for (pNode = pNodeHead; pNode != NULL; pNode = pNode->pNodeNext) /* do something with the pNode element */
Linked list traversal is a basic operation. Must understand. Analagous to traversing an array of elements.
for (i = 0; i < MAXQUEUE; i++) /* do something with the ith array element */
One use is to have a generic traversal function that prints each node. (It is also possible to pass functions as arguments).
/* * /cs/cs2005/pub/example/morell.C -- more linked list stuff */ #include <iostream.h> typedef char Item_type; struct NodeType { Item_type item; NodeType *pNodeNext; }; class LinkedList { private: NodeType *pNodeHead; NodeType *MakeNode(Item_type item); public: LinkedList(); void Traverse(); int InsertItem(Item_type); int DeleteItem(Item_type); }; main() { LinkedList list; // ignoring return values list.InsertItem('d'); list.InsertItem('a'); list.InsertItem('k'); list.InsertItem('a'); list.Traverse(); list.DeleteItem('d'); list.DeleteItem('a'); list.DeleteItem('k'); list.Traverse(); } /* * LinkedList -- set up the list */ LinkedList::LinkedList() { pNodeHead = NULL; } /* * Traverse -- traverse a linked list and print each node */ void LinkedList::Traverse() { NodeType *pNode; for (pNode = pNodeHead; pNode != NULL; pNode = pNode->pNodeNext) cout << "Item: " << pNode->item << "\n"; }
Common to maintain a sorted linked list (for example a sorted queue). Rather than insert at the beginning or the end we must traverse the list to find where to insert.
Insertion in an arbitrary location can be tricky. We must always have a pointer to the node previous to the node we with to insert at (show picture).
Many approaches to do so. We will save a second pointer which points to the previous node.
/* * InsertItem -- insert the item into a sorted list, 0=success, -1=failure */ int LinkedList::InsertItem(Item_type item) { NodeType *pNode, *pNodeTmp, *pNodeSave; if ((pNode = MakeNode(item)) == NULL) return(-1); else { if (pNodeHead == NULL) { /* first node in the list */ pNodeHead = pNode; } else if (item < pNodeHead->item) { /* insert at the head */ pNode->pNodeNext = pNodeHead; pNodeHead = pNode; } else { pNodeSave = pNodeHead; /* trailing pointer */ pNodeTmp = pNodeHead->pNodeNext; while ((pNodeTmp != NULL) && (item >= pNodeTmp->item)) { pNodeSave = pNodeTmp; pNodeTmp = pNodeTmp->pNodeNext; } pNode->pNodeNext = pNodeSave->pNodeNext; pNodeSave->pNodeNext = pNode; } return(0); } }
Similar to insertion we must retain a pointer to the node before the one to be deleted.
/* * DeleteItem -- delete the first occurrence of item in the list, * 0=success, -1=failure */ int LinkedList::DeleteItem(Item_type item) { NodeType *pNodeTmp, *pNodeSave; if (pNodeHead == NULL) return(-1); else if (pNodeHead->item == item) { pNodeTmp = pNodeHead; pNodeHead = pNodeHead->pNodeNext; delete pNodeTmp; return(0); } else { pNodeSave = pNodeHead; /* trailing pointer */ pNodeTmp = pNodeHead->pNodeNext; while ((pNodeTmp != NULL) && (item != pNodeTmp->item)) { pNodeSave = pNodeTmp; pNodeTmp = pNodeTmp->pNodeNext; } if (pNodeTmp == NULL) return(-1); /* not found */ else { pNodeSave->pNodeNext = pNodeTmp->pNodeNext; delete pNodeTmp; return(0); } } }
To make insertions and deletions easier and allow traversals in forward or backward order we can use a doubly linked list.
struct NodeType { Item_type item; NodeType *pNodeNext; NodeType *pNodePrev; };
Linked Lists versus Contiguous Storage
Polynomial example in book is different than what we did. First assignment could be implemented using linked lists. Would not need the degree.
struct PolyNode { float fCoeff; int wExp; PolyNode *pPolyNext; };
Can use array indices instead of pointers. Same idea.