Input : {10,9,8,7,6,5,4,3,2,1}
Output : {8,7,6,9,10,5,4,3,2,1}
I'm not sure what the issue is. I think it has something to do with the recursion in mergesort. I'm new to recursion so my understanding is not too good. Any hints?
#include <iostream>
void mergeSort(int a[], int w[], int n);
void merge(int a[], int w[], int n);
using namespace std;
void mergeSort(int a[], int t[], int n) {
if (n > 1) {
for (int i = 0; i < n/2; i++) {
t[i] = a[i];
}
mergeSort(a, t, n/2);
for (int i = n/2; i < n; i++) {
t[i] = a[i];
}
mergeSort(a, t, n/2);
merge(a, t, n/2);
}
}
void merge(int a[], int t[], int n) {
int leftIndex = 0, leftEnd = n/2;
int rightIndex = n/2, rightEnd = n;
int targetIndex = 0;
while (leftIndex < leftEnd && rightIndex < rightEnd) {
if (t[leftIndex] < t[rightIndex])
a[targetIndex++] = t[leftIndex++];
else
a[targetIndex++] = t[rightIndex++];
}
while (leftIndex < leftEnd) {
a[targetIndex++] = t[leftIndex++];
}
while (rightIndex < rightEnd) {
a[targetIndex++] = t[rightIndex++];
}
}
int main() {
const int SIZE = 10;
int a[] = {10,9,8,7,6,5,4,3,2,1};
int w[SIZE];
mergeSort(a,w,SIZE);
for (int i = 0; i < SIZE; i++) {
cout << a[i] << " ";
}
cout << endl;
}
The general problem is pointer confusion. One of the C language quirks that is not immediately obvious is that in
void mergeSort(int a[], int t[], int n);
both a and t are not arrays, but pointers. There is a special rule for this in the language standard. What this means is that in all instantiations of mergeSort on the call stack, t and a refer to the same areas of memory, and this means that every time you do something like
for (int i = 0; i < n/2; i++) {
t[i] = a[i];
}
you're changing the same region of memory. After you've done so and returned to the previous call frame, this region no longer contains the data you expect it to contain.
The way to solve this is to define a temporary local buffer where you need it, which is in merge. For example:
const int SIZE = 10;
// mergeSort is much simpler now:
void mergeSort(int a[], int n) {
if (n > 1) {
// sort the left side, then the right side
mergeSort(a , n / 2);
mergeSort(a + n / 2, n - n / 2);
// then merge them.
merge(a, n);
}
}
// Buffer work done in merge:
void merge(int a[], int n) {
// temporary buffer t, big enough to hold the left side
int t[SIZE];
int leftIndex = 0 , leftEnd = n / 2;
int rightIndex = n / 2, rightEnd = n ;
int targetIndex = 0;
// copy the left side of the target array into the temporary
// buffer so we can overwrite that left side without worrying
// about overwriting data we haven't yet merged
for(int i = leftIndex; i < leftEnd; ++i) {
t[i] = a[i];
}
// then merge the right side and the temporary buffer to
// the left side. By the time we start overwriting stuff on
// the right side, the values we're overwriting will have been
// merged somewhere into the left side, so this is okay.
while (leftIndex < leftEnd && rightIndex < rightEnd) {
if (t[leftIndex] < a[rightIndex]) {
a[targetIndex++] = t[leftIndex++];
} else {
a[targetIndex++] = a[rightIndex++];
}
}
// If there's stuff in the temporary buffer left over,
// copy it to the end of the target array. If stuff on the
// right is left over, it's already in the right place.
while (leftIndex < leftEnd) {
a[targetIndex++] = t[leftIndex++];
}
}
Before explaining the errors, let me first emphasize that a function argument like int a[] is nothing more than a pointer passed to the function. It points to a region of memory.
Now, mergesort requires some temporary memory and works by
copying the data to the temporary memory;
sorting each half of the data in temporary memory;
merging the two halves, whereby writing into the original array.
In step 2, the original array is not needed and can serve as temporary memory for the recursion.
In view of these facts, your code contains two errors:
You don't use the arrays t[] and a[] appropriately. The idea is that a[] is both input and output and t[] a temporary array. Internally, data are first copied to the temporary array, each half of which is sorted, before merging them fills the original array a[].
You don't sort the second half of the temporary array, but the first half twice.
A correct implementation is, for example,
void mergeSort(int*a, int*t, int n) {
if (n > 1) {
for (int i = 0; i < n; i++)
t[i] = a[i]; // copy to temporary
mergeSort(t , a , n/2); // sort 1st half of temporary
mergeSort(t+n/2, a+n/2, n-n/2); // sort 2nd half of temporary
merge(a, t, n);
}
}
Note that since t[] and a[] are pointers, the operation t+n/2 simply obtains the pointer to the second half of the array. The result of your code with this alteration is 1 2 3 4 5 6 7 8 9 10.
Related
I have been trying to write a code to randomly shuffle the array elements, and then use the quick sort algorithm on the array elements. This is the code I wrote:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void randomise(int arr[], int s, int e)
{
int j;
srand(time(NULL));
for (int i = e; i >= s; i--)
{
int j = rand() % (i + 1);
swap(&arr[i], &arr[j]);
}
}
int Partition(int arr[], int s, int e)
{
int pivot = arr[e];
int i = s - 1;
int j;
for (j = s; j <= e; j++)
{
if (arr[j] <= pivot)
{
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[e]);
return i + 1;
}
void QuickSort(int arr[], int s, int e)
{
if (s >= e)
return;
int x = Partition(arr, s, e);
QuickSort(arr, s, x - 1);
QuickSort(arr, x + 1, e);
}
int main()
{
int b[] = {1, 2, 3, 4, 5};
randomise(b, 0, 4);
cout << "Elements of randomised array are:";
for (int i = 0; i <= 4; i++)
{
cout << b[i] << endl;
}
QuickSort(b, 0, 4);
cout << "Elements after quick sort are:";
for (int i = 0; i <= 4; i++)
{
cout << b[i] << endl;
}
return 0;
}
However, on debugging on GDB, I found out that this program gives segmentation fault. On execution, this program gives the output as:
Elements of randomised array are:4
5
2
3
1
Can someone tell me what is the bug in this code (I have tried debugging it on GDB, but I am still clueless).
Basically, when the error is segmentation fault, you should be looking for a bug which you will feel like crashing your head into wall, after finding it. On line 26. change <=, to < . It's in your partition function. for (j = s; j < e; j++)
A little explanation about quick sort; After each time quickSort function runs on a partiotion, the last element of the partition, called pivot, will reach its' real place in array. The partition function, returns the real place of the pivot in the array. Then the main array will be split into two more partitions, before the pivot place, and after that. Your bug is returning real-pivot-place + 1, as the output of partition function. So you will run quickSort on wrong partition; the partition that is already sorted but the program will keep trying to sort it over and over because of wrong partitioning. As you may know, each time you run a function, its' variables will be saved into a stack in computer. Since your calling a recursive function over and over(that isn't supposed to stop), this stack will get full and will overflow. After that, computer will represent some undefined behavior and maybe throw an exception that can not describe the problem correctly. This is why your getting segmentation fault. But why you return real-pivot-place + 1? Because in your for loop in partition function, you will visit the pivot too, which you shouldn't. Because pivot isn't supposed to be compared with itself. So you will increase i variable unnecessarily. https://en.wikipedia.org/wiki/Call_stack Check this link for additional information about stack and how a function runs in computer.
#include <iostream>
using namespace std;
int main() {
int n,d,i=0,temp;
cin>>n>>d;
int a[1000000];
for(i=0;i<n;i++){
cin>>a[i];
}
while(d--){
temp=a[0];
for(i=1;i<n;i++){
a[i-1]=a[i];}
a[n-1]=temp;
}
for(i=0;i<n;i++){
cout<<a[i]<<" ";
}
return 0;
}
how to optimize it further as it's giving TLE error. the input file is very large obviously.
Some suggestions:
Rotate by the full amount d in a single loop (note that the result is a different array b):
for (i = 0; i < n; i++) {
b[(i+n-d) % n]=a[i];
}
Don't touch the array at all but transform the index when accessing it, for example:
cout << a[(i+n-d) % n] << " ";
The second version requires extra calculation to be done whenever accessing an array element but it should be faster if you don't need to access all array elements after each rotate operation.
There is also a way to do the rotation in-place by using a helper function that reverses a range of the array. It's a bit odd but might be the best solution. For convenience I have used a std::vector instead of an array here:
void ReverseVector( std::vector<int>& a, int from, int to ) {
for (auto i = 0; i < (to - from) / 2; i++) {
auto tmp = a[from + i];
a[from + i] = a[to - i];
a[to-i] = tmp;
}
}
void RotateVector( std::vector<int>& a, int distance ) {
distance = (distance + a.size()) % a.size();
ReverseVector( a, 0, a.size() - 1 );
ReverseVector( a, 0, distance - 1 );
ReverseVector( a, distance, a.size() - 1 );
}
I'm still relatively new to c++. I am trying to write a program that takes an array of numbers and reverses the order of those numbers in the array with a function. The program is as follows:
#include <iostream>
using namespace std;
void reverse(int *array, int size);
int main() {
int Array[] = { 1, 2, 3, 4, 5 };
int size = sizeof(Array) / 4;
reverse(Array, size);
return 0;
}
void reverse(int *array, int size) {
int Array2[5];
for (int i = 0; i < size; i++) {
Array2[i + size] = array[i];
array[i + size] = Array2[i + size];
};
}
When I run this program, it crashes and I'm not sure why. If anyone can help my figure out why it would be much appreciated. Thank you.
Zenith has it, but there are a few points and quick hacks worth noting to help you out.
#include <iostream>
//using namespace std; don't need this, and using namespace std is overkill and often
// causes problems. It pulls in a lot of stuff that may conflict, case in point
// std::reverse now becomes reverse. Which reverse will you get? Your reverse or the standard
// library's reverse? Only pull in what you need, for example
using std::cout; // still not used, but makes a good example.
void reverse(int *array, int size)
{
// no need for the other array and another loop. You can swap each element for
//it's counterpart in the upper half of the array.
for (int i = 0; i < size /2 ; i++) // only need to go half way. Other half was
// already swapped doing the first half.
{
int temp = array[i]; // store a temporary copy of element i
array[i] = array[size-1-i]; // replace element i with it's counterpart
// from the second half of the array
array[size-1-i] = temp; // replace the counterpart of i with the copy of i
// or call std::swap(array[i], array[size-1-i]);
};
}
int main() {
int Array[] = { 1, 2, 3, 4, 5 };
// int size = sizeof(Array) / 4; using 4 here can trip you up on a computer with
// a different sized int
int size = sizeof(Array) / sizeof(Array[0]);
// dividing the size of the array by the size of an element in the array will always
// get you the correct size
reverse(Array, size);
return 0;
}
Array2[i + size]
You're accessing out-of-bounds, no matter the value of i.
You probably meant Array2[size - 1 - i] to iterate the array backwards. (size - 1 is the index of the last element.)
by using swap you will get a much nicer solution which is also more efficient
void reverse(int *array, int size) {
for (int i = 0; i < size/2; i++) {
std::swap(array[i],array[size-1-i]);
};
}
When you say int size = sizeof(Array) / 4;, size is now (5 * sizeof(int)) / 4. That's how the sizeof operator works (at least when applied to arrays).
So size is probably 5, assuming a 4-byte int. Now, you get to reverse and its argument size is also 5.
You get to the for loop, and even at the first iteration, you have Array2[5] = /* something */ and array[5] = /* something */, both of which are buffer overruns.
Also, your reverse function doesn't actually do any reversing. Try this:
void reverse(int *arr, int size);
int main()
{
int Array[] = { 1, 2, 3, 4, 5 };
int size = sizeof(Array) / sizeof(int);
reverse(Array, size);
return 0;
}
void reverse(int *arr, int size)
{
int temp[5];
for (int i = 0; i < size; i++)
temp[size - 1 - i] = arr[i];
for (int i = 0; i < size; i++)
arr[i] = temp[i];
}
Trying to implement a Quick Sort algorithm.I think the problem is with the recursion, but I do not know what I should do to fix it. The program continues to crash every time I run it, and I cannot understand why. Here is the code:
#include<iostream>
using namespace std;
int pIndex;
int Partition(int *A,int start,int end){
int pivot;
int pIndex;
if(start<end){
int pivot=A[end];
int pIndex=start;
for(int x=0;x<end;x++){
if(A[x]<A[end]){
swap(A[x],A[pIndex]);
pIndex=pIndex+1;
}
}
swap(A[pIndex],A[end]);
}
//cout<<pIndex<<endl;
swap(A[pIndex],A[end]);
return pIndex;
};
void QuickSort(int *A, int start,int end){
if(start<end)
{
pIndex=Partition(A,start,end);
QuickSort(A,pIndex+1,end);
QuickSort(A,start,pIndex-1);
}
};
int main(){
int A[10]{4,23,1,43,2,10};
//Partition(A,0,9);
QuickSort(A,0,5);
for(int x=0;x<10;x++){
cout<< A[x]<<" ";
}
}
Your partition algorithm is about twice as much code as it needs to be. You seem to be always choosing the last element in the sequence for your pivot, and although not advisable, it will work for academic demonstration.
Your Crash
You're defining two pIndex values, only one of which is actually deterministic. You also declare two pivot variables, but that does NOT cause your crash (the first one is never used). It should be cleaned up none-the-less, but the death knell in your code is duplicate pIndex
int pivot;
int pIndex; // HERE
if(start<end){
int pivot=A[end];
int pIndex=start; // HERE AGAIN
for(int x=0;x<end;x++){
if(A[x]<A[end]){
swap(A[x],A[pIndex]);
pIndex=pIndex+1;
}
}
swap(A[pIndex],A[end]);
}
swap(A[pIndex],A[end]); // uses non-determined pIndex
return pIndex; // returns non-determined pIndex
Changing int pIndex=start; to simply be pIndex=start; will solve your crash, but your partition method still needs... help.
The "Sweep" Partition Method
The "sweep" method of partitioning is generally done like this for a pivot value that is assumed to be right-tailed, and you would be hard pressed to get this simpler (invoking std::partition not withstanding):
size_t Partition(int *A, size_t len)
{
if (len < 2)
return 0;
size_t pvt = 0;
for (size_t i=0; i<end; ++i)
{
if (A[i] < a[len-1])
std::swap(A[i], A[pvt++])
}
std::swap(A[pvt], a[len-1]);
return pvt;
};
The above algorithm includes only the necessities needed for a partition: the sequence iterator (a pointer in your case), and the sequence length. Everything else is deterministic based on those two items. A quick sample program of how this works follows, with a purposely placed 5 for the pivot value:
#include <iostream>
size_t Partition(int *A, size_t len)
{
if (len < 2)
return 0;
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i)
{
if (A[i] < A[len-1])
std::swap(A[i], A[pvt++]);
}
std::swap(A[pvt], A[len-1]);
return pvt;
};
int main()
{
int arr[] = { 4, 8, 7, 3, 9, 2, 1, 6, 5 };
size_t n = Partition(arr, sizeof(arr)/sizeof(*arr));
std::cout << "Partition : " << n << '\n';
for (auto x : arr)
std::cout << x << ' ';
std::cout << '\n';
}
Output
Partition : 4
4 3 2 1 5 7 8 6 9
How To Invoke From QuickSort
Invoking a partition in quicksort sets up the pivot location, which becomes the "end" iteration point of the bottom segment, and the one-BEFORE iteration point of the top segment. It is critical the pivot location returned from an invoke of Partition() should NOT be included in either subsequence when recursing.
void QuickSort(int *A, size_t len)
{
if (len < 2)
return;
size_t pvt = Partition(A, len);
QuickSort(A, pvt++); // NOTE: post increment...
QuickSort(A+pvt, len-pvt); // ...which makes this skip the pivot
}
Yeah, pointer arithmetic rocks, don't you think?
Putting It All Together
The program below incorporates both the Partition and QuickSort :
#include <iostream>
size_t Partition(int *A, size_t len)
{
if (len < 2)
return 0;
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i)
{
if (A[i] < A[len-1])
std::swap(A[i], A[pvt++]);
}
std::swap(A[pvt], A[len-1]);
return pvt;
};
void QuickSort(int *A, size_t len)
{
if (len < 2)
return;
size_t pvt = Partition(A, len);
QuickSort(A, pvt++); // NOTE: post increment
QuickSort(A+pvt, len-pvt);
}
int main()
{
int arr[] = { 4, 8, 7, 3, 9, 2, 1, 6, 5 };
QuickSort(arr, sizeof(arr)/sizeof(*arr));
for (auto x : arr)
std::cout << x << ' ';
std::cout << '\n';
}
Output
1 2 3 4 5 6 7 8 9
I hope it helps.
In this part:
int pivot;
int pIndex;
if(start<end){
int pivot=A[end];
int pIndex=start;
You are defining two pivots, and two pIndex's. You are not using the pivot at all, and with the last swap you are using the uninitialized pIndex. This should work:
int Partition(int *A,int start,int end){
int pIndex = start;
if(start<end){
for(int x=0;x<end;x++){
if(A[x]<A[end]){
swap(A[x],A[pIndex]);
pIndex=pIndex+1;
}
}
swap(A[pIndex],A[end]);
}
swap(A[pIndex],A[end]);
return pIndex;
}
I'm also something of a C++ newbie, but I find myself curious about what happens when start >= end. It looks as though your Partition function will still return a pIndex value, but I don't see where you define it anywhere. If (as I suspect) it returns whatever value happens to reside in memory, then you'll very likely end up referencing some undefined memory locations when you use A[pIndex]
I'm trying to write quicksort and understand the algorithm. I understand the 3 basic principles of quick sort
The element a[i] is in its final place in the array for some i.
None of the elements in a[l], a[i1] is greater than a[i].
None of the elemtns in a[i+1],..., a[r] is less than a[i].
I think I'm missing something about this algorithm. Any guidance much appreciated.
My first question is l and r, those are the min and max of the array? or is it any place within the left and right side of the array.
#include <iostream>
using namespace std;
void quicksort(int a[], int l , int r);
int partition(int a[], int l, int r);
void exchange(int a[], int i, int j);
int main()
{
const int MAX_ARRAY = 9;
// Quicksort
// Array of integers
int numArray[MAX_ARRAY] = {25,10,25,34,38,7,6,43,56};
for ( int i = 0 ; i < MAX_ARRAY ; i++)
{
cout << numArray[i] << endl;
}
quicksort(numArray, 4, 7);
// Call quicksort
for ( int i = 0 ; i < MAX_ARRAY ; i++)
{
cout << numArray[i]<< endl;
}
system("pause");
return 0;
}
void quicksort(int a[], int l , int r)
{
//
if (r <= l) {return;} // The max position and least position are now overlapping
int i = partition(a, l, r); // send the array and the two positions to partition
// i gives me the next position
quicksort(a,l,i-1); // sort left side
quicksort(a,i+1,r); // sort right side
}
int partition(int a[], int l, int r)
{
//Declarations
int i = l-1, j = r; int v = a[r];
for(;;) // Infinite ForLoop
{
// go through till you find a value in the array that is less than v = our pivot
while(a[++i] < v) ;
while (v < a[--j]) if (j == 1) break; // THis condition is to go thorugh and check for a number that is larger than v then if j is not at 1 then we break
if ( i >= j) break; // Overlap array
exchange(a, i , j); // swap the values
}
exchange(a,i,j); // swap the values
return i;
}
void exchange(int a[], int i, int j )
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
My first question is l and r, those are the min and max of the array? or is it any place within the left and right side of the array.
No, those are the left and right borders of the current sub-array being sorted. I have no idea why you invoke method with parameters 4 and 7: it means none of the elements before 4-th or after 7-th will be sorted.
first, quicksort(numArray, 4, 7); should be quicksort(numArray, 0, 8);
second, to make it a little bit faster, you can set a cutoff number like 10 on which you switch to insertion sort. Insertion sort is faster than quick sort for small input.