/* LinkedList.h CIS 250 David Klick 2014-03-11 Modified 2018-02-10 - modified to hold objects instead of pointers - added a test function - added a tail pointer - added comments and a little code for conversion to doubly linked list - added amd changed functions to act more like a vector or stack - added a contains(T) function - added a subscript operator overload - added an insertion operator overload Code for a templated linked list class. Modified to support/require a previous pointer and tail pointer. */ #define DEBUG #ifndef __LINKED_LIST_H__ #define __LINKED_LIST_H__ #include #include #include #include using std::out_of_range; using std::ostream; using std::cout; using std::stringstream; #ifdef DEBUG #include using std::vector; #endif template class LinkedList { private: class Node { public: Node() : next(NULL) {} // *** Need to initialize prev ptr Node(T sentData) : data(sentData), next(NULL) {} // *** Need to initialize prev ptr T data; Node *next; // *** Need to declare a prev ptr }; Node *head; Node *tail; unsigned int count; public: LinkedList(); LinkedList(const LinkedList& lst); ~LinkedList(); void clear(); unsigned int size() const; bool empty() const; LinkedList& push_front(T data); LinkedList& push_back(T data); LinkedList& push(T data); T pop_front(); T pop_back(); T pop(); T peek_front(); T peek_back(); T peek(); void insertAt(unsigned int pos, T data); void deleteAt(unsigned int pos); bool remove(T data); bool contains(T data); T& operator[](const unsigned int pos); #ifdef DEBUG static bool test(); static bool check(const vector& v, const LinkedList& lst); #endif friend ostream& operator<<(ostream& strm, const LinkedList& lst) { Node *p = lst.head; strm << "["; while (p != NULL) { strm << p -> data; p = p -> next; if (p != NULL) strm << ","; } strm << "]"; return strm; } }; // Default constructor template LinkedList::LinkedList() : head(NULL), tail(NULL), count(0) { } // Copy constructor template LinkedList::LinkedList(const LinkedList& lst) { if (lst.head == NULL) { head = tail = NULL; count = 0; } else { head = new Node(lst.head->data); Node *ptrCurr = head; Node *ptrOld = lst.head->next; while (ptrOld != NULL) { ptrCurr->next = new Node(ptrOld->data); // *** Need to set the next node's prev ptr to current node ptrCurr = ptrCurr->next; ptrOld = ptrOld->next; } ptrCurr->next = NULL; tail = ptrCurr; count = lst.count; } } // Destructor template LinkedList::~LinkedList() { clear(); } // Remove all items from list template void LinkedList::clear() { Node *p = head; while (head != NULL) { p = head; head = head->next; delete p; } tail = NULL; count = 0; } // Return number of items in list template unsigned int LinkedList::size() const { return count; } // Return true if list has no items template bool LinkedList::empty() const { return count == 0; } // Add an item to the front of the list template LinkedList& LinkedList::push_front(T data) { Node *newNode = new Node(data); // *** Need to set the new node's prev ptr to NULL if (head != NULL) ;// *** then set the old head's prev ptr to the new node else tail = newNode; newNode->next = head; head = newNode; count++; return *this; } // Add an item to the back of the list template LinkedList& LinkedList::push_back(T data) { Node *newNode = new Node(data); newNode->next = NULL; if (tail != NULL) tail->next = newNode; else head = newNode; // *** Need to set the new node's prev ptr to what used to be the tail node tail = newNode; count++; return *this; } // Add an item to the list (alias for push_back) template LinkedList& LinkedList::push(T data) { return push_back(data); } // Remove an item from the front of the list template T LinkedList::pop_front() { if (count == 0) throw out_of_range("Attempt to pop from empty list"); Node *temp = head; T data; head = head->next; if (head != NULL) ; // *** then set the old head's prev ptr to the new node else tail = NULL; data = temp->data; delete temp; count--; return data; } // Remove an item from the back of the list template T LinkedList::pop_back() { if (count == 0) throw out_of_range("Attempt to pop from empty list"); Node *temp = tail; T data; // *** Need to reset tail to point to the prev list node if (tail != NULL) tail->next = NULL; else head = NULL; data = temp->data; delete temp; count--; return data; } // Remove an item from (the back of) the list template T LinkedList::pop() { return pop_back(); } // Return data from first list element template T LinkedList::peek_front() { if (count == 0) throw out_of_range("Attempt to peek at empty list"); return head->data; } // Return data from last list element template T LinkedList::peek_back() { if (count == 0) throw out_of_range("Attempt to peek at empty list"); return tail->data; } // Return data from last list element template T LinkedList::peek() { return peek_back(); } // Insert an item at a specified position in the list template void LinkedList::insertAt(unsigned int pos, T data) { if (pos > count) throw out_of_range("Attempt to insert beyond bounds"); Node *pCurr; Node *newNode; newNode = new Node(data); if (pos == 0) { if (head != NULL) ; // *** then set the old head's prev ptr to the new node else tail = newNode; newNode->next = head; head = newNode; } else if (pos == count) { tail->next = newNode; // *** Need to set the new node's prev ptr to the current tail tail = newNode; } else { pCurr = head; for (unsigned int i = 0; i < pos-1; i++) { assert (pCurr != NULL); pCurr = pCurr->next; } newNode->next = pCurr->next; if (pCurr->next != NULL) ; // *** then set the next node's prev ptr to the new node // Need to set the new node's prev ptr to the current node pCurr->next = newNode; } count++; } // Delete an item at a specified position template void LinkedList::deleteAt(unsigned int pos) { if (pos < 0 || pos >= count) throw out_of_range("Attempt to delete beyond bounds"); Node *pCurr; Node *temp; if (pos == 0) pop_front(); else if (pos == count-1) pop_back(); else { pCurr = head; for (unsigned int i = 0; i < pos-1; i++) { assert (pCurr != NULL); pCurr = pCurr->next; } temp = pCurr->next; pCurr->next = temp->next; // *** Need to set the prev ptr of the node after temp to the current node delete temp; count--; } } // Delete a particular value from list // Returns true or false based on whether value was found. template bool LinkedList::remove(T data) { Node *pCurr = head; while (pCurr != NULL && pCurr->data != data) { pCurr = pCurr->next; } if (pCurr == NULL) return false; if (head == pCurr) head = pCurr->next; else ; // *** Need to set the previous node's next ptr to whatever the current node points to as next if (tail == pCurr) ; // *** then set tail to be whatever node comes before the current node else ; // *** then set the next node's prev ptr to whatever comes before the current node delete pCurr; count--; return true; } // Find out if the list contains a particular value // Returns true or false based on whether value was found. template bool LinkedList::contains(T data) { Node *pCurr = head; while (pCurr != NULL) { if (pCurr->data == data) return true; pCurr = pCurr->next; } return false; } // Overload of [] operator template T& LinkedList::operator[](const unsigned int pos) { if (pos >= count) { stringstream strm; strm << "Attempted access beyond bounds [" << pos << "]"; throw out_of_range(strm.str()); } Node *p = head; unsigned int n = 0; while (n < pos) { p = p->next; n++; } return p->data; } #ifdef DEBUG template bool LinkedList::test() { int errors = 0; int tests = 0; LinkedList lst; vector v; // Test default constructor tests++; if (!check(v, lst)) { cout << "Error: default constructor\n"; errors++; } else { cout << "Passed: default constructor test\n"; } // Test push_front(T) function lst.push_front(2).push_front(1); v.push_back(1); v.push_back(2); tests++; if (!check(v, lst)) { cout << "Error: push_front(int)\n"; errors++; } else { cout << "Passed: push_front(int) test\n"; } // Test push_back(T) function lst.push_back(3); v.push_back(3); tests++; if (!check(v, lst)) { cout << "Error: push_back(int)\n"; errors++; } else { cout << "Passed: push_back(int) test\n"; } // Test push(T) function lst.push(4); v.push_back(4); tests++; if (!check(v, lst)) { cout << "Error: push(int)\n"; errors++; } else { cout << "Passed: push(int) test\n"; } // Test peek_front() function tests++; if (lst.peek_front() != v[0]) { cout << "Error: peek_front()\n"; errors++; } else { cout << "Passed: peek_front() test\n"; } // Test peek_back() function tests++; if (lst.peek_back() != v[v.size()-1]) { cout << "Error: peek_back()\n"; errors++; } else { cout << "Passed: peek_back() test\n"; } // Test peek() function tests++; if (lst.peek() != v[v.size()-1]) { cout << "Error: peek()\n"; errors++; } else { cout << "Passed: peek() test\n"; } // Test insert_at(int, T) function tests++; lst.insertAt(0, 11); lst.insertAt(2, 12); lst.insertAt(lst.size(), 13); v.insert(v.begin(), 11); v.insert(v.begin()+2, 12); v.insert(v.end(), 13); if (!check(v, lst)) { cout << "Error: insertAt(int, int)\n"; errors++; cout << "Is: "; for (unsigned int i=0; i lst2(lst); if (!check(v, lst2)) { cout << "Error: copy constructor\n"; errors++; cout << "Is: "; for (unsigned int i=0; i bool LinkedList::check(const vector& v, const LinkedList& lst) { if ((v.size() != lst.size()) || (lst.size() != lst.count)) return false; if (lst.count == 0 && (lst.head != NULL || lst.tail != NULL)) return false; if (lst.count != 0 && (lst.head == NULL || lst.tail == NULL)) return false; Node* p = lst.head; for (unsigned int i=0; i< lst.count; i++) { if (p == NULL || p->data != v[i]) return false; // *** Uncomment the next line when prev pointers exist // if (i == 0 && (lst.head != p || p->prev != NULL)) return false; if (i == lst.count-1 && (lst.tail != p || p->next != NULL)) return false; p = p->next; } return true; } #endif #endif