How to use find in linkedlist - c++

Here is my code using STL library, where I try inserting a node at the end, in the middle and in front. For inserting in the middle, I want to provide insertion after a specific node, and not by incrementing the iterator by 2, as I might not know what to increment it by if it is a long list,
Kindly help why is find function not working:
#include <iostream>
#include <list>
#include <string>
using namespace std;
void printlist(list<int> l)
{
list<int>::iterator it = l.begin();
for (it; it != l.end(); ++it)
{
cout << "printlist function call list items: " << *it << endl;
}
}
int main()
{
list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
list<int>::iterator it = l.begin();
cout << 1 << endl;
printlist(l);
l.push_front(0);
cout << 2 << endl;
printlist(l);
it = l.find(l.begin(), l.end(), 2);
l.insert(it, 25);
cout << 3 << endl;
printlist(l);
return 0;
}
Thanks...

std::list<> doesn't have a find() method. You can use the standard algorithm std::find() declared in <algorithm>:
it = std::find(l.begin(), l.end(), 2);

See the answer by #0x499602D2.
But to elaborate on an important point raised in a comment by #NeilKirk, you wrote:
void printlist(list<int> l)
{
list<int>::iterator it = l.begin();
for (it; it != l.end(); ++it)
{
cout << "printlist function call list items: " << *it << endl;
}
}
Note that you are passing the list l by value, not by reference. Passing a class by value (that has not been designed to use implicit sharing) will make a copy. Thus, l will be a copy of the parameter passed. If your list contained a million elements, then passing it by value will make a million-element-copy. You can fix that with:
void printlist(list<int> & l) { ... }
Or if you don't plan on making any changes, it's always nice to announce that with:
void printlist(list<int> const & l) { ... }
Also, C++11 has a range-based for which does the iterator begin/end stuff under the hood for you, and automatic variable typing:
void printlist(list<int> const & l)
{
for (auto i : l)
{
cout << "printlist function call list items: " << i << endl;
}
}
Lots of ways to get fancy in that spirit. But the more critical thing is not go making copies of your data structures, passing them by value when you don't need to!

Related

Using iterators but with same amount of loc?

One can loop over a list by both:
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> alist{1, 2, 3};
for (const auto& i : alist)
cout << i << endl;
list<int>::iterator i;
for (i = alist.begin(); i != alist.end(); i++)
cout << *i << endl;
return 0;
}
Mostly I don't use iterators because of the extra line of code I have to write, list<int>::iterator i;.
Is there anyway of not writing it? And still use iterator? Any new trick on newer C++ versions? Perhaps implementing my own list instead of using the one from stl?
Mostly I don't use iterators because of the extra line of code I have to write, list<int>::iterator i;.
You don't need to put it in an extra line. As with every for loop, you can define the iterator type inside of the parentheses, unless you'll need the value outside of the loops body.
So you can also write
for (list<int>::iterator i = alist.begin(); i != alist.end(); i++)
cout << *i << endl;
or
for (auto i = alist.begin(); i != alist.end(); i++)
cout << *i << endl;

Duplicating std::list with std::copy and removal with std::list::erase

In the bellow example code after assigning example list with numbers I'm trying to duplicate container with std::copy but problem is at runtime it says "cannot dereference end list iterator".
my question is how do I duplicate the list so that duplicated range is inserted to the end of the list?
to the end because I later need to be able to remove duplicated range, that is why I save the beginning of the new range to iterator.
#include <iostream>
#include <list>
#include <algorithm>
void print(std::list<int>& ref)
{
for (auto& num : ref)
{
std::cout << num << std::endl;
}
}
int main()
{
std::list<int> mylist{ 1, 2, 3, 4 };
std::list<int>::iterator iter = mylist.end();
std::cout << "INITIAL LIST NUMBERS" << std::endl;
print(mylist);
// duplicate list, will cause runtime error
iter = std::copy(mylist.begin(), mylist.end(), --mylist.end());
std::cout << "COPIED LIST IS NOW CONTAINS DUPLICATE NUMBERS" << std::endl;
print(mylist);
// remove previsous duplication
mylist.erase(iter, mylist.end());
std::cout << "AFTER REMOVAL OF COPIED LIST SHOULD BE SAME AS INITIAL LIST" << std::endl;
print(mylist);
std::cin.get();
return 0;
}
You can use std::copy_n. This circumvents the issue with std::copy, which would execute an infinite loop of insertions when fed with a std::back_inserter(mylist) and an always valid mylist.end() iterator.
const std::size_t n = mylist.size();
std::copy_n(mylist.cbegin(), n, std::back_inserter(mylist));
De-duplication then works with
mylist.erase(std::next(mylist.begin(), n), mylist.end());
if (!mylist.empty()) --iter;
std::copy_n(mylist.begin(), mylist.size(), std::back_inserter(mylist));
if (!mylist.empty()) ++iter;
Unfortunately we can't use end iterator in copy(), since it might lead to an infinite loop, as new elements are added between the end and the current iterator all the time.

