c++ list random access [duplicate] - c++

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to get a certain element in a list, given the position?
so in python you can get an element in a list in a random access fashion....
list = [1,12,3]
print(list[1])
and it prints 12....
can you do the same thing with c++ list?
I'm talking about this: http://www.cplusplus.com/reference/stl/list/list/

In C++, the nearest along to what you want would be a vector:
std::vector<int> v;
v.push_back(1);
v.push_back(12);
v.push_back(3);
std::cout << v[1] << std::endl; // prints 12
You can use the iterators provided to traverse the vector, too. But once you modify the vector (insert or erase), it becomes invalid.
The actual List class provided (which is a doubly-linked list), doesn't provide this sort of feature.

no. if you use std::list you have to iterate through the list to find a specific element, because list is a double-linked list, elements cannot be accessed with random access operator.
and that's because with lists, it's fast and efficient to insert or delete at any point in the list, thus what was the first element at the beginning could be the third element after modifying the list.

If I understand your question right, you're asking about arrays
int list[3(size)] = {1,12,3};
cout << list[1];

If you are talking about a C++ STL list, no, that is the primary problem with the list.

Related

Declaring a std::list with an array index C++

I was following a hash table implementation online (https://www.youtube.com/watch?v=2_3fR-k-LzI) when I observed the video author initialize a std::list with an array index. This was very confusing to me as I was always under the impression that std::list was always meant to operate like a linked list and was not capable of supporting random indexing. However, I thought it was maybe a weird way to declare the size of a list and ignored it and moved on. Specifically, he did the following:
static const int hashGroups = 10;
std::list<std::pair<int, std::string>> table[hashGroups];
Upon trying to implement a function to search to see if a key resided in the hash table, I realized that I could not access the std::list objects as I would expect to be able to. In HashTable.cpp (which includes the header file that defines the two variables above) I was only able to access the table member variable's elements as a pointer with -> instead of with . as I would expect to be able to. It looks like what is directly causing this is using the array index in the list definition. This seems to change the type of the table variable from a std::list to a pointer to a std::list. I do not understand why this is the case. This also appears to break my current implementation of attempting to iterate through the table variable because when I declare an iterator to iterate through table's elements, I am able to see that the table has the correct data in the VS debugger but the iterator seems to have completely invalid data and does not iterate through the loop even once despite seeing table correctly have 10 elements. My attempt at the search function is pasted below:
std::string HashTable::searchTable(int key) {
for (std::list<std::pair<int, std::string>>::const_iterator it = table->begin(); it != table->end(); it++)
{
if (key == it->first) {
return it->second;
}
std::cout << "One iteration performed." << std::endl;
}
return "No value found for that key.";
}
With all of this being said, I have several burning questions:
Why are we even able to declare a list with brackets when a std::list does not support random access?
Why does declaring a list like this change the type of the list from std::list to a pointer?
What would be the correct way to iterate through table in its current implementation with an iterator?
Thank you for any help or insight provided!
After reading the responses from #IgorTandetnik I realized that I was thinking about the list incorrectly. What I didn't fully understand was that we were declaring an array of lists and not attempting to initialize a list like an array. Once I realized this, I was able to access the elements correctly since I was not trying to iterate through an array with an iterator for a list. My revised searchTable function which to my knowledge now works correctly looks like this:
std::string HashTable::searchTable(int key) {
int hashedKey = hashFunction(key);
if (table[hashedKey].size() > 0)
{
for (std::list<std::pair<int, std::string>>::const_iterator it = table[hashedKey].begin(); it != table[hashedKey].end(); it++)
{
if (key == it->first) {
return it->second;
}
}
}
return "No value found for that key.";
}
And to answer my three previous questions...
1. Why are we even able to declare a list with brackets when a std::list does not support random access?
Response: We are declaring an array of std::list that contains a std::pair of int and std::string, not a list with the array index operator.
2. Why does declaring a list like this change the type of the list from std::list to a pointer?
Response: Because we are declaring table to be an array (which is equivalent to a const pointer to the first element) which contains instances of std::list. So we are never "changing" the type of the list variable.
3. What would be the correct way to iterate through table in its current implementation with an iterator?
Response: The current implementation only attempts to iterate over the first element of table. Create an iterator which uses the hashed key value as the array index of table and then tries to iterate through the std::list that holds instances of std::pair at that index.

C++ copy vector range before erase [duplicate]

This question already has answers here:
Move all elements which satisfy some condition from one container to another, i.e. I'm looking for some kind of "move_if"
(3 answers)
Closed 4 years ago.
I have
std::vector v_1;
std::vector v_2;
and I want to delete some elements in v_1 but store them in vector v_2 before doing so. Something along the lines
auto deleteIf = [](auto& x) {return x.checkDelete();}
auto it = std::remove_if(v_1.begin(), v_1.end(), deleteIf);
// how can I efficiently copy elements it to v_1 end to v2?
v1.erase(it, v_2.end());
How can I most efficiently copy the elements that will be erased to v_2?
You cannot rely on remove_if to keep your elements intact. From cppreference.com :
[...] Iterators pointing to an element between the new logical end and the physical end of the range are still dereferenceable, but the elements themselves have unspecified values [...]
You have to somehow move them.
You can partition your vector and than split it into two:
std::vector<type> v_1;
std::vector<type> v_2;
auto split_check = [](auto&) { x.checkDelete() };
// stable_partition will keep the order. if not required use std::partition
auto it = std::stable_partition(v_1.begin(), v_1.end(), split_check);
v_2.insert(v2.end(), it, v_1.end());
v_1.erase(it, v_1.end());
running example.
I just realized the duplicate answer from T.C. mentions the exact same thing... ¯\_(ツ)_/¯

Remove object from vector based on object property [duplicate]

This question already has answers here:
Using erase-remove_if idiom
(3 answers)
Closed 4 years ago.
I want to delete object from vector which has property set on specific value. In this example:
void RemoveUser(int val)
{
users.remove_if([val](User u)
{
return u.val == val;
});
}
I can remove specific user based on its val property. This works on std::list, but its not working on std::vector. How can I archieve same result on vector?
std::vector doesn't have a remove_if member. But there is a standard algorith, aptly named std::remove_if that can work in tandem with the vectors erase member function.
users.erase(remove_if(begin(users), end(users), [val](User const& u)
{
return u.val == val;
}), end(users));
First remove_if shifts the elements of the range, and returns an iterator past the end of all the "good ones". The vector is of the same size as when we started at this point. Now that iterator is fed to erase, which kills all of those items, from that "new end", to the "old end" of the vector.
The reason std::list implements its own member version of remove_if is to better utilize its node based structure. It doesn't have to do copies to shift elements around or delete them. For std::vector, the algorithm is the best we can do.

Is it possible to send part of vector as a vector to a function? [duplicate]

This question already has answers here:
How can I pass a part of a vector as a function argument?
(7 answers)
Closed 5 years ago.
I want to see if it is possible to pass part of vector to a function so that it appears as a normal vector to the function. More importantly, I want this to be done in O(1), constant time. I don't want to iterate the vector to make a new one. In fact, I also want the size of the new vector to change to 40 in the following example.
void func(vector <int> &v){
//calling index 10 to 50 of v
func(v[10..50])
}
Use iterators as the parameters to the range-based function, and pass in the required range. Your code in the function become
funcWithRange(v.cbegin()+10, v.cbegin()+50);
with function signature
void funcWithRange(std::vector<int>::const_iterator first, std::vector<int>::const_iterator last)
This could be generalized by making this a function template with the vector member type as its template parameter, or still further to any container supporting this type of range iteration. As noted in the comments, <algorithm> has many examples of this pattern.
std::distance(first, last); will return the desired altered size. I don't think you can get closer to meeting your requirements without making a physical copy.
If you have a vector of say 100 elements
std::vector<int> v(100);
and you want to call void f(std::vector<int> v) with the first 10 elements, simply call the function as
f({v.cbegin(), v.cbegin() + 10});
That will construct a new vector from the two iterators (by copying the elements) and pass the new vector to f.
There is a means to achieve something similar to this that is being proposed for inclusion in the C++ standard. It is called a span and it operates like a vector in some ways.
#include <gsl/span>
void func(gsl::span<int> sp)
{
for(auto& i: sp)
std::cout << i << '\n';
}
int main()
{
// ...
std::vector<int> v(100);
// put something in the vector (numbers 0 - 99)
std::iota(std::begin(v), std::end(v), 0);
// wrap the container in a span
auto sp = gsl::make_span(v);
// send parts of it to functions
func(sp.subspan(10, 50));
}
The span presents a window onto the original vector so it is a reference type. It doesn't contain its own data just pointers to the data in the vector. As such they are lightweight and designed to be passed by value.
An implementation of span can be found here: https://github.com/Microsoft/GSL
This is the recommended way of passing contiguous containers in the Best Practices guide by Bjarne Stroustrup and Herb Sutter.
The guidelines can be found here: CppCoreGuidelines.md

How to traverse vector from end to start? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Erasing elements from a vector
I want to delete some elements as I go and so dont want miss the iteration
I travers right now as follows
vector<double> distances;
for(size_t i=0; i<distances.size();i++)
{
}
How do I traverse from end to beginning so I can safely delete elements and yet access the element I want?
for (size_t i = distances.size() - 1; i >=0; --i)
However, you should use an std::remove_if instead
The best way to handle this is to cheat, actually.
In C++, we have the erase/remove idiom to have the most efficiency. You create a predicate and then you're on, and it's gotten quite simple with lambdas now.
// This removes all odd elements in vec
vec.erase(vec.remove_if([](int i) { return i % 2 == 1 }), vec.end();
The way remove_if works is that it shuffles elements around (either copying or moving) so as to gather all the elements you wished to keep at the head of the container, the tail being left in an unspecified (but valid) state, and then returns to you an iterator to the first element of the tail.
By then using the range-erase method of the container, you ask the container to remove all those elements in one go.
use the reverse_iterator :
vector<something>::reverse_iterator iter = v.rbegin();
iter++; //Iterates backwards