540 likes | 944 Views
CSE 30331 Lecture 12 – Linked Lists …. Array vs. Linked List Pointers & Nodes Singly linked list Doubly linked list Circular linked lists Header (dummy) node Implementing ADT’s Stack Queue. Reading. Linked Lists Ford: Ch 9. Array vs. Linked List. Arrays (& STL vectors)
E N D
CSE 30331Lecture 12 – Linked Lists … • Array vs. Linked List • Pointers & Nodes • Singly linked list • Doubly linked list • Circular linked lists • Header (dummy) node • Implementing ADT’s • Stack • Queue
Reading • Linked Lists • Ford: Ch 9
Array vs. Linked List • Arrays (& STL vectors) • Direct access by index – O(1) • Insertion & deletion requires shifting – O(n) • Dynamically resizable only at one end • May require copying of all values • Pop & push at end – O(1) • Unless resizing is involved • Linked Lists ( & STL lists) • Sequential access – O(n) • Insertion & deletion without shifting – O(1) • Dynamically resizable anywhere –O(1) • Pop & push at either end – O(1)
front back Abstraction of a Linked List Singly Linked List Doubly Linked List
Linked List Nodes • Each Node is like a piece of a chain • To insert a new link, break the chain at the desired location and simply reconnect at both ends of the new piece.
Linked List Nodes • Removal is like Insertion in reverse.
Node Composition(singly linked list) • An individual Node is composed of two parts • a Data field containing the data stored by the node • a Pointer field containing the address of the next Node in the list.
front // front = NULL Deleting front of a 1-node list Deleting From the Front of a Singly Linked List
front // front = front->next Deleting front of a multi-node list Deleting From the Front of a Singly Linked List
front next target // // prev curr Removing a Target Node
Node data structure // forward declaration, just to keep the compiler happy template <typename T> class Stack; template <typename T> class Node { friend class Stack<T>; // let Stack access data & next public: Node(T value = T()) : data(value), next(NULL) { } private: T data; Node *next; };
Stack as a Singly Linked List template <typename T> class Stack { public: Stack(); ~Stack(); bool empty(); // true or false, status of stack T top(); // return copy of top node’s value void pop(); // remove top node void push(T value); // create new top node with value void clear(); // remove all nodes from stack private: Node<T> *topNode; };
Stack member functions template <typename T> Stack<T>::Stack() : topNode(NULL) { } template <typename T> Stack<T>::~Stack() { clear(); // make sure all memory is freed } template <typename T> bool Stack<T>::empty() { return (NULL == topNode); }
Stack member functions template <typename T> T Stack<T>::top(){ if (empty()) throw(“in top() with Empty Stack”); return topNode->data; } template <typename T> void Stack<T>::pop() { if (! empty()) { Node<T> *temp = topNode; // point to top node topNode = topNode->next; // point around to next node delete temp; // free node memory } }
Stack member functions template <typename T> void Stack<T>::push(T value) { Node<T> *temp = new Node<T>(value); // create new Node temp->next = topNode; // new node points to top Node topNode = temp; // now new node IS top Node } template <typename T> void Stack<T>::clear() { while(! empty()) pop(); }
Stack Tester #include "myStack.h" #include <string> #include <iostream> using namespace std; int main () { Stack<string> S; string name; cout << "Enter names ('done' to quit, 'purge' to clear)\n"; cin >> name; // get first name while (name != "done") { if (name == "purge") S.clear(); else S.push(name); cin >> name; // get another name } cout << "Names entered -- NOW reversed --\n"; while (! S.empty()) { cout << S.top() " "; S.pop(); } cout << endl; return 0; }
Queue as a Null Terminated Singly Linked List Need front and back pointers so we have access to both ends of list
Node data structure(Queue version ~ same as Stack) // forward declaration, just to keep the compiler happy template <typename T> class Queue; template <typename T> class Node { friend class Queue<T>; // let Queue access data & next public: Node(T value = T()) : data(value), next(NULL) { } private: T data; Node *next; };
Queue as a Singly Linked List template <typename T> class Queue { public: Queue(); ~Queue(); bool empty(); // true or false, status of queue T front(); // return copy of first value void pop(); // remove first node void push(T value); // create new last node with value void clear(); // remove all nodes from queue private: Node<T> *first, *last; };
Queue member functions template <typename T> Queue<T>::Queue() : first(NULL), last(NULL) { } template <typename T> Queue<T>::~Queue() { // same as Stack clear(); // make sure all memory is freed } template <typename T> bool Queue<T>::empty() {// essentially same as Stack return (NULL == first); }
Queue member functions template <typename T> T Queue<T>::front(){ if (empty()) throw(“in front() with Empty Queue”); return first->data; } template <typename T> void Queue<T>::pop() { if (! empty()) { Node<T> *temp = first; // point to first node first = first->next; // point around to next node delete temp; // free node memory } }
Queue member functions template <typename T> void Queue<T>::push(T value) { Node<T> *temp = new Node<T>(value); // create new Node if (empty()) first = temp; // new last node is also first node else last->next = temp; // new last node follows old last = temp; // now new node IS last Node } template <typename T> void Queue<T>::clear() { while(! empty()) pop(); }
Doubly Linked Lists • Singly linked list only allow easy traversal in one direction (forward) • Doubly linked lists allow easy traversal both directions (forward and backward) • The list can be linear • Having NULL pointers at both ends • The list can be circular • Having each end point back to the other • Usually this is implemented with a header node • That contains no data • That points to itself when list is empty
Circular Doubly Linked Lists • A Watch Band provides a good Real Life analogue for this Data Structure
Circular Doubly Linked Lists • Implemented on a Computer it might look something like this.
Implementing a Circular Doubly Linked List // forward declaration, just to keep the compiler happy template <typename T> class LinkedList; template <typename T> class Node { friend class LinkedList<T>; // let LinkedList access data & next public: Node(T value = T()) : data(value), next(NULL), prev(NULL) { } private: T data; Node *next, *prev; };
LinkedList(Circular and Doubly Linked) template <typename T> class LinkedList { public: LinkedList(); ~LinkedList(); int size(); // number of nodes in list T get(int pos); // return copy of value at pos void erase(int pos); // remove first node void insert(T value, int pos); // create new node with value at pos void clear(); // remove all nodes from queue int find(T value); // return position of first node with value private: Node<T> *last, // indicate beginning and end of list *current; // indicates current node int numNodes, // number of nodes in list currentPos; // indicates current position void moveTo(int pos); // moves current to desired Node };
LinkedList member functions template <typename T> LinkedList<T>::LinkedList() : numNodes(0), currentPos(-1) { last = new Node<T>; last->next = last; last->prev = last; current = last; // point to the header } template <typename T> LinkedList<T>::~LinkedList() { clear(); // make sure all Node memory is freed delete last; // free the header Node memory }
LinkedList member functions template <typename T> int LinkedList<T>::size() { return numNodes; } template <typename T> void LinkedList<T>::clear() { while(size() > 0) erase(0); // remove all nodes }
LinkedList member functions template <typename T> void LinkedList<T>::moveTo(int pos) { if ((pos < 0)|| (size() <= pos)) throw range_error(“LinkedList::moveTo() pos out of range”); while (currentPos < pos) // move forward along list { current = current->next; currentPos++; } while (currentPos > pos) // move backward along list { current = current->prev; currentPos--; } }
LinkedList member functions template <typename T> T LinkedList<T>::get(int pos) { try { moveTo(pos); // move to indicated position in list } catch (exception &e) { cerr << “Error from LinkedList::get()\n”; cerr << e.what(); exit 1; } return current->data; // return value of Node }
LinkedList member functions template <typename T> int LinkedList<T>::find(T value) { current = last->next; // point to first Node currentPos = 0; while (current != last) // move forward along list { if (current->data == value) break; current = current->next; currentPos++; } if (current == last) currentPos = -1; return currentPos; }
Deleting a Node at a Position // unlink the node (*curr) from the list curr->prev->next = curr->next; curr->next->prev = curr->prev; delete curr;
LinkedList member functions template <typename T> void LinkedList<T>::erase(int pos) { try { moveTo(pos); // move to indicated position in list } catch (exception &e) { cerr << “Error from LinkedList::get()\n”; cerr << e.what(); exit 1; } current->prev->next = current->next; // point around Node forward current->next->prev = current->prev; // point around Node backward delete current; // free Node memory }
Inserting a Node at a Position // insert newNode before curr newNode->prev = curr->prev; newNode->next = curr; curr->prev->next = newNode; curr->prev = newNode;
LinkedList member functions template <typename T> void LinkedList<T>::insert(T value, int pos) { try { moveTo(pos); // move to indicated position in list } catch (exception &e) { cerr << “Error from LinkedList::get()\n”; cerr << e.what(); exit 1; } Node<T> *newNode = new Node(value); // make a new Node with value newNode->prev = current->prev; // point back at previous Node newNode->next = current; // have it point to new Node current->prev->next = newNode; // point forward to next Node current->prev = newNode; // have it point to new Node }
So what is missing? • Copy Constructor & Assignment Operator • Need a deep copy • Traverse source list • Copy each node • Insert in destination list • Comparison Operator (==) • Compare list lengths, and if equal … • Traverse both lists • Compare corresponding nodes
Copy Constructor template <typename T> LinkedList<T>::LinkedList(LinkedList<T>& theList) : numNodes(0), currentPos(-1) { last = new Node<T>; last->next = last; last->prev = last; current = last; // point to the header intpos(0);// start at dummy node while (pos < theList.size()) // while nodes in theList { this->insert(theList.get(pos),pos)); // copy node and insert pos++; // move forward } }
Assignment Operator (=) template <typename T> LinkedList<T>& LinkedList<T>::operator=(LinkedList<T>& theList) { if (this == &theList) return; // same lists, nothing to do LinkedList<T> *newList; intpos(0);// start at dummy node while (pos < theList.size()) // while nodes in theList { newList.insert(theList.get(pos),pos)); // copy node and insert pos++; // move forward } return newList; }
Comparison (==) template <typename T> bool LinkedList<T>::operator==(LinkedList<T>& theList) { if (this == &theList) return true; // same lists, nothing to do if (this->size() != theList.size()) return false; // lists have different number of values bool same(true); // assume the same intpos(0);// start at first node while (same && (pos < size()) // while same and more nodes { if (this->get(pos) != theList.get(pos)) same = false; // nodes differ so lists do, too pos++; // move forward } return same; }
Comparison (==)(more efficient, without function calls) template <typename T> bool LinkedList<T>::operator==(LinkedList<T>& theList) { if (this == &theList) return true; // same lists, nothing to do if (this->numNodes != theList.numNodes) return false; // lists have different number of values bool same(true); // assume the same Node<T> *nodeThis(this->last->next);// start at first node Node<T> *nodeThat(theList.last->next); // start at first node while (same && (nodeThis != last) // while same and more nodes { if (nodeThis->data != nodeThat->data) same = false; // nodes differ so lists do, too nodeThis = nodeThis->next; // move forward nodeThat = nodeThat->next; // move forward } return same; }