Delete last 3 elements in list c++

I have this c++ code which works fine in deleting the last 3 elements of a list, but i was wondering if it's the correct way to do such a thing since i am worried of deleting elements with iterators issues.
The code basically takes a list of 6 elements "Groups", divides it into 2 smaller lists "Group1" and "Group2", and then compares a different List "GroupToCompare" to "Group2" and if they're equal it removes the last 3 elements of "Groups".
#include "pch.h"
#include <iostream>
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
std::list <string> Groups = {};
Groups = { "Spike", "Jet", "Faye", "Edward", "Vincent", "Elektra" };
std::list<string> Group1 = {};
std::list<string> Group2 = {};
std::list<string> GroupToCompare = {};
GroupToCompare = { "Edward", "Vincent", "Elektra" };
size_t half1 = Groups.size() / 2;
std::list<std::string>::iterator ig = Groups.begin();
advance(ig, half1);
Group1.insert(Group1.end(), Groups.begin(), ig);
Group2.insert(Group2.end(), ig, Groups.end());
std::list<std::string>::iterator removeIt = Groups.begin();
advance(removeIt, half1);
cout << "List Elements 1: " << endl;
std::list<string>::iterator itrofList = Group1.begin();
string firstvar;
for (itrofList = Group1.begin(); itrofList != Group1.end(); ++itrofList) {
firstvar = *itrofList;
cout << "Item: " << firstvar << endl;
}
cout << "List Elements 2: " << endl;
std::list<string>::iterator itrofList1 = Group2.begin();
string firstvar1;
for (itrofList1 = Group2.begin(); itrofList1 != Group2.end(); ++itrofList1) {
firstvar1 = *itrofList1;
cout << "Item: " << firstvar1 << endl;
}
if (Group2 == GroupToCompare) {
removeIt = Groups.erase(removeIt);
removeIt = Groups.erase(removeIt);
removeIt = Groups.erase(removeIt);
}
cout << "List Elements of Groups after removing the last 3 elements: " << endl;
std::list<string>::iterator itrofList2 = Groups.begin();
string firstvar2;
for (itrofList2 = Groups.begin(); itrofList2 != Groups.end(); ++itrofList2) {
firstvar2 = *itrofList2;
cout << "Item: " << firstvar2 << endl;
}
}
is there a more correct way in case i wanna delete more elements in the end of the list and avoid deleting issues with iterators?
Thanks in advance!
Even though calling std::list::erase() multiple times with the returned position works, calling the correct overload would help with readability and possibly performance:
iterator erase( iterator first, iterator last );
Example:
std::list<int> l{1, 2, 3, 4, 5};
l.erase(std::prev(l.end(), 3), l.end());
// l is {1, 2}
Beware: as-is, this code exhibit undefined behavior if l is not at least 3 in size.
Is there a more correct way to do it?
Not sure what that means, but im assuming you would be interested in learning std::list has a public member function pop_back which deletes the last element in a list. Using this seems ideal for your case.
Note: As stated in this refference
If the container is not empty, the function never throws exceptions
(no-throw guarantee). Otherwise, it causes undefined behavior.
So make sure the list is never empty when you call this function.
I would just...
std::list <string> Groups;
...
for (int i = 0; i < 3; ++i)
Groups.pop_back();

unordered_multimap - iterating the result of find() yields elements with different value

