I am working through a C++ book, and I am a little stuck on one of the challenge questions. I'm working on learning about pointers, and in this particular problem I need to sort an array of structures (using pointers) with a string of a student's name and a double of their score. After the sort, the structure's data members still need to match up, obviously (i.e. the right name's still need to be with their scores).
This is where my problem lies. So far, I have the sort properly arranging the scores in ascending order, but the names get all jumbled up. I haven't been able to figure out why, partly because I am still working to fully understand pointers and how to use them. I can do a bubble sort correctly, keeping the names with their scores, but not the selection sort. Any help would be greatly appreciated.
Here is the function I have for the selection sort:
void selection_sort(Student *ptr, int size) // selection sort - having some problems
{
int start,
min_index,
min_value;
for (start = 0; start < (size - 1); start++) {
min_index = start;
min_value = (ptr+start)->score;
for (int index = start+1; index < size; index++) {
if ( (ptr+index)->score < min_value) {
min_value = (ptr+index)->score;
min_index = index;
}
}
// the following line is where, i think, the problem is, but i haven't
// been able to figure out the solution, despite trying numerous approaches
*(ptr+min_index) = *(ptr+start);
(ptr+start)->score = min_value;
}
}
So that is what I have. I'm not great with sorting algorithms yet either, and this is all pretty new to me, so I hope it's not horribly messed up. If anyone knowledgeable in these areas could point me in the right direction, that would be awesome.
First of all I would like to give you one tip: instead of using the syntax *(ptr+min_index) you can use ptr[min_index] and it will have the same effect. I believe this version is more natural.
Second - your problem. You should swap ptr[min_index] and ptr[start] rather then just copying the values of one of them to the other.
That is instead of:
*(ptr+min_index) = *(ptr+start);
(ptr+start)->score = min_value;
Write this:
Student temp = ptr[start];
ptr[min_index] = ptr[start];
ptr[start] = temp;
Or if you are using c++ simply use the swap function:
std::swap(ptr[min_index], ptr[start]);
Why should you swap instead of what you are currently doing? Well, you should preserve all the fields in ptr[min_index] in order to be able to assign them to ptr[start].
Hope this helps.
I think you should use memcpy function in the standard library...
And one more thing:
*(ptr+min_index) = *(ptr+start);
This line seems to overwrite the data, but NOT swap them as they should be.
First lesson in C++ : In C++ we have operator overloading, so the line like this:
*(ptr+min_index) = *(ptr+start);
can have meaning if your Student class has any pointer in his member attributes.
and you must use a swap and not just assign.
Related
normally I would never ask my school exercise on stackoverflow but I think what they want from us is impossible without using temporary array or modifying the array.
What they want from us is:
QuickSort Function which should be recursive
It should sort(that is what sorting algorithms do lol) but it should leave the array intact,
may not use temporary array
and it has to return the sorted list in a linkedlist format.
Function should be like this:
LinkedList *quickSort(int *array, int startPosition, int endPosition)
Returning as linkedlist is easy, sorting is easy but without modyfying in everystep of the algorithm? I dont think so.
I am not saying please send me the solution. I only want you to answer this, is not it impossible?
Edit: By "Leaving the array intact" teacher meant "dont change the size of the array, but you can modify the order of the array", so problem solved
I will check one of the answers just to make tag question as solved.
To answer your question directly: A quick sort can be done in place (without any temporary storage areas.)
Since I don't want to complete your homework for you directly :) Here is a resource to get you going.
See the second algorithm at http://rosettacode.org/wiki/Sorting_algorithms/Quicksort
Based on my understanding of the question, quickSort can be defined as
std::list<int> quickSort(int * array, int startPosition, int endPosition) {
std::list<int> result;
for (int i = startPosition; i <= endPosition; ++i)
result.push_back(array[i]);
QuiskSort(result.begin(), result.end());
return result;
}
where QuickSort has the definition
template <class Iterator>
inline void QuickSort(Iterator begin, Iterator end) {
if (end <= begin) return;
Iterator pivot = begin, middle = begin + 1;
for (Iterator i = begin + 1; i < end; ++i) {
if (*i < *pivot) {
std::iter_swap(i, middle);
++middle;
}
}
std::iter_swap(begin, middle - 1);
QuickSort(begin, middle - 1);
QuickSort(middle, end);
}
Guys I thought I was missing something and there is a way which I dont know, after your comments I called the assistant and he said of course you need to modify the array, you would not be able to do without modifying it. But in the exercise declarations it clearly says
b) sorts the numbers in the sub-array of the array between the given positions using quick sort algorithm
and leaving the array intact (may NOT use a temporary array)
Again thanks for all the answers and comments. Sorry for the missunderstanding guys.
I have a struct and two vectors in my .h file:
struct FTerm {
int m_delay;
double m_weight;
};
std::vector<FTerm> m_xterms;
std::vector<FTerm> m_yterms;
I've already read in a file to populate values to m_xterms and m_yterms and I'm trying to iterate through those values:
vector<FTerm>::iterator terms;
for (terms = m_xterms.begin(); terms < m_xterms.end(); terms++)
{
int delaylength = m_xterms->m_delay * 2; // Assume stereo
double weight = m_xterms->m_weight;
}
Although I'm pretty sure I have the logic wrong, I currently get the error Error expression must have a pointer type. Been stuck at this for a while, thanks.
Change
int delaylength = m_xterms->m_delay * 2;
double weight = m_xterms->m_weight;
to
int delaylength = terms->m_delay * 2;
// ^^^^^
double weight = terms->m_weight;
// ^^^^^
as you want to access values through
vector<FTerm>::iterator terms;
within the loop
for (terms = m_xterms.begin(); terms < m_xterms.end(); terms++)
// ^^^^^
"Although I'm pretty sure I have the logic wrong, ..."
That can't be answered, unless you give more context about the requirements for the logic.
Along with the problem πάντα ῥεῖ pointed out, your code currently has a problem that it simply doesn't accomplish anything except wasting some time.
Consider:
for (terms = m_xterms.begin(); terms < m_xterms.end(); terms++)
{
int delaylength = m_xterms->m_delay * 2; // Assume stereo
double weight = m_xterms->m_weight;
}
Both delaylength and weight are created upon entry to the block, and destroyed on exit--so we create a pair of values, then destroy them, and repeat for as many items as there are in the vector--but never do anything with the values we compute. They're just computed, then destroyed.
Assuming you fix that, I'd also write the code enough differently that this problem simply isn't likely to happen to start with. For example, let's assume you really wanted to modify each item in your array, instead of just computing something from it and throwing away the result. You could do that with code like this:
std::transform(m_xterms.begin(), m_xterms.end(), // Source
m_xterms.begin(), // destination
[](FTerm const &t) { return {t.m_delay * 2, t.m_weight}; });// computation
Now the code actually accomplishes something, and it seems a lot less likely that we'd end up accidentally writing it incorrectly.
Bottom line: standard algorithms are your friends. Unlike human friends, they love to be used.
I know this might seem as a duplicate question, but it is not, I have a problem with a function and don''t know why it behaves like that.
I have a vector which holds elements of type MyMaterial** (std::vector). At a point in my program, I will know an element, "currentElement", and I will want to remove it.
I tried doing this:
myMaterials.erase(currentElement);
But here is the problem: Instead of only deleting "currentElement", it also deletes all elements added after it. Why does it do that and how can I solve it?
I must mention that I don''t know the position of "currentElement" in the vector, and i prefere not to search for it, I''m hoping there is another way.
If you are using std::vector, then:
Erase elements Removes from the vector either a single element
(position) or a range of elements ([first,last)).
Maybe you are looking for something like this:
How do I remove an item from a stl vector with a certain value?
To use vector::erase(iterator) to remove an element from a vector, you may either have to know its index OR iterate thru the list to hunt for it.
Luckily, There is the std::map, and this is how you would work it
std::map<std::string,myMaterial> myMaterials;
myMaterial mat;//assuming it doesnt take any args
myMaterials['myMaterialXYZ'] = mat; ///add it to the array
myMaterials.erase('myMaterialXYZ'); ///Erase by key..(or "name" in this case)
Now you can easily track string names instead of ever changing index positions...and memory locations, which by the way may be another ace up your sleeve.
I couldn''t really use the examples given by you above, because I got all kinds of errors, due to the type of elements the vector holds. But I made a function which managed to delete the specific element:
int i=0;
int found=0;
MyMaterial **material = gMaterials.begin();
while(material != gMaterials.end() && found == 0)
{
if(currentElement == material)
{
gMaterials.erase(gMaterials.begin() + i, gMaterials.begin() + i+1);
found = 1;
}
i++;
cloth++;
}
I don''t know how good/correct it is, but it does the job.
Thank you very much for the suggestions and the help.
Having a bit of a headache trying to sum the elements in a 3d Vector.
Its for a k-means algorithm that I'm currently programming; an algorithm that I understand and can do on paper, but syntactically has me a bit tongue tied at the moment. I might mention that this project is the first time that I've really dealt with complex containers in C++. Currently I want calculate the new centroid for my points in a cluster, this is done by averaging the positions of every co-ordinate in the cluster. My 3d vector is set out as a vector of clusters each containing a vector which contain vectors of my co-ordinates in that cluster (I hope that sounds clear, hopefully my code will alleviate any confusion). I'm trying to use iterators at the moment, but am considering going back to ints and indices as I am more comfortable with them, though I feel that I should learn how this syntax works as it seems to be important and powerful.
I'll post just the function that I'm stuck on and the parts of the header that relate to it. If you would like to see any of the other code I'm happy to throw that in too on request, but I feel that this should be enough to show my problem.
.h file parts (public members of class):
vector< vector < vector <float> > > clusters;
vector<vector<float> > avg;
int avgDiv;
.cpp file part with comments to help elaborate my query:
vector<vector<vector<float> > >::iterator threeD;
vector<vector<float> >::iterator row;
vector<float>::iterator col;
for (threeD = clusters.begin(); threeD != clusters.end(); threeD++) {
for (row = threeD->begin(); row != threeD->end(); row++) {
for(col = row->begin(); col != row->end(); col++){
//its this code below that is causing my headache,
//I know that what is written isn't correct,
//it is there to serve as an example of what I've
//been trying to do to sort out my issue.
avg.at(row) ( = or push_back ) ((clusters.at(row).at(col)) + (clusters.at(row+1).at(col)));
}
avgDiv = distance(row->begin(),row->end());
//divide each value in avg vector by the amount of members in row, giving the new centroid for that cluster, loop forward to next cluster. this isn't a problem I should think.
}
}
My problem is that the compiler tells me that call to 'at' is not a member function. Now from what I can see from other questions it is because I'm not passing the right object as an arguement, though, I'm sure that I want to add together every element in the vector which the iterators are at together with the element next in the row.
I've tried and make this as clear as possible, please ask and I will add as much detail as I can to help you answer. I am new to this, and am very happy to take criticism; it will only make me a better programmer. Thank you for your time.
avg.at(index) is used with an integer index, it's just the 'c' array[index] notation with bounds checking - incidentally in real code you want to use [] or disable checking for speed.
But row is an iterator, effectively it's a pointer to the element in avg already so just dereference it to get the value.
*row = value of avg at position of iterator 'row'
A good tutorial on C++ iterators http://www.cprogramming.com/tutorial/stl/iterators.html
ps. With vectors and 'maths' type code, it's often simpler to just use array index notation
I have been taught at school to use database with integer IDs, and I want to know if it's also a good way to do so in C/C++. I'm making a game, using Ogre3D, so I'd like my game code to use as few cycles as possible.
This is not the exact code (I'm using vectors and it's about characters and abilities and such), but I'm curious to know if the line where I access the weight is going to cause a bottleneck or not, since I'd doing several array subscript.
struct item
{
float weight;
int mask;
item(): mask(0) {}
}
items[2000];
struct shipment
{
int item_ids[20];
}
shipments[10000];
struct order
{
int shipment_ids[20];
}
orders[3000];
int main()
{
// if I want to access an item's data of a certain order, I do:
for (int i = 0; i < 3000; ++ i)
{
if (items[shipments[orders[4].shipment_ids[5]]].weight > 23.0)
s |= (1<< 31);
}
}
I have heard that putting data into arrays is the best way to gain performance when looping over data repeatedly, I just want to know your opinion on this code...
A good optimizer should be able to compute the exact offset of the memory address each of those items. There is no dependency between loop iterations, so you should be able to get loop unrolled (SIMD processing). Looks great, IMHO. If you can avoid floats, that will also help you.