1 / 27

CSE 143

CSE 143. Linked Lists [Chapter 8.1-8.6, 8.8]. Linked Lists . A linked list is a collection of dynamically allocated nodes Each node contains at least one member that links to another node in the list.

ronniev
Télécharger la présentation

CSE 143

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CSE 143 Linked Lists [Chapter 8.1-8.6, 8.8]

  2. Linked Lists • A linked list is a collection of dynamically allocated nodes • Each node contains at least one member that links to another node in the list. • In the simplest case, each node has two members: a data item and a link field which points to the successor node. • Example: a list of 3 integers: 4 8 16 head

  3. Creating Nodes • First we’ll declare a struct which we’ll use to represent a node: • struct Node { • int data; • Node* next; • }; • Now we can create new nodes: • Node* p; • p = new Node; • p->data = 100; // shorthand for: (*p).data = 100 • p->next = NULL; // shorthand for: (*p).next = NULL • Note the use of the -> operator

  4. Manipulating Nodes • Draw the picture that results from the following code: • Node* front, * temp; • front = new Node; • front->data = 1; • front->next = new Node; // adding nodes • front->next->data = 2; • front->next->next = NULL; • temp = front; // deleting nodes • front = front->next; • delete temp;

  5. Using Linked Lists • Let’s write a Stack ADT which uses a linked list: • struct Node; // partial or "forward" declaration. • //the full decl is hidden in the impl. file • class IntStack { • public: • IntStack(); • void push(int item); • int pop(); • bool isEmpty(); • bool isFull(); • private: • Node* items; • };

  6. Stack Implementation: • struct Node { • int data; • Node* next; • }; • IntStack::IntStack() { items = NULL; } • void IntStack::push(int item) { • Node* temp = new Node; • temp->data = item; • temp->next = items; • items = temp; • }

  7. Stack Implementation (2) • int IntStack::pop() { • assert(!isEmpty()); • Node* p = items; • int rval = p->data; • items = items->next; • delete p; • return rval; • } • bool IntStack::isEmpty() { return (items == NULL); } • bool IntStack::isFull() { return false; }

  8. Stack Wrapup • What’s missing? • top() • sizeOf() - how would you write this? • Dynamic memory suite: copy constructor, destructor, assignment operator… • What are advantages/disadvantages of this implementation of a stack? • We’d like to separate out the linked list from the Stack. • The “right” way to do this is to re-implement our List ADT using a linked list

  9. Implementing a List ADT • Again, we’ll declare a struct to represent a node: • struct Node { • int data; • Node* next; • }; • Now our List ADT will look like this: items 4 8 16 cursor

  10. List Specification • We can use the same old List specification with a minor change: • struct Node; // again, a partial declaration • class IntList { • public: • IntList(); // The public interface remains • int sizeOf(); // the same! • void reset(); • // etc… • private: • Node* items; // Different representation!! • Node* cursor; • };

  11. Implementation • We need to implement at least the following suite of member functions: • IntList() // constructor • bool isEmpty(); // predicates • bool isFull(); • int data(); • int sizeOf(); • void reset(); // cursor manipulators • void advance(); • bool endOfList(); • void insertBefore(int item); // insertion & deletion • void insertAfter(int item); • void deleteItem();

  12. Implementation (1) • IntList::IntList() { • cursor = items = NULL; • } • bool IntList::isEmpty() { • return (items==NULL); • } • bool IntList::isFull() { • return false; // Can we do better than this? • } • bool IntList::endOfList() { • return cursor==NULL; • //doesn't quite match original List semantics • }

  13. Implementation (2) • void IntList::advance() { • assert(!endOfList()); • cursor = cursor->next; • } • void IntList::reset() { cursor = items; } • int IntList::sizeOf() { • int i=0; • Node* p; • for (Node* p = items; p != NULL; p = p->next) • i++; • return i; • }

  14. after implementation (1) • Inserting after the cursor. This code is a little broken... • void IntList::insertAfter(int item) { • Node* nodePtr = new Node; • nodePtr->data = item; • nodePtr->next = cursor->next; • cursor->next = nodePtr; • cursor = nodePtr; • } items 4 8 16 cursor

  15. Examples items 4 8 16 cursor items 4 8 16 cursor

  16. after implementation fixed: • void IntList::insertAfter(int item) { • assert(items == NULL || !endOfList()); • Node* nodePtr = new Node; • nodePtr->data = item; • if (items == NULL) { // empty list • nodePtr->next = NULL; • items = nodePtr; • } • else { • nodePtr->next = cursor->next; • cursor->next = nodePtr; • } • cursor = nodePtr; • }

  17. before Implementation • Inserting before the cursor is tricky too. • void IntList::insertBefore(int item) { • Node* nodePtr = new Node; • nodePtr->data = item; • nodePtr->next = cursor; • if (cursor == items) // cursor points to head • items = nodePtr; • else { • Node* prevPtr = Previous(); • prevPtr->next = nodePtr; • } • cursor = nodePtr; • }

  18. delete Implementation • Case 1: cursor points at first item • Case 2: cursor points somewhere else • void IntList::deleteItem() { • assert(cursor != NULL); // cursor must be valid • Node* temp = cursor; • if (cursor == items) • items = items->next; • else { • Node* previous = Previous(); • previous->next = cursor->next; • } • cursor = cursor->next; • delete temp; • }

  19. before and delete helper • The above code depends on a helper member function Previous(), which we’ll make private: • // return the pointer to the node previous to the • // node the cursor points at... • Node* IntList::Previous() • { • Node* p = items; • while (p->next != cursor) • p = p->next; • return p; • }

  20. Exercise 1: Manipulations • Draw the picture that results from this code: • IntList aList; • for (i=0; i<4; i++) • aList.insertAfter(i); • for (aList.reset(); !aList.endOfList(); • aList.advance()) • cout << aList.data();

  21. Exercise 2: Traversals • Count the occurrences of an item: • int occurrencesOf(List& l, int item) { int count = 0; • for (l.reset(); !l.endOfList(); l.advance()) • if (l.data() == item) • count++; • return count; • }

  22. Exercise 3: Deletion • A function to find and delete an item: • bool • findAndDelete(IntList& l, int item) { • for (l.reset(); !l.endOfList(); l.advance()) • if (l.data() == item) { • l.delete(); • return true; • } • return false; • }

  23. Variations on a theme • Doubly linked lists • Makes finding the previous pointer a breeze • Takes a little more space and complexity to manage the extra pointers • Circular lists • Can remove some special cases • Head and tail pointers. • Good for queues • Dummy nodes • Can remove some special cases

  24. Some Issues • Previous( ) is an expensive operation • Could maintain a "previous" pointer • Probably want our full dynamic memory suite: • Copy constructor • Deep copying assignment • Destructor • Making deep copies of large lists is expensive. • Solutions: always pass by reference. Can enforce this by making the copy constructor private...

  25. A Bigger Issue • Our list is not as generic as it could be: • It has the name of the element type (in this case, ints) hard-coded into it. • Solutions: • Rewrite the code (a poor - but common - solution) • Templates (cf. Lippman) • Inheritance (Chapters 10 and 11)

  26. List ADT Summary • We’ve seen 2 implementations of a List ADT: • Arrays: support efficient random access, but inefficient insertion and deletion, because we need to shift elements around • Linked-lists: support efficient insertion and deletion, but inefficient random access • What kind of tradeoffs are we making? • List as an ADT vs. a List as a data structure.

  27. Linked-Lists Summary • We’ve seen a very simple linked-list implementation. • Examples: Stack ADT, List ADT (more general) • Many variations: singly vs doubly linked, head and/or tail pointers, list cursors, dummy nodes • Pick the structure to fit the problem being solved • Common operations: traversals, insertions, deletions

More Related