The multimap in C++ seems to work really odd, i would like to know why
#include <iostream>
#include <unordered_map>
using namespace std;
typedef unordered_multimap<char,int> MyMap;
int main(int argc, char **argv)
{
MyMap map;
map.insert(MyMap::value_type('a', 1));
map.insert(MyMap::value_type('b', 2));
map.insert(MyMap::value_type('c', 3));
map.insert(MyMap::value_type('d', 4));
map.insert(MyMap::value_type('a', 7));
map.insert(MyMap::value_type('b', 18));
for(auto it = map.begin(); it != map.end(); it++) {
cout << it->first << '\t';
cout << it->second << endl;
}
cout << "all values to a" << endl;
for(auto it = map.find('a'); it != map.end(); it++) {
cout << it->first << '\t' << it->second << endl;
}
}
this is the output:
c 3
d 4
a 1
a 7
b 2
b 18
all values to a
a 1
a 7
b 2
b 18
why does the output still contain anything with b as the key when I am explicitly asking for 'a'? Is this a compiler or stl bug?
find, as implemented, returns an iterator for the first element which matches the key in the multimap (as with any other map). You're likely looking for equal_range:
// Finds a range containing all elements whose key is k.
// pair<iterator, iterator> equal_range(const key_type& k)
auto its = map.equal_range('a');
for (auto it = its.first; it != its.second; ++it) {
cout << it->first << '\t' << it->second << endl;
}
That's not a bug, it is by design. find returns an iterator to one of the matching elements, that's all. You'll iterate to the end of the map with your construct.
You need to use multimap::equal_range to do what you are after.
There is an Example in www.cplusplus.com , about How to use equal_range method to get all elements having the same key.
// unordered_multimap::equal_range
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm>
typedef std::unordered_multimap<std::string,std::string> stringmap;
int main ()
{
stringmap myumm = {
{"orange","FL"},
{"strawberry","LA"},
{"strawberry","OK"},
{"pumpkin","NH"}
};
std::cout << "Entries with strawberry:";
auto range = myumm.equal_range("strawberry");
for_each (
range.first,
range.second,
[](stringmap::value_type& x){std::cout << " " << x.second;}
);
return 0;
}
Please reference the link : http://www.cplusplus.com/reference/unordered_map/unordered_multimap/equal_range/
It would seem that you get an iterator into the full "list" of pairs, starting at the first pair with 'a' as it's key. So when you iterate to the end, naturally you will get everything beyond 'a' as well. If you sought for 'c', you would probably iterate through the entire "list" doing what you do there. Perhaps you should iterate to "it != map.end() && it->first == 'a'" if you want all the a's.

Reversing the contents of an std::list

class Print
{
public:
void PrintAll() {}
private:
std::list<int> mylist;
};
I see this example question from a C++ language book.
And I want to print the internal mylist elements.
How can it be done if mylist needs to be reversed, using C++ STL library and using to output the result.
Thanks you very much!
std::list<>::reverse()?
That said, if you only need to print the list in reverse, you can simply print it using list's reverse iterators (obtained by std::list<>::rbegin() and std::list<>::rend()) rather than by using list's normal iterators. E.g.:
// given std::list<int> l;
for (std::list<int>::const_reverse_iterator iter(l.rbegin()), iter_end(l.rend());
iter != iter_end;
++iter)
{
std::cout << *iter << '\n';
}
You can use the reverse() method on your list.
mylist.reverse();
will reverse the contents of your list and then you can print the same, using iterators.
list<int>::iterator it;
cout << "mylist contains:";
for (it=mylist.begin(); it!=mylist.end(); ++it)
cout << " " << *it;
You can wrap all the functionality up in your own member function.
You can also use reverse iterators provided by the container e.g. l.rbegin() and l.rend() and that will iterate through the list backwards.
Code example for list::reverse
// list_reverse.cpp
// compile with: /EHsc
#include <list>
#include <iostream>
int main( )
{
using namespace std;
list <int> c1;
list <int>::iterator c1_Iter;
c1.push_back( 10 );
c1.push_back( 20 );
c1.push_back( 30 );
cout << "c1 =";
for ( c1_Iter = c1.begin( ); c1_Iter != c1.end( ); c1_Iter++ )
cout << " " << *c1_Iter;
cout << endl;
c1.reverse( );
cout << "Reversed c1 =";
for ( c1_Iter = c1.begin( ); c1_Iter != c1.end( ); c1_Iter++ )
cout << " " << *c1_Iter;
cout << endl;
}
And the output will be
c1 = 10 20 30
Reversed c1 = 30 20 10
iterator simply point to the current element in the list. So, if we write a for loop going from end to beginning, we can print the list in reverse. in the code given below:
#include <iostream>
#include <list>
using namespace std;
int main()
{
std::list<int> ii;
ii.push_back(1);
ii.push_back(2);
ii.push_back(3);
ii.push_back(4);
ii.push_back(5);
for (std::list<int>::iterator it = (ii.begin()); it != (ii.end()) ; ++it)
{
cout << (*it) << " ";
}
cout << endl;
for (std::list<int>::iterator it = (--ii.end()); it != (--ii.begin()) ; it--)
{
cout << (*it) << " ";
}
return 0;
}
The first for loop prints out the list from front to back while the second one prints from back to front.