Data Structure BASICS (12) and data structure basics 12
Features of two-way linked list operation:
(1) "query" is the same as a single-chain table;
(2) When "insert" and "delete", you must modify the pointer in both directions.
However, for a two-way cyclic linked list, inserting at the end of the table is very fast. It only takes O (1) time, because there is a pointer to the front, therefore, the two-way cyclic linked list can easily find the elements at the end of the table. Therefore, the two-way cyclic linked list is suitable for frequent insertion at the end of the table.
Empty linked list:
Bidirectional cyclic linked list node structure:
Class DoubleListNode {private: Type data; DoubleListNode * prev; // The forward pointer field DoubleListNode * next; // The backward pointer field };
Because you need to use it in the DoubleList class, you need to transform it as follows:
Template <typename Type> class DoubleListNode {// friend element declaration friend class DoubleList <Type>; friend class ListIterator <Type>; template <typename T> friend ostream & operator <(ostream & OS, const DoubleList <T> & list); private: DoubleListNode (const Type & dataValue): data (dataValue ), prev (NULL), next (NULL) {} Type data; DoubleListNode * prev; // forward pointer field DoubleListNode * next; // post pointer field };
Bidirectional cyclic linked list structure:
Template <typename Type> class DoubleList {friend class ListIterator <Type>; template <typename T> friend ostream & operator <(ostream & OS, const DoubleList <T> & list ); public: DoubleList ();~ DoubleList (); void push_back (const Type & data); void push_front (const Type & data); void insert (int position, const Type & data); void pop_front (); void pop_back (); void remove (const Type & removeData); bool search (const Type & searchData) const; bool isEmpty () const {return (first-> next = first);} private: // insert node x to the void insertPrivate (DoubleListNode <Type> * previous, doubleListNode <Type> * x); void removePrivate (DoubleListNode <Type> * x); private: DoubleListNode <Type> * first ;};
Structure and analysis of linked lists:
// Construct the linked list template <typename Type> DoubleList <Type >:: DoubleList () {first = new DoubleListNode <Type> (0); first-> next = first; first-> prev = first ;}
// Destructor linked list template <typename Type> DoubleList <Type> ::~ DoubleList () {DoubleListNode <Type> * deleteNode = NULL; // Save the end element of the linked list DoubleListNode <Type> * tmp = first; // first point to the first real element first = first-> next; // all the way to the end of the linked list while (first! = Tmp) {deleteNode = first; first = first-> next; delete deleteNode;} // release the empty node (header) of the linked list to delete tmp ;}
Two main elements of linked list element insertion and deletion:
// Same as the private member // Insert the node template <typename Type> void DoubleList <Type >:: insertPrivate (DoubleListNode <Type> * previous, DoubleListNode <Type> * x) {x-> prev = previous; x-> next = previous-> next; previous-> next-> prev = x; previous-> next = x ;}
// Delete the node template <typename Type> void DoubleList <Type >:: removePrivate (DoubleListNode <Type> * x) {if (x = first) throw std :: range_error ("permission denied to delete first pointer"); x-> prev-> next = x-> next; x-> next-> prev = x-> prev; delete x ;}
Insert provided to the customer:
// Insert the template to the end of the table <typename Type> void DoubleList <Type >:: push_back (const Type & data) {DoubleListNode <Type> * newNode = new DoubleListNode <Type> (data); // locate the first node DoubleListNode <Type> * previous = first-> prev; // insert insertPrivate (previous, newNode);} // insert it to the header template <typename Type> void DoubleList <Type >:: push_front (const Type & data) {DoubleListNode <Type> * newNode = new DoubleListNode <Type> (data); // insert InsertPrivate (first, newNode);} // insert to any position (specified according to position) template <typename Type> void DoubleList <Type >:insert (int position, const Type & data) {if (position = 1) return push_front (data); int count = 1; // previous indicates a position before the position to be inserted DoubleListNode <Type> * previous = first-> next; // if the position is too large, the previous will stop when it finds the first element. // at this time, the element should be inserted to the end of the linked list while (count <position-1 & previous! = First) {++ count; previous = previous-> next;} // if the end of the linked list is found or the linked list is empty at this time, therefore, insert it to the end of the table if (previous = first) return push_back (data ); // if a suitable Insertion Location is found, DoubleListNode <Type> * newNode = new DoubleListNode <Type> (data); insertPrivate (previous, newNode );}
Deletion provided to the customer:
// Delete the last element template <typename Type> void DoubleList <Type >:: pop_back () {removePrivate (first-> prev );} // Delete the Header element template <typename Type> void DoubleList <Type >:: pop_front () {removePrivate (first-> next );} // Delete the template <typename Type> void DoubleList <Type >:: remove (const Type & removeData) {if (isEmpty () throw std :: range_error ("link list is empty"); for (DoubleListNode <Type> * searchNode = first-> Next; searchNode! = First; searchNode = searchNode-> next) {if (searchNode-> data = removeData) removePrivate (searchNode );}}
Check whether it exists in the linked list:
template <typename Type>bool DoubleList<Type>::search(const Type &searchData) const{ DoubleListNode<Type> *searchNode = first->next; while (searchNode != first) { if (searchNode->data == searchData) return true; searchNode = searchNode->next; } return false;}
Output all elements of the linked list (for testing ):
template <typename Type>ostream &operator<<(ostream &os, const DoubleList<Type> &list){ for (DoubleListNode<Type> *currentNode = (list.first)->next; currentNode != list.first; currentNode = currentNode->next) os << currentNode->data << ' '; return os;}
Design and Implementation of a two-way cyclic linked list iterator:
// Except operator -- added, almost no template <typename Type> class ListIterator {public: ListIterator (const DoubleList <Type> & _ list): list (_ list ), currentNode (_ list. first)-> next) {}// reload * operator const Type & operator * () const throw (std: out_of_range); Type & operator * () throw (std :: out_of_range); // overload-> operator const DoubleListNode <Type> * operator-> () const throw (std: out_of_range); DoubleListNode <Type> * operator-> () throw (std: out_of_range); // overload ++ operator ListIterator & operator ++ () throw (std: out_of_range); // Note: The returned value is, instead of reference ListIterator operator ++ (int) throw (std: out_of_range); // reload -- operator, // In fact this version -- operator is not perfect, // The ListIterator & operator -- (); // Note: The value returned here is not the reference ListIterator operator -- (int); bool isEmpty () const {return (currentNode = list. first);} private: const DoubleList <Type> & list; DoubleListNode <Type> * currentNode ;};
Template <typename Type> const Type & ListIterator <Type>: operator * () constthrow (std: out_of_range) {if (isEmpty () throw std :: out_of_range ("iterator is out of range"); // return the content pointed to by the current pointer: return currentNode-> data;} template <typename Type> Type & ListIterator <Type> :: operator * () throw (std: out_of_range) {return const_cast <Type &> (static_cast <const ListIterator <Type> &> (* this ). operator *());}
Template <typename Type> const DoubleListNode <Type> * ListIterator <Type>: operator-> () constthrow (std: out_of_range) {if (isEmpty () throw std :: out_of_range ("iterator is out of range"); // directly return the pointer return currentNode;} template <typename Type> DoubleListNode <Type> * ListIterator <Type> :: operator-> () throw (std: out_of_range) {return const_cast <DoubleListNode <Type> *> (static_cast <const ListIterator <Type> (* this ). operator-> ());}
Template <typename Type> ListIterator <Type> & ListIterator <Type>: operator ++ () throw (std: out_of_range) {if (isEmpty () throw std :: out_of_range ("iterator is out of range"); // the pointer moves forward currentNode = currentNode-> next; return * this ;} template <typename Type> ListIterator <Type>: operator ++ (int) throw (std: out_of_range) {ListIterator tmp (* this ); ++ (* this); // call the forward ++ version return tmp ;}
Template <typename Type> ListIterator <Type> & ListIterator <Type>: operator -- () {// forward the pointer currentNode = currentNode-> prev; return * this ;} template <typename Type> ListIterator <Type>: operator -- (int) {ListIterator <Type> tmp (* this); -- (* this ); return tmp ;}
Test code:
int main(){ cout << "-------- 1 --------" << endl; DoubleList<int> myList; for (int i = 0; i < 3; ++i) myList.push_back(i+1); for (int i = 0; i < 5; ++i) myList.push_front(10+i); for (int i = 0; i < 3; ++i) myList.push_back(i+1); ListIterator<int> iter(myList), iter2(myList); while (!iter.isEmpty()) { cout << *iter << ' '; ++ iter; ++ iter2; } cout << endl; -- iter2; while (!iter2.isEmpty()) { cout << *iter2 << ' '; iter2 --; } cout << endl; cout << "-------- 2 --------" << endl; cout << myList << endl; cout << "Test insert..." << endl; myList.insert(1, 14); myList.insert(2, 13); myList.insert(2, 13); myList.insert(88, 88); cout << myList << endl; myList.pop_back(); myList.pop_front(); cout << myList << endl; for (int i = 0; i < 5; ++i) { if (myList.search(i)) cout << i << ": Have found!" << endl; else cout << i << ": Not in the list!" << endl; } cout << "Test remove..." << endl; cout << myList << endl; int value; while (cin >> value) { try { myList.remove(value); } catch (const std::exception &e) { cerr << e.what() << endl; } cout << myList << endl; if (myList.isEmpty()) { cout << "empty" << endl; } else { cout << "not empty" << endl; } } return 0;}