Best structure to be able to access an itemized list. - c++

I need to be able to populate a list one time and then return the index of the item quickly. For instance if I had 10 people in a list i need to be able to look up the person and return their number(1-10) in the list. I thought about several different ways but so far none have worked. I have a vector populated with my data now I just need to be able to get to the index. I am not set on a vector though if anyone has a better idea I am more than willing to listen.
Thanks.

Welcome to Stackoverflow.
For a list that small, it doesn't really matter. Generally though you are going to get better lookup times from ordered collections. I would suggest std::set. If that doesn't work in your scenario (e.g. you want to determine the order yourself) then std::map may be better.
Edit*
As you clarified in your comment, all you want is the index from a vector, then I would do something like this.
int main() {
vector<string> v;
vector<string>::iterator it;
v.push_back("a");
v.push_back("b");
v.push_back("c");
v.push_back("d");
it=find(v.begin(),v.end(),"c");
if(it!=v.end()){
int index = distance(v.begin(), it);
cout<<"found "<<index<<endl;
}
else{
cout<<"not found"<<endl;
}
return 0;
}
Sorry if my formatting is strange, I'm not really a cpp programmer

Related

using an iterator of a map<char,strring> to return a pointer map<char,strring>*

Im working on a university assignment hence the pedantic nature of my question.
The assignment is to implement a hash table, of size 26. The hash function is simply the last letter of the word {hash(apple) = e, hash(car) = r}. I have chosen to use a map<char, string> char for each letter of the alphabet (hash) and string to store the input words (key).
I want to search the map and return a pointer to the position where a new key needs to be added. The search aspect I have working its the pointer I am having trouble with. any assistance would be much appreciated.
map<char,string>* search(map<char,string>* hashTable, string key, char hashVal){
map<char,string>::iterator it;
for(it = hashTable->begin(); it != hashTable->end(); ++it){
if(I want to return this address of hashTable){
return &it->hashTable || something to that effect
}
}
)
The reason I want to do this is to fulfil the following use case in an add() or remove().
void remove||add(map<char,string>* hashTable, string key){
map<char, string>* temp = search(hashTable, key, hashVal);
temp->second == NULL||key;
}
NOTE:
I understand that the map library has functions like at() and find() however using them wont prove my understanding hash tables. This is pedantic nature of university data structures assignments 2020 I was referring to.
ps. let me know if you think there is a better library to base my hash table off. linked lists maybe?
Ok, so I think you are attacking this the wrong way. std::map is already a fully fledged, hash-table-like container (cringe, yes I know it is not really, don't hurt me!). Using it will most likely lead you astray from what you should learn from this exercise.
std::vector is your friend. If you have your ASCII table ready, you should be able to formulate a good hash function: hash(val) -> Index.
The example might strike you as pedantically defined, but it will make all the properties of hash tables strikingly obvious (like in your face kind of obvious).
Ah, and to answer your question: std::next has a good overload for getting a pointer/iterator to an element.
edit Addendum:
(hint) The killer feature of hash tables would probably get lost when using linked lists...

what is the most elegant way of returning a lot of std::map<,>::iterator?

I am working on a car fleet program that has a container that contains a lot of cars
std::map<CarKey, Car> _cars;
I need to write a function/class that operate on a subset of the car objects in the _cars
Naively I can just iterate through _cars with a filter function
for(auto& p : _cars){
//please note: I cannot get things done with one iteration, I have to iterate many times to get things done
if (isOfInterest(p.second)){
// do some thing
}
}
the downside of such a solution is that is I am interested only in 10% of the cars, I will have to waste a lot time iterating
I am trying to find an elegant way to return all the iterators that I am interested
std::vector<std::map<CarKey, Car> :: iterator > getAllIntereted(_cars)
then I can simply iterate through the vector
I am not sure if this is a good approach. Maybe there is some design pattern that can be helpful?
Can anyone give any insights?
Thanks
Without seeing more of the code, I think returning a vector containing pointers to just the cars you are interested in is a good solution. Pointers because otherwise you are creating duplicate objects.
However, if you can just do the work in the same loop that you identify the car, I think that would be best. Just depends on if that is practical.

Faster way to search list?

I'm trying to make my program run more efficiently, and I believe fixing this linear search would do a great deal of help in terms of speed, but am curious as to how I'd go about changing this to something like binary search, as I believe the list isn't necessarily ordered. Is there some way of ordering the list based on it's first argument key?
What I'm working with currently:
int* key_sequences::data(int key){
for(it=myList.begin(); it!=myList.end(); ++it){
if(it->first==key){
return &(it->second[0]);
}
}
return nullptr;
};
Searching in a simply linked list is O(n), where n is the size of the list.
However, in some implementations, people use three list pointers, one at the start, one at the middle and one at the end, so that when searching comes up - they can speedup the process, so you can search online for this.
BUT, if I were you and was so interesting in speeding up the search, I would try another data structure (hash? like std::unordered_map) or sort the list.

C++ class instances

I am working on intro c++ homework, but I am stuck.
Account *GetAccount(int an);
int main()
{
Account *a1,*a2,*b1;
a1=GetAccount(123);
a2=GetAccount(456);
b1=GetAccount(123);
if(a1==b1)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
GetAccount method is supposed to check whether the instance already exists with the same account number, if it does, returns that instance.
Only method I can think of is to create array of Account and search for account, then if it doesn't exist, insert new Account in the array. If it exists, returns the pointer to the array.
This method doesn't really seem efficient to me, and is there any other way?
Yes. Instead of array, use a map. It fill be more efficient in terms of space, and almost as fast.
You can use STL and keep your accounts in a std::map, one of these variants:
map<int, Account> or
map<int, Account*>
In the first case, you keep the Accounts in the map, in the second you keep the pointers to Accounts, and are responsible for creation/deletion. Which variant is more appropriate? it depends on the way you create/initialize the account.
Short tutorial on STL map usage
I will exlain the case when you keep the pointers in the map.
This is how you would declare the map:
map<int, Account*> accounts;
This is how you can add a new account to the map:
int account_id = 123; // or anything else
Account* account = new Account(...paramters for the constructor...)
// any additional code to initialize the account goes here
accounts[account_id] = account; // this adds account to the map
This is how you check if the account with account_id is in the map:
if (accounts.find(account_id) != accounts.end()) {
// It is in the map
} else {
// it is not in the map
}
This is how you get pointer to an account from the map:
Account* ifoundit = accounts[account_id];
Finally, somewhere at the end of your program, you need to clean the map and delete all the account objects. The program will work fine even without the cleanup, but it's important to clean up after yourself. I leave this as an exercise for you :)
Find how to iterate all the elements of the map, and apply delete appropriately.
This method doesn't really seem efficient to me, and is there any other way?
Yes, as others have mentioned, there are more efficient ways using data structures other than arrays. If you've recently been studying arrays and loops in your class, though, the method you describe is probably what your instructor is expecting. I wouldn't try to get too far ahead of your instructor, since the arrays and loop method is probably the sort of thing you'll need to be very familiar with when you take your exams. It's also a good idea to have a strong foundation in the basics before you move forward. (Don't let that deter you from asking more advanced questions on here, though.)
Consider hash tables.
You could use a std::map instead of a simple array.
The method you proposed, an array of ids which you walk through and test, is a very easy one. I would use a std::vector, however, not an array, as you then don't have to worry about size. Otherwise, you just declare a big array, and test that it isn't full when adding.
In terms of efficiency, doing a linear search over a small array (in the hundreds) is quite fast, and may well be faster than other solutions, like maps and sets. However, it does not scale well.
Try to write your code well, but don't worry about optimising it until you know you have a probelem. I would much rather my programmers wrote clean, easy to maintain code than go for optimal speed. We can always speed things up later, if we need to.
Create a std::map of account # => Account object.

Why did I get a Segmentation Fault with a map insert

I want to insert a pair< string, vector<float> > into a map, first it works, but after several loops, it cannot insert any more and throw me a segmentation fault. Can anybody give a possible reason?
Btw: I first read a file and generate the map (about 200,000 elements) and I read another file and update the old map. the error occurs while the updating step.
Can anybody help me with the info I gave above? Thanks a lot
The code is pretty long.....I just erase the previous key and then insert a new one, it seems not complicated.....but drives me crazy....could you guess what happened here?
Thanks A lot for all your answers! And I found it is really a good place for solving problems. Thanks again, I'll try to simplify my codes and add it here today or tomorrow.
Update: I used the code from MSN and it works, thanks a lot that you solved my problem without seeing my code......also many thanks to other kind-hearted people here! However, i can only choose one as the answer.
Are you inserting using the iterator you called erase() on? Or using that iterator in any way? After erase(p) is called, p is invalidated.
Without more information, it's not easy to say, but what are you inserting? Could it simply be that you run out of memory? Although, I do think normal C++ would throw an exception in that case, are you using any custom allocators, malloc, or arrays on the stack which are overrun perhaps?
Perhaps a snippet of code describing what you do could be helpful in determining the cause of your problem.
could you guess what happened here?
You're abusing memory in some way.
There are a lot of ways to do that, in C++!
Rather than guess, and without reading your code, I suggest run the kind of platform-specific debugger which will detect this problem, for example valgrind.
Your alternative is to make the problem smaller: reproduce the problem in only a few lines of code, which you can then post for people to look at.
The type in question is pair<string, vector<float> >. You will be copying that pair on every insert. If either the string or the vector are big then you could be running out of memory.
Edit: to fix running out of memory, you can change how you insert key-value pairs to:
pair<map::iterator, bool> insert_result= map.insert(make_pair(name, vector<float>());
if (insert.second) { insert_result.first->second.swap(vector_read_in); }
That will ensure that you do not copy memory, only move it.
It easily happens if you either modify the keys of the elements already in the data structure or if you have a bad compare function, which misleads the search algorithm.
If you can detect which concrete insert operation causes the seg.fault, then try debugging/logging with what values the compare function is called.
Alternatively, you should print the contents of the map before the erroneous insert, the keys will probably not be in order.
please post some code, you cant expect us to debug your problem on guess work alone.
...but ill give it a stab anyway :) Also what compiler, and system are you doing this on?
If you are reading the data in a loop you may run out of stack space which would cause a seg fault.
Ill edit my answer if you post some code.
Remember that if you are looping through the map, finding stuff to delete, save off the key for later deletion. If you delete while iterating, you run the risk of invalidating the iteration loop.
std::map<string, vector<float> >::iterator iter = my_map.begin();
while (iter != my_map.end()) {
if (somethingBadAboutItem(iter)) {
my_map.erase(iter); // this can mess up my_map iteration above
// and cause bad data access later
}
++iter;
}
instead, try
std::map<string, vector<float> >::iterator iter = my_map.begin();
std::vector<string> keys_to_delete;
while (iter != my_map.end()) {
if (somethingBadAboutItem(iter)) {
keys_to_delete.push_back(iter->first);
}
++iter;
}
for (std::size_t i = 0; i < keys_to_delete.size(); ++i) {
iter = my_map.find(keys_to_delete[i]);
my_map.erase(iter);
}
I am interested if other people have found something more elegant than this, but this is my preferred technique.
Bill: Have you tried something like this:
for(std::map<string, vector<float> >::iterator iter = my_map.begin(); iter != my_map.end();) {
if(somethingBadAboutItem(iter)) {
my_map.erase(iter++);
} else {
++iter;
}
}
The key is to postincrement the iterator when deleting, so it's still valid when incremented but you erase (and hence invalidate) a copy pointing to the previous item.
I'm not sure this technique necessarily works for all STL containers; I can't remember all the invalidation rules offhand (seems unlikely to work for vector for example, but you're normally better off using remove_if for those for non-trivial sizes) but it should be fine for map.