Having trouble with bubble sort C++ - c++

I'm having trouble with my bubble sort code. I am trying to sort a vector of strings containing numbers. It seems to work and then halfway through comparing numbers it starts to compare everything wrong (ex: it thinks that 4 > 35).
I read in the number from a text file while running the .o file
Here is the .txt file
6
89
-9
4
718
-60
35
92
1
Here it what I have:
using namespace std;
void bubbleSort(vector<string>&); //declare sort function
int main()
{
vector<string> v; //Initialize vector
string s; //Initialize string
while (cin >> s)
v.push_back(s);
bubbleSort(v); //call sort function
}
void bubbleSort(vector<string>& v){
for(int i = 0; i <= v.size(); i++) //start first loop through vector
for(int j = i+1; j < v.size(); j++){ //start second loop through vector
if(v[i] > v[j]){ //compare i-th element to i-th+1 (j-th) element
swap(v[i],v[j]); //swap elements if i-th element is greater than j-th element
for (int k = 0; k != v.size(); ++k) //loop through vector and print out binomials one per line
cout << v[k] << endl;
}
And this is what it outputs:
-60
-9
1
35
4
6
718
89
92
If someone could please tell me where I am going wrong it would be greatly appreciated! I don't understand why it works all the way up until it tries to compare 4 to 35 and then incorrectly compares them and throws everything off.

In string, "4" is actually bigger than "35". If you want to compare as a numbers, you should convert string to int. Then you find your desired answer.
You can do that by simply change if condition statement if( atoi(v[i].c_str()) > atoi(v[j].c_str()) ) in bubblesort function
So, the final code is:
void bubbleSort(vector<string>& v)
{
for(int i = 0; i < v.size(); i++) //start first loop through vector
for(int j = i+1; j < v.size(); j++) //start second loop through vector
{
if( atoi(v[i].c_str()) > atoi(v[j].c_str()) )
{
swap(v[i],v[j]); //swap elements if i-th element is greater than j-th element
}
}
for (int k = 0; k != v.size(); ++k) //loop through vector and print out binomials one per line
cout << v[k] << endl;
}
Output:
-60
-9
1
4
6
35
89
92
718

You should notice that although 33 is a bigger int then 4, if you compare strings it isn't the case. StrING compare checks the first char against the first char and if there is an equality it moves on to check next char. So if comparing 4 with 33 you get that 33 is lesser because 3 is lesser then 4.
Solution: use atoi to change the string to int and then check who is bigger

Basically what's going wrong is that strings do a lexicographical compare by default. Easiest fix is just to change your vector-of-strings into a vector-of-int and filling that vector with an int-converted-from-string (assuming that that conversion succeeds). How to convert a string to an int is something else to search the web or SO for.

Related

Is there any way to make this sorting algorithm have a linear time complexity?

I am doing this homework assignment in my data structures & algorithms class that asks me to make an array of random numbers, an array of counters, and make our own sorting algorithm using both arrays in C++.
The array of counters would basically contain the index of where each element is supposed to go when the array of numbers is sorted, such that the value of counters[i] would be the index where array[i] would go after it's sorted. It uses this code, and it was given by our professor:
for (int i = 1; i < arraySize; i++)
for (int j = 0; j < i; j++)
{
if (array[i] < array[j])
counters[j]++;
else
counters[i]++;
}
I thought that using both of the arrays, we would be able to design a sorting algorithm that would have a O(n) time complexity, so I tried to think of a way to do this, and I came up with this:
for (int i = 0; i < arraySize; i++)
{
int index = counters[i];
swap(array[i], array[counters[i]]);
swap(counters[i], counters[index]);
}
For every iteration, I am swapping array[i] with array[counters[i]] because counters[i] determines the index for array[i], and then I am also swapping the values in the counters to make sure that the counters stay in the same index as their corresponding value.
For some reason, this is not achieving the sorting correctly, but I don't understand why.
I'm thinking to use another sorting algorithm, but I wanted to try this one first because it would be O(n).
Anyone could help?
Thanks.
Since the professor gave you the counting (which btw even handles duplicates correctly), I agree that you should use it to finish the sorting in additional O(n).
To do that, keep swapping what's at array[i] to where it belongs until array[i] contains what belongs there. Only then go to i+1. This way you know array[0..i] all contain what belongs there (and thus in the everything is where it belongs).
for (int i = 0; i < arraySize; i++) {
int belongs_at = counters[i];
while (belongs_at != i) {
swap(array[i], array[belongs_at]);
swap(counters[i], counters[belongs_at]);
belongs_at = counters[i];
}
}
This is O(n), since every iteration of the inner loop puts one (or two) more value to where it belongs, and overall you can't put more than n values to where they belong, so overall you can't have more than n inner loop iterations.
Let's take {20, 50, 60, 70, 10, 40, 30} as an example and see what the array looks like at the end of each iteration of the for-loop:
10 20 60 70 50 40 30 # The while-loop fixed the cycle 20->50->10
10 20 60 70 50 40 30 # 20 was already where it belongs, so nothing happened
10 20 30 40 50 60 70 # The while-loop fixed the cycle 60->40->70->30
10 20 30 40 50 60 70 # 40 was already where it belongs, so nothing happened
10 20 30 40 50 60 70 # 50 was already where it belongs, so nothing happened
10 20 30 40 50 60 70 # 60 was already where it belongs, so nothing happened
10 20 30 40 50 60 70 # 70 was already where it belongs, so nothing happened
Let's see an example where yours goes wrong: {1, 2, 3, 0}. You'll first swap the 1 to where it belongs: {2, 1, 3, 0}. This still leaves 2 not where it belongs! You rely on it hopefully getting fixed later on. But that never happens, as you then swap 1 with itself, then 3 with 0, and then 3 with itself. But if at i=0 you keep going until array[i] contains what belongs there, then you don't leave that 2 there dangerously out-of-place but fix it right away.
Full code, also at repl.it (this produced the above output):
#include <iostream>
using namespace std;
int main() {
// Sample array
int arraySize = 7;
int array[7] = {20, 50, 60, 70, 10, 40, 30};
// Count
int counters[7] = {};
for (int i = 1; i < arraySize; i++)
for (int j = 0; j < i; j++)
if (array[i] < array[j])
counters[j]++;
else
counters[i]++;
// Sort
for (int i = 0; i < arraySize; i++) {
int belongs_at = counters[i];
while (belongs_at != i) {
swap(array[i], array[belongs_at]);
swap(counters[i], counters[belongs_at]);
belongs_at = counters[i];
}
// Show
for (int i = 0; i < arraySize; i++)
cout << array[i] << ' ';
cout << endl;
}
}
If you want to achieve linear time, the input array must be comes with some kind of the assumption, ie, input array is integer and within some kind of the range for counting sort and radix sort.
In the bottom solution, counterSort assume the input array is from 0-9, and insert the number to a pair,
vector< pair<vector<int>,int> >
the pair will keep tracking of the vector and its' counter.
#include <stdlib.h>
#include <iostream>
#include <vector>
using namespace std;
// counterSort
void counterSort(int sze, int* arrp, vector<pair< vector<int>,int> >& v)
{
// pair will store a map between list and counter,
for(int i = 0; i < sze; i++)
{
int numb = arrp[i];
v[numb].first.push_back(numb);
v[numb].second++;
}
}
// method for print out
void printOut(vector< pair<vector<int>,int> >& v)
{
for(auto& item: v)
{
cout << "counter:" << item.second << "| ";
for(auto& i:item.first)
{
cout << i << " ";
}
cout << endl;
}
}
int main()
{
int* arrp = new int[50];
for(int i = 0; i < 50; i++)
{
int r = rand() % 10;
arrp[i] = r;
cout << arrp[i] << " ";
}
cout << endl;
int sze = 50;
vector<pair<vector<int>,int> > v( sze,make_pair(vector<int>(0),0) );
counterSort(sze,arrp, v);
printOut(v);
delete [] arrp;
return 0;
}

What are all of these numbers following the expected output?

I was trying to write a program to take an input and output the reverse of that input; here was my code:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int num[n];
for (int i=1; i<=n; i++)
{
cin >> num[i];
}
for (int i=n; i>=0; i--)
{
cout << num[i] << " ";
}
return 0;
}
I realized that in the second for loop, i can equal 0 and then i equals -1. However, the outputs don't really make sense. For example, an input of
6
8 1 2 6 3 9
results in
9 3 6 2 1 8 8 8
and a lot of the time it's just the array reversed but with an 8 added onto the end, but sometimes there are these types of numbers:
9
1 0 2 8 1 4 2 9 8
8 9 2 4 1 8 2 0 1 1703936
Where do these ending numbers come from? Because I don't understand what's going on, I can't generalize what the problem is, but if you have an easily-accessible IDE nearby and will copy and paste my code (assuming it's not a well-known problem and I'm making everyone laugh at my stupidity), could anyone tell me why there are these numbers added onto the end?
for (int i=1; i<=n; i++)
In C++, array indexes are 0-indexed. This means if your array is size n, the valid indexes are 0, 1, 2, ..., n-1.
Your loop will loop over 1, 2, 3, ..., n. Do you see the issue? You never write to the 0'th index, and you write one past the last index (which is not allowed).
Then when you go to print:
for (int i=n; i>=0; i--)
You print n, n-1, n-2, ..., 0. You're printing the n'th element, which is again, not allowed.
Instead, your loops should look like:
for (int i=0; i<n; i++)
{
cin >> num[i];
}
for (int i = 0; i < n; i++)
{
cout << num[n - i - 1] << " ";
}
Your second loop begins with i equal to n ... but that's not a valid array index, because an array containing n elements has indexes [0..n-1]! Therefore, you are seeing "memory garbage."
So, your second loop should be:
for (int i=n-1; i>=0; i--)
You seem to think that arrays are indexed from 1 which is your main issue.
for (int i=1; i<=n; i++)
The last element of an array if length-1, so if you had 3 elements the last index is 2, not 3; however, the loop above starts at 1 (the second element) and then accesses an index out of bounds, which is undefined behaviour.
But really you don't need to use indices at all, just for (auto& i : n) will iterate properly over your container. If you want to loop using indices, you need
for (int i = 0; i < n; i++) // forwards
for (int i = n-1; i >= 0; i--) //backwards
It's worth noting that variable length arrays (that is, arrays whose lengths are not known at compile time) are not standard C++, but are an extension of GCC. It would be worth ditching that behaviour now, and using vector instead:
int length = 0;
std::cin >> length;
std::vector<int> n(length);

Insert numbers divisible by a number into a vector

I was given the integers 15, 16, 17 ,18 ,19 and 20.
I am supposed to put only the numbers divisible by 4 into a vector and then display the values in the vector.
I know how to do the problem using arrays but I'm guessing I don't know how to properly use pushback or vectors.
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> arrmain; int i,j;
for (int i = 15; i <=20 ; i++)
{
//checking which numbers are divisible by 4
if (i%4 == 0)
{ //if number is divisible by 4 inserting them into arrmain
arrmain.push_back(i);
//output the elements in the vector
for(j=0; j<=arrmain.size(); j++)
{
cout <<arrmain[i]<< " "<<endl;
}
}
}
return 0;
}
wanted output: Numbers divisible by 4: 16, 20
As already mentioned in the comments, you have a couple of problems in your code.
All which will bite you in the end when writing more code.
A lot of them can be told to you by compiler-tools. For example by using -Weverything in clang.
To pick out the most important ones:
source.cpp:8:10: warning: declaration shadows a local variable [-Wshadow]
for (int i = 15; i <=20 ; i++)
and
source.cpp:6:26: warning: unused variable 'i' [-Wunused-variable]
vector arrmain; int i,j;
Beside those, you have a logical issue in your code:
for values to check
if value is ok
print all known correct values
This will result in: 16, 16, 20 when ran.
Instead, you want to change the scope of the printing so it doesn't print on every match.
Finally, the bug you are seeing:
for(j=0; j<=arrmain.size(); j++)
{
cout <<arrmain[i]<< " "<<endl;
}
This bug is the result of poor naming, let me rename so you see the problem:
for(innercounter=0; innercounter<=arrmain.size(); innercounter++)
{
cout <<arrmain[outercounter]<< " "<<endl;
}
Now, it should be clear that you are using the wrong variable to index the vector. This will be indexes 16 and 20, in a vector with max size of 2. As these indexes are out-of-bounds for the vector, you have undefined behavior. When using the right index, the <= also causes you to go 1 index out of the bounds of the vector use < instead.
Besides using better names for your variables, I would recommend using the range based for. This is available since C++11.
for (int value : arrmain)
{
cout << value << " "<<endl;
}
The main issues in your code are that you are (1) using the wrong variable to index your vector when printing its values, i.e. you use cout <<arrmain[i] instead of cout <<arrmain[j]; and (2) that you exceed array bounds when iterating up to j <= arrmain.size() (instead of j < arrmain.size(). Note that arrmain[arrmain.size()] exceeds the vector's bounds by one because vector indices are 0-based; an vector of size 5, for example, has valid indices ranging from 0..4, and 5 is out of bounds.
A minor issue is that you print the array's contents again and again while filling it up. You probably want to print it once after the first loop, not again and again within it.
int main()
{
vector<int> arrmain;
for (int i = 15; i <=20 ; i++)
{
//checking which numbers are divisible by 4
if (i%4 == 0)
{ //if number is divisible by 4 inserting them into arrmain
arrmain.push_back(i);
}
}
//output the elements in the vector
for(int j=0; j<arrmain.size(); j++)
{
cout <<arrmain[j]<< " "<<endl;
}
return 0;
}
Concerning the range-based for loop mentioned in the comment, note that you can iterate over the elements of a vector using the following abbreviate syntax:
// could also be written as range-based for loop:
for(auto val : arrmain) {
cout << val << " "<<endl;
}
This syntax is called a range-based for loop and is described, for example, here at cppreference.com.
After running your code, I found two bugs which are fixed in code below.
vector<int> arrmain; int i, j;
for (int i = 15; i <= 20; i++)
{
//checking which numbers are divisible by 4
if (i % 4 == 0)
{ //if number is divisible by 4 inserting them into arrmain
arrmain.push_back(i);
//output the elements in the vector
for (j = 0; j < arrmain.size(); j++) // should be < instead of <=
{
cout << arrmain[j] << " " << endl; // j instead of i
}
}
}
This code will output: 16 16 20, as you are printing elements of vector after each insert operation. You can take second loop outside to avoid doing repeated operations.
Basically, vectors are used in case of handling dynamic size change. So you can use push_back() if you want to increase the size of the vector dynamically or you can use []operator if size is already predefined.

Fixing my implementation of Sieve of Eratosthenes in C++

My algorithm runs correctly up to the first 100 primes but then something goes wrong. Please have a look at my code below, I tried to follow the pseudo-code given here https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main()
{
int n = 1000; //compute primes up to this number
vector<bool> p(true,n); //all values set to true, from 0 to n
for(int i = 2; i < sqrt(n)+1; i++){
if( p[i-1] == true ){
for(int j = i*i; j < n; j += i) //start looking for multiples of prime i at i*i (optimized)
p[j-1] = false;
}
}
for(int i = 2; i < n; i++){
if( p[i-1] == true )
cout << i << "\n";
}
return 0;
}
The output is:
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
193
199
I'm absolutely amazed the program runs at all. It has an absolute truckload of undefined behaviour!.
Unless I'm very much mistaken (in which case please reward the serenity of my Friday afternoon with downvotes), vector<bool> p(true, n) is creating a vector of size true, with the elements initialised to n.
You have the constructor arguments the wrong way round. This is effectively reversing the sieve for most of the values.
Have you absolutely clobbered your compiler's warning level?
First of all, you do not need to store a boolean value for each and every number. This way, you are wasting memory. You should store only the found primes instead, unless you have a very good reason not to do so.
I will not implement the code, since it would spoil the joy of learning. You should implement the following:
Initialize p as a vector of integers.
Store 2 as first value inside p
Iterate all odd numbers starting from 3 and ending at the end number
For each number, calculate its square root and store it into a variable
Iterate all previous elements of the p until you reach a divisor or the value of the vector at the given index reaches the square root, starting from the second element, as pair numbers larger than 2 are ignored
If you find a divisor inside your inner loop, store it into your vector and get out of the inner loop
At the end you will have a vector of primes, indexes will mean the prime index and the values will be actual primes. Each element will be a prime of its own.
Your vector construction is wrong. It must be
vector<bool> p(n, true); //all values set to true, from 0 to n
instead of
vector<bool> p(true, n); //all values set to true, from 0 to n
You mess your numbering up. If p[k] is the primeness of number k+1, your for loop is wrong
for(int j = i*i; j < n; j += i)
and should be
for(int j = (i-1)*(i-1); j < n; j += (i-1))
My advice would be to use more informative variable names and avoid those sources of confusion like p[k] giving information about integer k+1.

Sorting two arrays into a combined array

I haven't done any programming classes for a few years, so please forgive any beginner mistakes/methods of doing something. I'd love suggestions for the future. With the code below, I'm trying to check the values of two arrays (sorted already) and put them into a combined array. My solution, however inefficient/sloppy, is to use a for loop to compare the contents of each array's index at j, then assign the lower value to index i of the combinedArray and the higher value to index i+1. I increment i by 2 to avoid overwriting the previous loop's indexes.
int sortedArray1 [5] = {11, 33, 55, 77, 99};
int sortedArray2 [5] = {22, 44, 66, 88, 00};
combinedSize = 10;
int *combinedArray;
combinedArray = new int[combinedSize];
for(int i = 0; i <= combinedSize; i+=2)
{
for(int j = 0; j <= 5; j++)
{
if(sortedArray1[j] < sortedArray2[j])
{
combinedArray[i] = sortedArray1[j];
combinedArray[i+1] = sortedArray2[j];
}
else if(sortedArray1[j] > sortedArray2[j])
{
combinedArray[i] = sortedArray2[j];
combinedArray[i+1] = sortedArray1[j];
}
else if(sortedArray1[j] = sortedArray2[j])
{
combinedArray[i] = sortedArray1[j];
combinedArray[i+1] = sortedArray2[j];
}
}
}
for(int i = 0; i < combinedSize; i++)
{
cout << combinedArray[i];
cout << " ";
}
And my result is this
Sorted Array 1 contents: 11 33 55 77 99
Sorted Array 2 contents: 0 22 44 66 88
5 77 5 77 5 77 5 77 5 77 Press any key to continue . . .
In my inexperienced mind, the implementation of the sorting looks good, so I'm not sure why I'm getting this bad output. Advice would be fantastic.
what about this:
int i=0,j=0,k=0;
while(i<5 && j<5)
{
if(sortedArray1[i] < sortedArray2[j])
{
combinedArray[k]=sortedArray1[i];
i++;
}
else
{
combinedArray[k]=sortedArray2[j];
j++;
}
k++;
}
while(i<5)
{
combinedArray[k]=sortedArray1[i];
i++;k++;
}
while(j<5)
{
combinedArray[k]=sortedArray2[j];
j++; k++;
}
Firstly, there are some immediate problems with how you use C++:
You use = instead of == for equality check (hence causing undesired value assignments and the if-condition to return true when it shouldn't);
Your outer loops upper boundary is defined as i <= 10, while the correct boundary check would be i < 10;
You have a memory leak at the end of the function because you fail to de-allocate memory. You need a delete [] combinedArray at the end.
Secondly, your outer loop iterates through all values of the destination array, and in each step uses an inner loop to iterate through all values of the source arrays. That is not what you want. What you want is one loop counting from j=0 to j<5 and iterating through the source arrays. The positions in the destination array are then determined as 2*j and 2*j+1, and there is no need for an inner loop.
Thirdly, as explained in the comment, a correct implementation of sorted-list merge needs two independent counters j1 and j2. However, your current input is hardwired into the code, and if you replace 00 with 100, your current algorithm (after the corrections above are made) will actually work for the given input.
Finally, but less importantly, I wonder why your destination array is allocated on the heap using new. As long as you are dealing with small arrays, you may allocate it on the stack just like the source arrays. If, however, you allocate it on the heap, better use a std::unique_ptr<>, possibly combined with std::array<>. You'll get de-allocation for free then without having to think of putting a delete [] statement at the end of the function.
Before even looking at the implementation, check the algorithm and write it down with pen and paper. The first thing that pops is that you are assuming that the first two elements in the result will come one from each source array. That need not be the case, consider two arrays where all elements in one are smaller than all elements in the other and the expected result:
int a[] = { 1, 2, 3 };
int b[] = { 4, 5, 6 };
If you want the result sorted, then the first three elements come all from the first array. With that in mind think on what you really know about the data. In particular, both arrays are sorted, which means that the first elements will be smaller than the rest of the elements in the respective array. The implication of this is that the smaller element is the smaller of the heads. By putting that element into the result you have reduced the problem to a smaller set. You have a' = { 2, 3 }, b = { 4, 5, 6 } and res = { 1 } and a new problem that is finding the second element of res knowing that a' and b are sorted.
Figure out in paper what you need to do, then it should be straight forward to map that to code.
So, I modified your code to make it work. Actually it would be good idea to have two pointer/index for two sorted arrays. So that you can update your corresponding pointer after adding it to your combinedArray. Let me know if you don't understand any part of this code. Thanks.
int sortedArray1 [5] = {11, 33, 55, 77, 99};
int sortedArray2 [5] = {0, 22, 44, 66, 88};
int combinedSize = 10;
int *combinedArray;
combinedArray = new int[combinedSize];
int j = 0;
int k = 0;
for(int i = 0; i < combinedSize; i++)
{
if (j < 5 && k < 5) {
if (sortedArray1[j] < sortedArray2[k]) {
combinedArray[i] = sortedArray1[j];
j++;
} else {
combinedArray[i] = sortedArray2[k];
k++;
}
}
else if (j < 5) {
combinedArray[i] = sortedArray1[j];
j++;
}
else {
combinedArray[i] = sortedArray2[k];
k++;
}
}
for(int i = 0; i < combinedSize; i++)
{
cout << combinedArray[i];
cout << " ";
}
cout<<endl;
The else if(sortedArray1[j] = sortedArray2[j]), did you mean else if(sortedArray1[j] == sortedArray2[j])?
The former one will assign the value of sortedArray2[j] to sortedArray1[j] -- and that's the reason that why you get 5 77 5 77...
But where's the 5 come from? There's no 5 in either sortedArray, yet I find for(int j = 0; j <= 5; j++) must be something wrong. The highest index of a size N array is N-1 rather than N in C/C++(but not in Basic).. so use j<5 as the condition, or you may fall into some situation which is hard to explain or predict..
After all, there's problem in your algorithm itself, every time the outer loop loops, it will at last compare the last elements in the two arrays, which makes the output to repeat two numbers.
So you need to correct your algorithm too, see Merge Sort.
Slightly different approach, which is IMHO a bit cleaner:
//A is the first array, m its length
//B is the second array, n its length
printSortedAndMerged(int A[], int m, int B[], int n){
int c[n+m];
int i=0, j=0;
for(int k=0; k < n+m; k++){
if(i < m && j < n){
if(A[i] < B[j]){
c[k] = A[i];
i++;
}
else{
c[k] = B[j];
j++;
}
continue; //jump to next iteration
}
if(i < m){ // && ~(j < n)
//we already completely traversed B[]
c[k] = A[i];
i++;
continue;
}
if(j < n){ // %% ~(i < m)
//we already completely traversed A[]
c[k] = B[j];
j++;
continue;
}
//we should never reach this
cout << "Wow, something wrong happened!" << endl;
}//for
for(int i=0; i<n+m; i++){
cout << c[i] << endl;
}
}
Hope it helps.