I'm trying to implement the quicksort on a string of characters. The output should give me an alphabetical order version of the input, however right now it's just giving me the original input. This was an attempt trying to translate the pseudo code from Intro to Algorithm 3rd edition on Quicksort.
Any help would be greatly appreciated, thanks!
Here's the pseudo code of quicksort from the book
#include <string>
#include <iostream>
#include <stdlib.h>
using namespace std;
int partition_str(string A, int start, int finish){
char x = A[finish], temp;
int i = start - 1;
for (int j = start; j <= finish -1; j++){
if (A[j] <= x){
i ++;
temp = A[i]; A[i] = A[j]; A[j] = temp;
}
temp = A[i+1]; A[i+1] = A[finish]; A[finish] = temp;
return i+1;
}
}
string quicksort_char(string A, int start, int finish)
{
if (start < finish)
{
start = partition_str(A, start, finish);
quicksort_char(A, start, finish -1);
quicksort_char(A, start+1, finish);
}
return A;
}
int main(){
string test = "gsgkdsdkjs";
string result = quicksort_char(test, 0, 10);
cout << result << endl;
return 0;
}
In the pseudocode you linked, it mentions that partition() alters subarrays in place. This insinuates that you need to pass by reference, rather than by value. Notice the ampersand (&) I add in the function signature. Your code was passing by value, so it was making a copy of the input string, rather than altering it in place. In your quicksort() function, you wrote the code expecting that A will be altered by the function.
I cleaned up your code a bit here with the intent of making it clearer and look more like the pseudocode...
#include <iostream>
#include <string>
using namespace std;
void exchange(char& a, char& b)
{
char value_of_a = a;
char value_of_b = b;
a = value_of_b;
b = value_of_a;
};
int partition(string& A, int p, int r)
{
char x = A[r];
int i = p-1;
for (int j=p; j<=(r-1); ++j)
{
if (A[j] <= x)
{
i++;
exchange(A[i], A[j]);
}
}
exchange(A[i+1], A[r]);
return i+1;
};
void quicksort(string& A, int p, int r)
{
if (p < r)
{
int q = partition(A, p, r);
quicksort(A, p, q-1);
quicksort(A, q+1, r);
}
};
int main()
{
string input = "gsgkdsdkjs";
cout << "input string: " << input << endl;
quicksort(input, 0, input.size());
cout << "sorted string: " << input << endl;
return 0;
}
In your partition_str() function you pass in string A by value, which makes a copy of A rather than using the same A you passed in. It then performs some operations and returns an integer. The copy of A is then thrown away and your original A variable was never modified. This means that if you want your variable A to be changed, you must pass by reference.
Also, don't be confused by the function argument naming. Your partition_str() function signature is:
int partition_str(string A, int start, int finish)
The fact the 'string A' is defined as an argument does not mean that it is related to any other variable in your code called 'A'. It is merely a way of referring to particular argument that was passed in.
Related
I tried to write quicksort by myself and faced with problem that my algorithm doesn't work.
This is my code:
#include <iostream>
#include <vector>
using namespace std;
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
void qsort(vector <int> a, int first, int last)
{
int f = first, l = last;
int mid = a[(f + l) / 2];
do {
while (a[f] < mid) {
f++;
}
while (a[l] > mid) {
l--;
}
if (f <= l) {
swap(a[f], a[l]);
f++;
l--;
}
} while (f < l);
if (first < l) {
qsort(a, first, l);
}
if (f < last) {
qsort(a, f, last);
}
}
int main()
{
int n;
cin >> n;
vector <int> a;
a.resize(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
qsort(a, 0, n - 1);
for (int i = 0; i < n; i++) {
cout << a[i] << ' ';
}
return 0;
}
My sort is similar to other that described on the Internet and I can't find where I made a mistake.
Even when I change sort function, the problem was not solved.
You don't pass qsort the array you want to sort, you pass it the value of that array. It modifies the value that was passed to it, but that has no effect on the array.
Imagine if you had this code:
void foo(int a)
{
a = a + 1;
}
Do you think if I call this like this foo(4); that foo is somehow going to turn that 4 into a 5? No. It's going to take the value 4 and turn it into the value 5 and then throw it away, since I didn't do anything with the modified value. Similarly:
int f = 4;
foo(f);
This will pass the value 4 to foo, which will increment it and then throw the incremented value away. The value f has after this will still be 4 since nothing ever changed f.
You meant this:
void qsort(vector <int>& a, int first, int last)
Your swap has the same problem. It swaps the values of a and b, but then never does anything with the value of a or b. So it has no effect. How could it? Would swap(3, 4); somehow change that 3 into a 4 and vice-versa? What would that even mean?
Your swap does not swap anything. You should write tests not only for the whole program but for as small pieces as possible. At least you should test single functions. Try this:
int main() {
int a= 42;
int b= 0;
std::cout << "before " << a << " " << b << "\n";
swap(a,b);
std::cout << "after " << a << " " << b << "\n";
}
This is "poor mans testing". For automated tests you should use a testing framework.
Then read about pass by reference. (While doing so you hopefully also realize the issue with not passing the vector to qsort by reference.)
Then use std::swap instead of reinventing a wheel.
I Found two error on your code
void swap(int a, int b) This method is not working, cause is receive value only and swap , but the original one is not updated.
void swap(int *a, int *b)
{
int t;
t = *b;
*b = *a;
*a = t;
}
swap(&a, &b);
And you pass vactor which also pass value. replace your void qsort(vector <int> &a, int first, int last) method.
The full error is as follows:- "|error: cannot convert 'int*' to 'int**' for argument '1' to 'void quickSort(int**, int, int)'|"
MY whole code is below:
#include <iostream>
using namespace std;
int Partition (int *A[], int p, int r) {
int x = *A[r];
int i = p-1;
for (int j=0; j<=r; j++){
if(*A[j]<=x){
i++;
int save=*A[j];
*A[j] = *A[i];
*A[i] = save;
}
}
int save2=*A[i+1];
*A[i+1]=*A[r];
*A[r]=save2;
return (i+1);
}
void quickSort(int *A[], int p, int r) {
if (p<r){
int q = Partition(A, p, r);
quickSort(A, p, (q-1));
quickSort(A, (q+1), r);
}
}
int main() {
int RR[] = {2,8,7,1,3,5,6,4};
int y=sizeof(RR)/sizeof(int)-1;
cout << y << endl;
int *QQ = RR;
cout << *QQ << endl;
quickSort(QQ, 0, y);
return 0;
}
This is an implementation that I tried myself from a pseudo code. I'm new to programming so it would be a great help if you could illustrate a little of why this error occurred.
Thanks in advance
The first thing I notice about the code is a whole lot of unneccessary pointer dereferencing. The contents of A will be changed without the need for additional pointers because Arrays decay to pointers (What is array decaying?) so A is treated as a pointer to the first array element and you are effectively passing the array by reference already.
Worse, int * A[] isn't a pointer to an array of int, it is an array of pointers to int. A very different thing. *A[0] does not return 2, it tries to use 2 as an address and return whatever happens to be in memory at address 2. This will almost certainly not be anything you want, or are allowed, to see so the program will do something unfortunate. Crash if you are lucky.
Instead, try
int Partition (int A[], int p, int r) {
int x = A[r];
int i = p-1;
for (int j=0; j<=r; j++){
if(A[j]<=x){
i++;
int save=A[j];
A[j] = A[i];
A[i] = save;
}
}
int save2=A[i+1];
A[i+1]=A[r];
A[r]=save2;
return (i+1);
}
void quickSort(int A[], int p, int r) {
cout << p << ',' << r << endl; // Bonus: This will make the next bug really easy to see
if (p<r){
int q = Partition(A, p, r);
quickSort(A, p, (q-1));
quickSort(A, (q+1), r);
}
}
Note the extra cout statement at the top of quickSort This will help you see the logic error in Partition. The program will crash due to a... wait for it! A Stack Overflow, but the cout will show you why.
Write a void function called string_list_sort() that reads in any number of strings (duplicates are allowed) from cin, stores them in a vector, and then sorts them. Don’t use the standard C++ sort function here — use the version of quicksort that you created.
My problem is I tried to use strcmp() but I got a lot of errors, so I tried this method, but I have a problem with char val = v[end]. I am not sure how to compare two std::string values.
I changed char to string and it works. Now my problem is for example v = {" apple", "car", "fox", " soap", "foz"}; the result I get is apple, soap, car, fox, foz which is not in alphabetical order
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <vector>
#include "error.h"
using namespace std;
void string_list_sort(vector<string> v){
string line;
while (getline(cin, line)){
if (line.empty()){
break;
}
v.push_back(line);
}
}
int partition(vector<string>&v, int begin, int end)
{
char val = v[end];
char temp;
int j = end;
int i = begin - 1;
while (true)
{
while (v[++i] < val)
while (v[--j] > val)
{
if (j == begin)
break;
}
if (i >= j)
break;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
temp = v[i];
v[i] = v[end];
v[end] = temp;
return i;
}
void quicksort(vector<string>& v, int begin, int end)
{
if (begin < end)
{
int p = partition(v, begin, end);
quicksort(v, begin, p - 1);
quicksort(v, p + 1, end);
}
}
void quick_sort(vector<string>& v)
{
quicksort(v, 0, v.size() - 1);
}
int main()
{
vector<string> v;
v =
{ " this is a test string,.,!"};
string word;
while (cin >> word)
{
v.push_back(word);
}
quick_sort(v);
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
}
OP almost has a sorting function. Two mistakes in particular stand out:
char val = v[end];
char temp;
v is a vector<string> so v[end] will return a string.
string val = v[end];
string temp;
Takes care of that and makes the program compile and successfully sort. There is no need to go inside the strings to compare character by character. string does that work for you.
The second problem: Quicksort's partition function is supposed to look like (Looting from wikipedia here)
algorithm partition(A, lo, hi) is
pivot := A[lo]
i := lo – 1
j := hi + 1
loop forever
do
i := i + 1
while A[i] < pivot
do
j := j – 1
while A[j] > pivot
if i >= j then
return j
swap A[i] with A[j]
and OP's partition function has picked up a bunch of extra baggage that needs to be removed to get an optimal mark from their instructor. Take a look at the above pseudo implementation and compare it with yours. You may see the mistakes right way, but if not, stand on the shoulders of giants and translate it into C++ (hints: := is plain old = in C++, and you'll need to add some ;s and braces). Debug the result as required. I won't translate it because that would almost totally defeat the point of the assignment.
Side notes (gathering a few important comments):
When writing a test driver don't take in user input until you know the algorithm works. Start with preloaded input that is easy to visualize like
int main()
{
vector<string> v{"C","B","A"};
quick_sort(v);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
}
When the output is "A B C ", change the input to something more complicated, but still easy to visualize
vector<string> v{"A","C","Q","B","A"};
And when that works go nuts and feed it something nasty. I like the Major General's Song from the Pirates of Penzance.
You can compare strings using std::string::compare() or relational operators.
It looks like you've tried using relational operators here, but as #user4581301 pointed out, in partition() on the first line, you have
char val = v[end];
However, v[end] is of type 'string', not 'char'. If you declare val and temp as string instead of char, you can sort them with the relational operators you have, and I think you'll be fine.
compare() documentation: fttp://www.cplusplus.com/reference/string/string/compare/
Relational operators: http://www.cplusplus.com/reference/string/string/operators/
I have read others posts, but they don't answer my problem fully.
I'm learning to delete elements from an array from the book and try to apply that code.
As far as I can grasp I'm passing array wrong or it is sending integer by address(didn't know the meaning behind that).
#include <iostream>
#include <cstdlib>
using namespace std;
void delete_element(double x[], int& n, int k);
int main()
{
// example of a function
int mass[10]={1,2,3,45,12,87,100,101,999,999};
int len = 10;
for(int i=0;i<10;i++)
{
cout<<mass[i]<<" ";
};
delete_element(mass[10],10&,4);
for(int i=0;i<10;i++)
cout<<mass[i]<<" ";
return 0;
}
void delete_element(double x[], int& n, int k)
{
if(k<1 || k>n)
{
cout<<"Wrong index of k "<<k<<endl;
exit(1); // end program
}
for(int i = k-1;i<n-1;i++)
x[i]=x[i+1];
n--;
}
There are a couple of errors in your code. I highlight some of the major issues in question 1-3:
You call exit, which does not provide proper cleanup of any objects since it's inherited from C. This isn't such a big deal in this program but it will become one.
One proper way too handle such an error is by throwing an exception cout<<"Wrong index of k "<< k <<endl;
exit(1);
Should be something like this:
throw std::runtime_error("invalid index");
and should be handled somewhere else.
You declare function parameters as taking a int& but you call the function like this: delete_element(mass[10],10&,4); 10& is passing the address of 10. Simply pass the value 10 instead.
You are "deleting" a function from a raw C array. This inherently doesn't make sense. You can't actually delete part of such an array. It is of constant compile time size created on the stack. The function itself doesn't do any deleting, try to name the functions something more task-oriented.
You are using C-Arrays. Don't do this unless you have a very good reason. Use std::array or std::vector. These containers know their own size, and vector manages it's own memory and can be re sized with minimal effort. With containers you also have access to the full scope of the STL because of their iterator support.
I suggest you rewrite the code, implementing some type of STL container
Line 15: syntax error
you can't pass a number&
If you want to pass by reference, you need to create a variable first, like:
your delete_element function signature conflicts with your declared arrays. Either use a double array or int array and make sure the signatures match.
delete_element(mass, len , 4);
when you write the name of an array without the brackets, then it's the same as &mass[0]
ie. pointer to the first element.
complete changes should be:
#include <iostream>
#include <cstdlib>
using namespace std;
void delete_element(int x[], int& n, int k);
int main(){
// example of a function
int mass[10] = { 1, 2, 3, 45, 12, 87, 100, 101, 999, 999 };
int len = 10;
for (int i = 0; i<10; i++){ cout << mass[i] << " "; };
cout << endl;
delete_element(mass, len , 4);
for (int i = 0; i<10; i++)cout << mass[i] << " ";
cout << endl;
cin.ignore();
return 0;
}
void delete_element(int x[], int& n, int k){
if (k<1 || k>n){
cout << "Wrong index of k " << k << endl;
exit(1); // end program
}
for (int i = k - 1; i<n - 1; i++)
x[i] = x[i + 1];
n--;
}
There are a couple of mistakes in your program.
Apart from some syntax issues you are trying to pass an int array to a function which wants a double array.
You cannot pass a lvalue reference of a int literal. What you want is to pass a reference to the length of the int array. see also http://en.cppreference.com/w/cpp/language/reference.
Here is an updated version of your program.
#include <iostream>
#include <cstdlib>
using namespace std;
void delete_element(int x[], int& n, int k);
int main() {
// example of a function
int mass[10] = { 1,2,3,45,12,87,100,101,999,999 };
int len = 10;
for (int i = 0;i < len;i++)
cout << mass[i] << " "; ;
cout << endl;
delete_element(mass, len, 4);
for (int i = 0;i < len;i++) // len is 9 now
cout << mass[i] << " ";
cout << endl;
return 0;
}
void delete_element(int x[], int& n, int k) {
if (k<1 || k>n) {
cout << "Wrong index of k " << k << endl;
exit(1); // end program
}
for (int i = k - 1;i<n - 1;i++)
x[i] = x[i + 1];
n--;
}
Although it does not answer your question directly, I would like to show you how you can use C++ to solve your problem in a simpler way.
#include <vector>
#include <iostream>
void delete_element(std::vector<int>& v, const unsigned i)
{
if (i < v.size())
v.erase(v.begin() + i);
else
std::cout << "Index " << i << " out of bounds" << std::endl;
}
int main()
{
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7};
delete_element(v, 4);
for (int i : v)
std::cout << i << std::endl;
return 0;
}
You cannot delete elements from an array, since an array's size is fixed. Given this, the implementation of delete_element can be done with just a single call to the appropriate algorithm function std::copy.
In addition, I highly suggest you make the element to delete a 0-based value, and not 1-based.
Another note: don't call exit() in the middle of a function call.
#include <algorithm>
//...
void delete_element(int x[], int& n, int k)
{
if (k < 0 || k > n-1 )
{
cout << "Wrong index of k " << k << endl;
return;
}
std::copy(x + k + 1, x + n, x + k);
n--;
}
Live Example removing first element
The std::copy call moves the elements from the source range (defined by the element after k and the last item (denoted by n)) to the destination range (the element at k). Since the destination is not within the source range, the std::copy call works correctly.
#include <iostream>
#include <cstdlib>
using std:: cin;
using std:: cout;
using std:: endl;
const int N=10;
void readarray(int array[], int N);
int bubble_sort (int array[], int size, int round,
int place);
int main ()
{
int array[N];
readarray( array, N );
int round, place;
cout << bubble_sort(array, N, place, round);
return EXIT_SUCCESS;
}
void readarray(int array[], int N)
{
int i=0;
if (i < N)
{
cin >> array[i];
readarray(array+1, N-1);
}
}
int bubble_sort (int array[], int size, int round,
int place)
{
round =0;
place =0;
if (round < N-1) // this goes over the array again making sure it has
// sorted from lowest to highest
{
if (place < N - round -1) // this sorts the array only 2 cells at a
// time
if (array[0] > array[1])
{
int temp = array[1];
array[1]=array[0];
array[0]=temp;
return (array+1, size-1, place+1, round);
}
return (array+1, size-1, place, round+1);
}
}
I know how to do a bubble sort using two for loops and I want to do it using recursion. Using loops you require two for loops and I figured for recursion it might also need two recursive functions/calls. This is what I have so far. The problem is that its outputting only one number, which is either 1 or 0. I'm not sure if my returns are correct.
In c++11, you can do this:
#include <iostream>
#include <vector>
void swap(std::vector<int &numbers, size_t i, size_t j)
{
int t = numbers[i];
numbers[i] = numbers[j];
numbers[j] = t;
}
bool bubble_once(std::vector<int> &numbers, size_t at)
{
if (at >= numbers.size() - 1)
return false;
bool bubbled = numbers[at] > numbers[at+1];
if (bubbled)
swap(numbers, at, at+1);
return bubbled or bubble_once(numbers, at + 1);
}
void bubble_sort(std::vector<int> &numbers)
{
if ( bubble_once(numbers, 0) )
bubble_sort(numbers);
}
int main() {
std::vector<int> numbers = {1,4,3,6,2,3,7,8,3};
bubble_sort(numbers);
for (size_t i=0; i != numbers.size(); ++i)
std::cout << numbers[i] << ' ';
}
In general you can replace each loop by a recursive function which:
check the guard -> if fail return.
else execute body
recursively call function, typically with an incremented counter or something.
However, to prevent a(n actual) stack overflow, avoiding recursion where loops are equally adequate is good practice. Moreover, a loop has a very canonical form and hence is easy to read for many programmers, whereas recursion can be done in many, and hence is harder to read, test and verify. Oh, and recursion is typically slower as it needs to create a new stackframe (citation needed, not too sure).
EDIT
Using a plain array:
#include <iostream>
#include <vector>
#define N 10
void swap(int *numbers, size_t i, size_t j)
{
int t = numbers[i];
numbers[i] = numbers[j];
numbers[j] = t;
}
bool bubble_once(int *numbers, size_t at)
{
if (at >= N - 1)
return false;
bool bubbled = numbers[at] > numbers[at+1];
if (bubbled)
swap(numbers, at, at+1);
return bubbled or bubble_once(numbers, at + 1);
}
void bubble_sort(int *numbers)
{
if ( bubble_once(numbers, 0) )
bubble_sort(numbers);
}
int main() {
int numbers[N] = {1,4,3,6,2,3,7,8,3,5};
bubble_sort(numbers);
for (size_t i=0; i != N; ++i)
std::cout << numbers[i] << ' ';
}
Please read this post
function pass(i,j,n,arr)
{
if(arr[i]>arr(j))
swap(arr[i],arr[j]);
if(j==n)
{
j=0;
i=i+1;
}
if(i==n+1)
return arr;
return pass(i,j+1,n,arr);
}