|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
Introduction to listIn the second part of the tutorials, we will take a look at the STL list container. The list container implements the double linked list data structure. Unlike the vector container, list occupies uncontiguous memory, an element being accessed only through iterators. Creating a list. ConstructorsIn order to use the list container, we must include the #include <list> using namespace std; A list is created similar to a vector. In the next examples, we will create lists using the various forms of the constructor, we will show how to use the copy constructor, and also how to use the // create an empty list (of zero size) capable of holding doubles list<double> list0; cout << "Size of list0 is " << list0.size() << endl; // create a list with 5 empty elements list<double> list1(5); cout << "Size of list1 is " << list1.size() << endl; // create a list with 5 elements, each element having the value 10.2 list<double> list2(5, 10.2); cout << "list2: "; list<double>::iterator it; for(it = list2.begin(); it != list2.end(); ++it) cout << *it << " "; cout << endl; // create a list based on an array of elements // only the first 5 elements of the array are copied into the vector double array[8] = {3.45, 67, 10, 0.67, 8.99, 9.78, 6.77, 34.677}; list<double> list3(array, array + 5); cout << "list3: "; for(it = list3.begin(); it != list3.end(); ++it) cout << *it << " "; cout << endl; // use the copy constructor to copy list3 list into list3copy list list<double> list3copy(list3); cout << "list3copy: "; for(it = list3copy.begin(); it != list3copy.end(); ++it) cout << *it << " "; cout << endl; // assign 5 values of 10.2 to the list list<double> list4; list4.assign(5, 10.2); cout << "list4: "; for(it = list4.begin(); it != list4.end(); ++it) cout << *it << " "; cout << endl; // Output // Size of list0 is 0 // Size of list1 is 5 // list2: 10.2 10.2 10.2 10.2 10.2 // list3: 3.45 67 10 0.67 8.99 // list3copy: 3.45 67 10 0.67 8.99 // list4: 10.2 10.2 10.2 10.2 10.2 Just like in the vector case, the Printing the elements of a listUnlike vector, list doesn't have the [] operator overloaded and also it doesn't have the void print(list<double> lst, char * name) { list<double>::iterator it; cout << name << ": "; for(it = lst.begin(); it != lst.end(); ++it) cout << *it << " "; cout << endl; } We can also print the elements of a list in reverse order, using a reverse iterator and double array[8] = {3.45, 67, 10, 0.67, 8.99, 9.78, 6.77, 34.677}; list<double> lst(array, array + 5); print(lst, "lst"); cout << "lst in reverse order: "; list<double>::reverse_iterator rit; for(rit = lst.rbegin(); rit != lst.rend(); ++rit) cout << *rit << " "; // Output // lst: 3.45 67 10 0.67 8.99 // lst in reverse order: 8.99 0.67 10 67 3.45 Inserting elements into a listBesides the list<double> lst; list<double>::iterator it; // add elements to the end of the list lst.push_back(2.4); lst.push_back(4.5); lst.push_back(0.98); print(lst, "lst"); // insert value 6.7 in the second position in the list it = lst.begin(); lst.insert(++it, 6.7); print(lst, "lst"); // insert elements from the array at the end of the list double array[2] = {100.89, 789.76}; it = lst.end(); lst.insert(it, array, array + 2); print(lst, "lst"); // add elements to the beginning of the list lst.push_front(0.45); lst.push_front(0.56); lst.push_front(0.78); print(lst, "lst"); // Output // lst: 2.4 4.5 0.98 // lst: 2.4 6.7 4.5 0.98 // lst: 2.4 6.7 4.5 0.98 100.89 789.76 // lst: 0.78 0.56 0.45 2.4 6.7 4.5 0.98 100.89 789.76 Removing elements from a listThe double array[10] = {3.45, 67, 10, 0.67, 8.99, 9.78, 6.77, 34.677, 100.67, 0.99}; list<double> lst(array, array + 10); list<double>::iterator it; print(lst, "lst"); // remove the last element of the list lst.pop_back(); print(lst, "lst"); // remove the second element of the list it = lst.begin(); lst.erase(++it); print(lst, "lst"); // remove the first element of the list lst.pop_front(); print(lst, "lst"); // Output // lst: 3.45 67 10 0.67 8.99 9.78 6.77 34.677 100.67 0.99 // lst: 3.45 67 10 0.67 8.99 9.78 6.77 34.677 100.67 // lst: 3.45 10 0.67 8.99 9.78 6.77 34.677 100.67 // lst: 10 0.67 8.99 9.78 6.77 34.677 100.67 For removing elements from the list, we can also use the double array[10] = {3.45, 67, 19.25, 0.67, 8.99, 9.78, 19.25, 34.677, 100.67, 19.25}; list<double> lst(array, array + 10); print(lst, "lst"); // remove all elements with value 19.25 from the list lst.remove(19.25); print(lst, "lst"); // Output // lst: 3.45 67 19.25 0.67 8.99 9.78 19.25 34.677 100.67 19.25 // lst: 3.45 67 0.67 8.99 9.78 34.677 100.67 For conditional removing of elements from a list, we use the In the following example, we remove all the elements from the list that are greater or equal to 15. First, we define the predicate, which is bool predicate(double& element) { return (element >= 15.0) ? true : false; } Next, we call double array[10] = {3.45, 67, 19.25, 0.67, 8.99, 9.78, 19.25, 34.677, 100.67, 19.25}; list<double> lst(array, array + 10); print(lst, "lst"); // remove all elements greater or equal to 15.0 from the list lst.remove_if(predicate); print(lst, "lst"); // Output // lst: 3.45 67 19.25 0.67 8.99 9.78 19.25 34.677 100.67 19.25 // lst: 3.45 0.67 8.99 9.78 For removing duplicate elements from a list, we use the double array[10] = {3.45, 67, 67, 0.67, 8.99, 8.99, 8.99, 34.677, 100.67, 19.25}; list<double> lst(array, array + 10); print(lst, "lst"); // remove all duplicate elements from the list lst.unique(); print(lst, "lst"); // Output // lst: 3.45 67 67 0.67 8.99 8.99 8.99 34.677 100.67 19.25 // lst: 3.45 67 0.67 8.99 34.677 100.67 19.25 The second version of First, we define the binary predicate which tests if two elements are "almost equal". #include <math.h> bool almost_equal(double& el1, double& el2) { return (fabs(el1 - el2) <= 0.1) ? true : false; } Next, we call the double array[10] = {3.45, 3.455, 67, 0.67, 8.99, 9.0, 9.01, 34.677, 100.67, 19.25}; list<double> lst(array, array + 10); print(lst, "lst"); // remove all duplicate elements from the list lst.unique(almost_equal); print(lst, "lst"); // Output // lst: 3.45 3.455 67 0.67 8.99 9 9.01 34.677 100.67 19.25 // lst: 3.45 67 0.67 8.99 34.677 100.67 19.25 The functionality of list<double> lst; lst.push_back(0.67); lst.push_back(7.89); lst.push_back(3.56); if(lst.empty()) cout << "lst is empty" << endl; else cout << "lst is not empty" << endl; // remove all the elements from the list lst.clear(); if(lst.empty()) cout << "lst is empty" << endl; else cout << "lst is not empty" << endl; // Output // lst is not empty // lst is empty Sizing and resizing a listUnlike the vector container, list doesn't have the list<double> lst; lst.push_back(0.67); lst.push_back(7.89); lst.push_back(3.56); lst.push_back(10.67); lst.push_back(9.89); cout << "lst size is " << lst.size() << endl; print(lst, "lst"); // case when new size <= size of list lst.resize(3); cout << "lst size is " << lst.size() << endl; print(lst, "lst"); // case when new size > size of list lst.resize(10); cout << "lst size is " << lst.size() << endl; print(lst, "lst"); // Output // lst size is 5 // lst: 0.67 7.89 3.56 10.67 9.89 // lst size is 3 // lst: 0.67 7.89 3.56 // lst size is 10 // lst: 0.67 7.89 3.56 0 0 0 0 0 0 0 Reversing a listIn order to reverse the order of the elements in a list, we use the list<double> lst; lst.push_back(0.67); lst.push_back(7.89); lst.push_back(3.56); lst.push_back(10.67); lst.push_back(9.89); print(lst, "lst"); // reverse the elements of the list lst.reverse(); print(lst, "lst reversed"); // Output // lst: 0.67 7.89 3.56 10.67 9.89 // lst reversed: 9.89 10.67 3.56 7.89 0.67 Unlike the case of the reverse iterator, when we just print the elements of the list in reverse order, the Sorting a listFor sorting the elements of a list, we use the double array[10] = {3.45, 3.455, 67, 0.67, 8.99, 9.0, 9.01, 34.677, 100.67, 19.25}; list<double> lst(array, array + 10); print(lst, "lst"); // sort the list; // < operator will be used as default // the elements will be sorted in ascending order lst.sort(); print(lst, "lst in ascending order"); // sort the list; specify the sorting function // > operator will be used in this case // the elements will be sorted in descending order lst.sort( greater<double>() ); print(lst, "lst in descending order"); // sort the list; specify the sorting function // < operator will be used in this case // the elements will be sorted in descending order lst.sort( less<double>() ); print(lst, "lst in ascending order"); // Output // lst: 3.45 3.455 67 0.67 8.99 9 9.01 34.677 100.67 19.25 // lst in ascending order: 0.67 3.45 3.455 8.99 9 9.01 19.25 34.677 67 100.67 // lst in descending order: 100.67 67 34.677 19.25 9.01 9 8.99 3.455 3.45 0.67 // lst in ascending order: 0.67 3.45 3.455 8.99 9 9.01 19.25 34.677 67 100.67 Exchanging two listsExchanging the content of two lists is done using the double odds[5] = {1.0, 3.0, 5.0, 7.0, 9.0}; double evens[5] = {2.0, 4.0, 6.0, 8.0, 10.0}; list<double> lst1(odds, odds + 5); list<double> lst2(evens, evens + 5); cout << "The lists before swap: " << endl; print(lst1, "lst1"); print(lst2, "lst2"); // swap the contents of the two lists lst1.swap(lst2); cout << "The lists after swap: " << endl; print(lst1, "lst1"); print(lst2, "lst2"); // Output // The lists before swap: // lst1: 1 3 5 7 9 // lst2: 2 4 6 8 10 // The lists after swap: // lst1: 2 4 6 8 10 // lst2: 1 3 5 7 9 Merging two listsOne way to copy the elements of a list into another list is to use the list<double> list1; list<double> list2; list1.push_back(1.0); list1.push_back(5.0); list1.push_back(6.0); list1.push_back(7.0); list1.push_back(8.0); list2.push_back(2.0); list2.push_back(3.0); list2.push_back(4.0); cout << "Before splice: " << endl; print(list1, "list1"); print(list2, "list2"); list<double>::iterator it1 = list1.begin(); ++it1; // move all the elements of list2 into list1, starting from the second position list1.splice(it1, list2); cout << "After splice: " << endl; print(list1, "list1"); print(list2, "list2"); // Output // Before splice: // list1: 1 5 6 7 8 // list2: 2 3 4 // After splice: // list1: 1 2 3 4 5 6 7 8 // list2: In the second form, we call list<double> list1; list<double> list2; list1.push_back(1.0); list1.push_back(5.0); list1.push_back(6.0); list1.push_back(7.0); list1.push_back(8.0); list2.push_back(2.0); list2.push_back(3.0); list2.push_back(4.0); cout << "Before splice: " << endl; print(list1, "list1"); print(list2, "list2"); list<double>::iterator it1 = list1.begin(); ++it1; list<double>::iterator it2 = list2.begin(); // move only the first element of list2 into list1, in the second position list1.splice(it1, list2, it2); cout << "After splice: " << endl; print(list1, "list1"); print(list2, "list2"); // Output // Before splice: // list1: 1 5 6 7 8 // list2: 2 3 4 // After splice: // list1: 1 2 5 6 7 8 // list2: 3 4 The third way of calling list<double> list1; list<double> list2; list1.push_back(1.0); list1.push_back(5.0); list1.push_back(6.0); list1.push_back(7.0); list1.push_back(8.0); list2.push_back(2.0); list2.push_back(3.0); list2.push_back(4.0); cout << "Before splice: " << endl; print(list1, "list1"); print(list2, "list2"); list<double>::iterator it1; list<double>::iterator it2begin; list<double>::iterator it2end; it1 = list1.end(); it2begin = list2.begin(); it2end = list2.end(); --it2end; // move from the first to the last element of list2 at the end of list1 // observe that the last element of list2 is not moved list1.splice(it1, list2, it2begin, it2end); cout << "After splice: " << endl; print(list1, "list1"); print(list2, "list2"); // Output // Before splice: // list1: 1 5 6 7 8 // list2: 2 3 4 // After splice: // list1: 1 5 6 7 8 2 3 // list2: 4 Another useful function for merging two lists is list<double> list1; list<double> list2; list1.push_back(1.0); list1.push_back(5.0); list1.push_back(6.0); list1.push_back(7.0); list1.push_back(8.0); list2.push_back(2.0); list2.push_back(3.0); list2.push_back(4.0); cout << "Before merge: " << endl; print(list1, "list1"); print(list2, "list2"); // merge the two lists list1.merge(list2); cout << "After merge: " << endl; print(list1, "list1"); print(list2, "list2"); // Output // Before merge: // list1: 1 5 6 7 8 // list2: 2 3 4 // After merge: // list1: 1 2 3 4 5 6 7 8 // list2: Two dimensional listsTwo dimensional lists are lists of lists. list< list<double> > matrix; list<double> lst1(5, 6.713); list<double> lst2(6, 5.678); matrix.push_back(lst1); matrix.push_back(lst2); list< list<double> >::iterator it2d; for(it2d = matrix.begin(); it2d != matrix.end(); it2d++) print(*it2d, "row"); // Output // row: 6.713 6.713 6.713 6.713 6.713 // row: 5.678 5.678 5.678 5.678 5.678 5.678 Above, we call Lists of user defined elementsLists can also store user defined elements. In what follows, we create the class class Person { char * name; char sex; int age; public: // constructor Person() { name = new char[strlen("Anonymous") + 1]; sex = 'N'; age = 0; } // constructor Person(char * pName, char pSex, int pAge) : sex(pSex), age(pAge) { name = new char[strlen(pName) + 1]; strcpy(name, pName); } // copy constructor Person(const Person& rhs) : sex(rhs.sex), age(rhs.age) { name = new char[strlen(rhs.name) + 1]; strcpy(name, rhs.name); } // overload the assignment operator Person& operator=(const Person& rhs) { name = new char[strlen(rhs.name) + 1]; strcpy(name, rhs.name); sex = rhs.sex; age = rhs.age; return *this; } // overload the == operator // for sorting purposes, we consider that two Person objects are "equal" // if they have the same age bool operator==(const Person& rhs) const { return (age == rhs.age) ? true : false; } // overload the < operator // for sorting purposes, we consider that a Person object is "less than" another // if it's age is less than the other object's age bool operator<(const Person& rhs) const { return (age < rhs.age) ? true : false; } // overload the > operator // for sorting purposes, we consider that a Person object is "greater than" another // if it's age is greater than the other object's age bool operator>(const Person& rhs) const { return (age > rhs.age) ? true : false; } // print the object void print() { cout << name << " " << sex << " " << age << endl; } // destructor ~Person() { delete []name; } }; We modify a little bit the function for printing the contents of the list. void print(list<Person> lst, char * name) { list<Person>::iterator it; cout << name << ":" << endl; for(it = lst.begin(); it != lst.end(); ++it) it->print(); cout << endl; } Next, we create a list to hold list<Person> lst; // create some Person objects Person p1("Bill Gates", 'M', 50); Person p2("Scott Meyers", 'M', 43); Person p3("Charles Petzold", 'M', 48); Person p4("Christina Dermayr", 'F', 30); Person p5("Andrei Neagu", 'M', 22); Person p6("Yin Su", 'F', 56); Person p7("Georgeta Bills", 'F', 37); // add the objects to the list lst.push_back(p1); lst.push_back(p2); lst.push_back(p3); lst.push_back(p4); lst.push_back(p5); lst.push_front(p6); lst.push_front(p7); print(lst, "lst"); // sort the list in ascending order lst.sort( less<Person>() ); print(lst, "lst in ascending order"); // sort the list in descending order lst.sort( greater<Person>() ); print(lst, "lst in descending order"); // delete the first element from the list lst.pop_front(); print(lst, "lst"); // clear the list lst.clear(); if(lst.empty()) cout << "lst is empty" << endl; else cout << "lst is not empty" << endl; // Output // lst: // Georgeta Bills F 37 //Yin Su F 56 // Bill Gates M 50 // Scott Meyers M 43 // Charles Petzold M 48 // Christina Dermayr F 30 // Andrei Neagu M 22 // // lst in ascending order: // Andrei Neagu M 22 // Christina Dermayr F 30 // Georgeta Bills F 37 // Scott Meyers M 43 // Charles Petzold M 48 // Bill Gates M 50 // Yin Su F 56 // // lst in descending order: // Yin Su F 56 // Bill Gates M 50 // Charles Petzold M 48 // Scott Meyers M 43 // Georgeta Bills F 37 // Christina Dermayr F 30 // Andrei Neagu M 22 // // lst: // Bill Gates M 50 // Charles Petzold M 48 // Scott Meyers M 43 // Georgeta Bills F 37 // Christina Dermayr F 30 // Andrei Neagu M 22 // // lst is empty ComplexityThe way it is implemented suggests that the list container is mostly used for storing large objects, with multiple insertions and deletions, especially inside the list, because they involve only renewing some links and not moving objects in memory. Even the Going through the elements of the list is much slower than in the vector case, because it assumes pointer movement, not address calculation. The list container also occupies additional memory for managing the previous and next links.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||