Trouble bubble sorting alphabetically in struct array - c++

When trying to bubble sort an inventory stored in a struct array, I am getting two different errors when I compile the code below:
void SORT_INVENTORY(Books* list, int max, int position)
{
bool swap;
string temp;
do
{
swap = false;
for (int count = 0 ; count < (position - 1) ; count++)
{
if ( tolower(list[count].Title) > tolower(list[count + 1].Title))
{
temp = list[count];
list[count] = list[count + 1];
list[count + 1] = temp;
swap = true;
}
}
} while (swap);
I wish to use tolower to compare the Title element of two struct arrays. However, compiler won't let me run the program because it says that no matching function to call for tolower.
When I switch the if statement to this:
if ( ::tolower(list[count].Title) > ::tolower(list[count + 1].Title))
The "no matching function" message goes away but is replaced by a new one: no viable conversion from 'string' (aka 'basic_string, allocator >') to 'int'.
Lastly I get a consistent error message regarding statments in the body of the if statement, stating no viable overloaded '=' in temp = list[count] and list[count + 1] = temp.
One last detail: list is an array declared as a struct data type. What am I doing wrong?

tolower works on a single character, not a string. Check out How to convert std::string to lower case?
You are trying to assign a Book to a string (and vice versa). Change the type of temp.

I take it you're new to C++, first, as Carl Norum mentioned, tolower() works on char's, not strings.
Second, Carl is right about temp being a string (it should be a book), but, there is another large problem, you are copying the "Book" class if you plan on doing it this way. Depending on the size of the class, this could be computationally difficult. If you must "sort" an array multiple times, I would suggest having an array of pointers to speed up the swap function.
Lastly, bubble sort is terrible, don't use it. If you need a set that is always sorted, use a binary search tree or hash. If you must sort an array, the "default" option is Quicksort, which has a plethora of sources online, so I'm not going to post a how to of it.

Related

Getting multiple Instances of key using bsearch()

is there a way to implement bsearch() to find multiple instances of key.
for example: (obj*)bsearch(key=r,arr,elements,sizeof(obj),(int(*)(const void*, const void*)bcompare);
The code I currently wrote only finds the first instance and cannot proceed past the first found due to how it works.
getline(target,81);
if(strcmp(target,"exit") == 0 || strcmp(target, "") == 0) break;
p = (Info*)bsearch(target,list,num,sizeof(Info),(int(*)(const void*, const void*))bcompare);
int foundIndex = (int)(p-list);
if(!p){
err_dis1_win();
clrscr();
}
else{
display_record(p);
cout << "\n\n found at index " << foundIndex << "\n";
getch();
clrscr();
}
Variables:
p - is a pointer to object of class Info
target - arr of char
list - arr of obj
foundIndex - index of element found
Info - derived class from base class
**compare function
int bcompare(char *a,Info *b){
return(strncmpi(a, b -> get_name(), strlen(a)));
}
I cannot use other methods such as std::find or writing my own binary search function and have to use bsearch()
I have tried loops inside the else block, and the compare function using the varible foundIndex, as well as using a while loop on the return value looping through the obj list arr. Is there a way to start at a specific index. I appreciate any help. I am not looking for code but a general push in the right direction. Thank you.
Caveat - The current code compiles and runs as expected however, the functionality that I want, cannot be figured out by myself. Google and search on Stackoverflow has not produced an related issue.
Since bsearch() returns only one item, I interpret "find multiple instances of key" as "find the first instance of a key". The caller can then step forward through the array from that item to process each item matching the key, until it reaches the end or reaches an item that does not match.
If you must use the standard library's bsearch() function and persuade it to find the first item matching a given key, then all you really have to work with is the comparison function you present. bsearch() will return an item that matches the key according to that function, but if more than one item matches then there is no guarantee which one will be returned. You must ensure, then, that only the item you want matches.
You can approach that with an appropriate implementation of the comparison function, but there is a significant problem. The function will in some cases need to evaluate the item preceding the one specified to it, but it must not attempt to examine an item preceding the array's first. bsearch() does not itself convey any information about the array bounds to the comparison function.
There are at least two possible solutions, neither of them stellar.
Store the array lower bound in some well-known location that the function can access. For example, if the comparison function is a static member function, then maybe you would use a static variable of its class. But that is not thread-safe. You could do something similar with thread-local variables, but even then it's ugly. Either way, you have to be sure to set that variable appropriately before you call bsearch(), and that's ugly, too.
OR
Ensure that you never bsearch() for the first item. One way you could do that would be by checking preliminarily whether the first item matches (but not via the comparison function), and using it directly instead of calling bsearch() in the event that it does match. I'd wrap that in a method, myself, and if you must not do so then requiring that such a calling discipline be employed manually is also ugly.
Having chosen one of the above, you can implement a comparison function that looks at the previous item's key in addition to the specified item's. Something along these lines (which assumes the second alternative):
struct my_item {
int key;
void *data;
};
// bsearch() passes the target item as the first argument, and the one to compare
// to it as the second
int compare_items(const void *to_find, const void *to_check) {
const struct my_item *to_find_item = (const struct my_item *) to_find;
const struct my_item *to_check_item = (const struct my_item *) to_check;
// Check first how the key members are ordered
if (to_find_item->key < to_check_item->key) {
return -1;
} else if (to_find_item->key > to_check_item->key) {
return 1;
} else {
// The key members match, so check whether we're looking at the first
// such item.
const struct my_item *previous_item = to_check_item - 1;
// If the previous item's key does match, then we know the item we're
// looking for is an earlier one than we are presently checking.
return (previous_item->key == to_check_item->key) ? -1 : 0;
}
}

C++ code performance strings compare

I have an array of struct (arrBoards) which has some integer values, vector and a string type.
I want to compare if certain string in the struct is equal with entered parameter (string p1).
What idea is faster - to check equation of input string with every string element inside an array, or firstly check if string.length() in current string element of the array greater than 0, then compare the strings.
if (p1.length())
{
transform(p1.begin(), p1.end(), p1.begin(), ::tolower); //to lowercase
for (int i=0; i<arrSize; i++) //check if string element already exists
if ( rdPtr->arrBoards[i].sName == p1 )
{
*/ some code */
break;
}
}
if (p1.length())
{
transform(p1.begin(), p1.end(), p1.begin(), ::tolower); //to lowercase
for (int i=0; i<arrSize; i++) //check if string element already exists
if ( rdPtr->arrBoards[i].sName.length() ) //check length of the string in the element of the array
if ( rdPtr->arrBoards[i].sName == p1 )
{
*/ some code */
break;
}
}
I think the second idea is better because it don't need to calculate the name everytime, but I can be wrong because using if could slow down code.
Thanks for the answers
I'm sure the comparison operator (==) of the string class is already optimized enough. Just use it.
operator==(...) returns a bool based on a short-circuit comparison
return __x.size() == __n && _Traits::compare(__x.data(), __s, __n) == 0;
It checks the size of the strings before calling compare(), so, there is no need for further optimization.
Always remember one of the principles of Software Engineering: KISS :P
What you want to do is play percentages.
Since the strings are highly likely to be different, you want to find that out as quickly as possible.
You're comparing length first, but don't assume length is cheap to compute, compared to whatever else you're doing.
Here's the kind of thing I've done (in C):
if (a[0]==b[0] && strcmp(a, b)==0)
so if the leading characters are different, it never gets to the string compare.
If the dataset is such that the leading characters are likely to be different, it saves a lot of time.
(strcmp also has this kind of optimization, but you still have to pay the price of setting up the arguments and getting in and out of the function. We're talking about small numbers of cycles here.)
If you do something like that, then you may find the loop iteration overhead is costing a significant fraction of time.
If so, you might consider unrolling it.
(The compiler might unroll it for you, but I wouldn't depend on it.)
Comparing a number is faster than comparing a string. Try comparing the strings length before comparing the string itself.

no suitable conversion from "std::string" to "char" exists

I'm working on a project for school and I am running into a bit of a problem (error is in the title).
Here is the line of code that runs into the error:
kruskalS[n].nodeList[m].push_back(tempFirstCity);
kruskalS is a struct and nodeList is a vector of type string within the struct and I'm trying to insert tempFirstCity (also a string) into that array.
I could easily be making a basic mistake since I haven't done any programming since April. Any kind of help would be appreciated and I'm willing to post a bit more information from the program if needed.
A std::string is (sort of) a container of chars. A push_back function is used to add one element to the end of a container. So when you call kruskalS[n].nodeList[m].push_back(tempFirstCity);, you say you are trying to add one element to the end of the string called kruskalS[n].nodeList[m]. So the compiler expects that one element to be a char.
If you know that tempFirstCity is not empty and you want to add the first char from tempFirstCity to the end of kruskalS[n].nodeList[m] (including the case where you know tempFirstCity.size() == 1), you can do
kruskalS[n].nodeList[m].push_back(tempFirstCity[0]);
If you want to add the entire string after any current contents, you can do
kruskalS[n].nodeList[m] += tempFirstCity;
If you expect there are no current contents and/or you want to just replace anything already there with the tempFirstCity string, you can do
kruskalS[n].nodeList[m] = tempFirstCity;
You can use:
std::string::c_str()
It returns a const char *.
You say nodeList is an array of type string. i.e. std::string nodeList[x] where x is a constant.
Then assigning a new element to that array where m < x is as follows:
kruskalS[n].nodeList[m] = tempFirstCity;
Based on comments:
For appending to end of vector you don't need the index m:
kruskalS[n].nodeList.push_back(tempFirstCity);
For inserting at index m:
vector<string>::iterator itr = nodeList.begin();
for (int i = 0; i < m; i++)
itr++;
nodeList.insert(itr, tempFirstCity);
In C++, you can use string::c_str() to convert a string to C programming char array..

c++ NULL terminated array segfault

I am writing a program that splits graphs, I got a class
Graph and an Algorithm class. I compute the partitioning in my Algorithm class and split the graph with a method in the Graph class according to the partitioning.
My code looks like this:
In my GraphClass:
void bisectGraph(int *iPartitioning, Graph **Subgraphs, Edge **Separator){
...
// Store separators in an array
Separator = new Edge*[Separators.size()+1]; //Separators is a vector containing the separating edges
if(Separator == NULL)
writeErrorMsg("Error assigning memory.", "Graph::bisectGraph");
for(i=0, SepIter = Separators.begin(); SepIter != Separators.end(); i++, SepIter++)
Separator[i] = *SepIter;
Separator[Separators.size()] = NULL;
}
In my Algorithm clas I call it like this:
Edge** separators;
Graph** subgraphs;
int somePartitioning;
g->bisectGraph(somePartitioning, subgraphs, separators);
Works fine so far, but when I want to work on my separators array like this for instance:
for(int i=0; separators[i]!=NULL, i++){
...
}
I always get a segmentation fault. ddd tells me that at the end of bisectGraph separators contains some content. Since I can't find any other mistake I think i got some concept wrong?
The new value of Separator is not being propagated to the separators variable outside the function call. Even though it has type Edge ** you're assigning to it inside the function, but that only assigns to the function's copy of the variable. Remember that C++ is pass-by-value unless otherwise specified.
You could change the signature to Edge **&, but it'd be more sensible to use a vector, and take a parameter of type vector<Edge *> &.

C++ Selection Sort on Array of Structures

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.