I tried to implement number of inversions in an array, using merge sort.
Every time I execute this code, I get different value of the number of inversions. I am not able to figure out the reason for this. Please have a look at the code and tell me the mistake.
#include<stdio.h>
#include<iostream>
using namespace std;
int count =0;
void merge(int A[],int start,int mid,int end)
{
int size1 = mid-start+1;
int size2 = end-(mid+1)+1;
int P[size1];
int Q[size2];
for(int i=0;i<size1;i++)
P[i]=A[start+i];
for(int j=0;j<size2;j++)
Q[j]=A[mid+j+1];
int k = 0;
int l = 0;
int i =0;
while(k<mid && l<end)
{
if(P[k]>Q[l])
{
A[i] = Q[l];
l++; i++;
count++;
}
else
{
A[i] = P[k];
k++; i++;
}
}
}
void inversions(int A[],int start,int end)
{
if(start!=end)
{
int mid = (start+end)/2;
inversions(A,start,mid);
inversions(A,mid+1,end);
merge(A,start,mid,end);
}
}
int main()
{
int arr[] = {4,3,1,2,7,5,8};
int n = (sizeof(arr) / sizeof(int));
inversions(arr,0,n-1);
cout<<"The number of inversions is:: "<<count<<endl;
return 0;
}
int k = 0;
int l = 0;
int i =0;
while(k<mid && l<end)
{
if(P[k]>Q[l])
{
A[i] = Q[l];
l++; i++;
count++;
}
else
{
A[i] = P[k];
k++; i++;
}
}
Few mistakes here, i starts from start and not 0. k must loop from 0 till size1 and not till mid. Similarly, l must loop from 0 till size2 and not till end. You are incrementing count by 1 when P[k] > Q[l] but this is incorrect. Notice that all the elements in array P following the element P[k] are greater than Q[l]. Hence they also will form an inverted pair. So you should increment count by size1-k.
Also, the merge procedure should not only count the inversions but also merge the two sorted sequences P and Q into A. The first while loop while(k<size1 && l<size2) will break when either k equals size1 or when l equals size2. Therefore you must make sure to copy the rest of the other sequence as it is back into A.
I have made the appropriate changes in merge and pasted it below.
void merge(int A[],int start,int mid,int end)
{
int size1 = mid-start+1;
int size2 = end-(mid+1)+1;
int P[size1];
int Q[size2];
for(int i=0;i<size1;i++)
P[i]=A[start+i];
for(int j=0;j<size2;j++)
Q[j]=A[mid+j+1];
int k = 0;
int l = 0;
int i = start;
while(k<size1 && l<size2)
{
if(P[k]>Q[l])
{
A[i] = Q[l];
l++; i++;
count += size1-k;
}
else
{
A[i] = P[k];
k++; i++;
}
}
while (k < size1)
{
A[i] = P[k];
++i, ++k;
}
while (l < size2)
{
A[i] = Q[l];
++i, ++l;
}
}
int P[size1];
int Q[size2];
VLA (Variable length arrays) are not supported by C++. size1 and size2 are unknown during compile time. So, each time they get a different value and hence the difference in output.
Use std::vector instead
std::vector<int> P(size1, 0); //initialize with size1 size
std::vector<int> Q(size2, 0); //initialize with size2 size
Related
I was trying to solve the quick sort - 2 challenge on hackerrank. It said that we had to repeatedly call partition till the entire array was sorted. My program works for some test cases but for some it crashes, "Quick Sort - 2.exe has stopped working". I couldn't find the reason as to why it's happening.
The first element of the array/sub-array was to be taken as pivot element each time.
#include <iostream>
#include <conio.h>
using namespace std;
void swap(int arr[], int a, int b)
{
int c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
void qsort(int arr[], int m, int n) //m - lower limit, n - upper limit
{
if (n - m == 1)
{
return;
}
int p = arr[m], i, j, t; //p - pivot element, t - temporary
//partition
for (int i = m+1; i < n; i++)
{
j = i;
if (arr[j] < p)
{
t = arr[j];
while (arr[j] != p)
{
arr[j] = arr[j-1];
j--;
}
arr[j] = t; //pivot is at j and j+1
}
}
//check if sorted
int f = 1;
while (arr[f] > arr[f-1])
{
if (f == n-1)
{
f = -1;
break;
}
f++;
}
if (f == -1)
{
cout << "Sub Array Sorted\n";
}
else
{
if (p == arr[m]) //pivot is the smallest in sub array
{
qsort(arr, m+1, n); //sort right sub array
}
else
{
qsort(arr, m, j+1); //sort left sub array
qsort(arr, j+1, n); //sort right sub array
}
}
}
int main()
{
int n;
cin >> n;
int arr[n];
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
qsort(arr, 0, n);
for (int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
return 0;
}
You have an index out of range problem.
This will not give you the solution, but it may help you to find the reason why your program fails.
I have modified your program so it uses a vector of int rather than a raw array of int, and when you run this program you get an index out of range exception.
The sequence 4 3 7 1 6 4 that triggers the problem is hardcoded, so you don't need to type it each time.
#include <iostream>
#include <vector>
using namespace std;
void swap(vector<int> & arr, int a, int b)
{
int c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
void qsort(vector<int> & arr, int m, int n) //m - lower limit, n - upper limit
{
if (n - m == 1)
{
return;
}
int p = arr[m], j, t; //p - pivot element, t - temporary
//partition
for (int i = m + 1; i < n; i++)
{
j = i;
if (arr[j] < p)
{
t = arr[j];
while (arr[j] != p)
{
arr[j] = arr[j - 1];
j--;
}
arr[j] = t; //pivot is at j and j+1
}
}
//check if sorted
int f = 1;
while (arr[f] > arr[f - 1])
{
if (f == n - 1)
{
f = -1;
break;
}
f++;
}
if (f == -1)
{
cout << "Sub Array Sorted\n";
}
else
{
if (p == arr[m]) //pivot is the smallest in sub array
{
qsort(arr, m + 1, n); //sort right sub array
}
else
{
qsort(arr, m, j + 1); //sort left sub array
qsort(arr, j + 1, n); //sort right sub array
}
}
}
int main()
{
vector<int> arr = { 4,3,7,1,6,4 };
qsort(arr, 0, arr.size());
for (unsigned int i = 0; i < arr.size(); i++)
{
cout << arr[i] << " ";
}
return 0;
}
First of all, what you made is not quick sort, but some combination of divide-ans-conquer partitioning and insert sort.
Canonical quicksort goes from from lower (p) and upper (q) bounds of array, skipping elements arr[p]m respectively. Then it swaps arr[p] with arr[q], increments/decrements and checks if p>=q. Rinse and repeat until p>=q. Then make calls on sub-partitions. This way p or q holds pivot position and subcalls are obvious.
But you are doing it different way: you insert elements from right side of subarray to left side. Such thing can produce O(N^2) time complexity for one iteration. Consider 1,0,1,0,1,0,1,0,1,0,1,0,... sequence, for example. This can increase worst case complexity over O(N^2).
Out of time complexity... The problem in your function lies in assumption that j holds pivot location in subcalls:
qsort(arr, m, j+1); //sort left sub array
qsort(arr, j+1, n); //sort right sub array
Actually, j is set again and again equal to i in your main for loop. If last element is equal or greater than pivot, you end up with j=n-1, the you call qsort(arr, n, n) and first lines check is passed (sic!), because n-n != 1.
To fix this you should do two things:
1) find pivot location directly after rearrange:
for (int i = m; i < n; i++)
if (p == arr[i])
{
j = i;
break;
}
or initialize it in different variable, update after this line:
arr[j] = t; //pivot is at j and j+1
and update recursive calls to use new variable instead of j
2) make a more bulletproof check in the beginning of your function:
if (n - m <= 1)
the latter will be enough to get some result, but it will be much less effective than your current idea, falling down to probably O(N^3) in worst case.
well it's not exactly a merge sort, the algorithm counts the number on inversions in an array using merge sort (basically I just added one simple line)
it takes 2.415 seconds to read and merge sort 100,000 distinct integers from a text file while others who solved the same problem (on coursera.com) said it took them less than 0.5 seconds
here's my code, what went wrong? file reading maybe? thanks
#include <bits/stdc++.h>
using namespace std;
int a,b,i,j,n,x,k;
int t[100000]={0};
long long int s=0;
void merge(int a, int mid, int b)
{
i=a;
j=mid+1;
k=a;
int v[100000]={0};
while(i<=mid && j<= b)
{
if (t[i]<t[j])
{v[k]=t[i];
i++;
}
else
{v[k]=t[j];
j++;
s+=mid-i+1; //this line here counts the inversions
}
k++;
}
while(i<=mid)
{v[k]=t[i];
i++; k++;}
while(j<=b)
{v[k]=t[j];
j++; k++;}
for (i=a;i<k;i++)
t[i]=v[i];
}
void mergeSort(int a, int b)
{
if(a<b)
{
int mid=(a+b)/2;
mergeSort(a,mid);
mergeSort(mid+1,b);
merge(a,mid,b);
}
}
int main(){
ifstream fin("C:\\Users\\ASUS\\Desktop\\coursera.txt");
n=100000;
for(i=0;i<n;i++)
fin>>t[i];
mergeSort(0,n-1);
cout<<endl<<s;
}
One issue I could see is that in merge function, you allocate too much space, and the copy back also take quite O(N), which make the total copy time O(N * N) instead of O(N*Log(N)). Simple change to merge function could be like:
void merge(int a, int mid, int b)
{
i = a;
j = mid + 1;
k = 0;
int* v = new int[b - a + 1];
while (i <= mid && j <= b)
{
if (t[i]<t[j])
{
v[k] = t[i];
i++;
}
else
{
v[k] = t[j];
j++;
s += mid - i + 1; //this line here counts the inversions
}
k++;
}
while (i <= mid)
{
v[k] = t[i];
i++; k++;
}
while (j <= b)
{
v[k] = t[j];
j++; k++;
}
for (i = 0; i<k; i++)
t[a+i] = v[i];
delete[] v;
v = NULL;
}
I'm trying to create a program merge-sort on an array of int butI keep having troubles running this merge sort, it gives me a segment fault but I couldn't find anything wrong with it. In void mergesort when I put first <= last then the segment fault appears if not, then 5 5 5 5 is being print.
#include <iostream>
using namespace std;
void merge(int *arr, int size, int first, int middle, int last)
{
int temp[size];
for(int i = first; i<=last; i++)
{
temp[i] = arr[i];
}
int i=first, j=middle+1, k=0;
while(i<=middle && j<=last)
{
if(temp[i] <= temp[j])
{
arr[k] = temp[i];
i++;
}
else
{
arr[k]=temp[i];
j++;
}
k++;
}
while(i<=middle)
{
arr[k]=temp[i];
k++;
i++;
}
}
void mergesort(int *arr, int size, int first, int last)
{
if(first<last)
{
int middle = ( first + last )/2;
mergesort(arr,size,first,middle);
mergesort(arr,size,middle+1,last);
merge(arr,size,first,middle,last);
}
}
int main()
{
cout <<"Him";
const int size = 10;
int numbers [] = {5,10,1,6,2,9,3,8,7,4};
mergesort(numbers,size,0,9);
for( int i= 0; i<size; ++i)
{
cout << numbers[i] << " ";
}
return 0;
}
There are (at least) two bugs. This:
else
{
arr[k]=temp[i];
j++;
}
should be this:
else
{
arr[k]=temp[j];
j++;
}
and this:
int i=first, j=middle+1, k=0;
should be this:
int i=first, j=middle+1, k=first;
In general, you ought to learn to step through the code, at least by putting diagnostic output statements here and there. Once you have the hang of that you can move up to a good debugger.
The standard library already implements a function that merges correctly: std::inplace_merge. Implementation adapted from this more general post
void mergesort(int * first, int * last)
{
std::ptrdiff_t N = std::distance(first, last);
if (N <= 1) return;
int * middle = std::next(first, N / 2);
mergesort(first, middle);
mergesort(middle, last);
std::inplace_merge(first, middle, last);
}
int main()
{
cout <<"Him";
const int size = 10;
int numbers [] = {5,10,1,6,2,9,3,8,7,4};
mergesort(numbers, numbers+size);
for( int i= 0; i<size; ++i)
{
cout << numbers[i] << " ";
}
return 0;
}
Suggestion 1:
Instead of that line:
int temp[size];
If you need a dynamic size array use:
int temp = new int[size];
Then once you are done with it
delete[] temp;
Edit: As Neil suggested using std::vector is may be more useful than arrays in such situations (if you are allowed to use it).
Your code has 3 bugs, Also you can reduce your code length too if required.
void merge(int *arr, int size, int first, int middle, int last)
{
int temp[size];
for(int i = first; i<=last; i++)
temp[i] = arr[i];
int i=first, j=middle+1, k=first; // 1st Change, Set k to first instead of 0
while(i<=middle && j<=last)
{
if(temp[i] <= temp[j])
arr[k++] = temp[i++];
else
arr[k++]=temp[j++]; // 2nd Change, use j instead of i
}
while(i<=middle)
arr[k++]=temp[i++];
while(j<=last) // 3rd Change you missed this case
arr[k++]=temp[j++];
}
Live Code
I don't understand why they give me different output when I compile them. For example ... when I compile only one algorithm the answer is good, the same is for the other one, but when I compile them both at the same time they give me some weird output.
My code:
#include <iostream>
using namespace std;
int parent(int i){
return i/2;
}
int leftChild(int i){
return 2*i+1;
}
int rightChild(int i){
return 2*i+2;
}
void maxHeapify(int a[], int i, int n){
int largest;
int temp;
int l = leftChild(i);
int r = rightChild(i);
// p.countOperation("CMPbottomUp",n);
if (l <= n && (a[l] > a[i]))
largest = l;
else
largest = i;
// p.countOperation("CMPbottomUp",n);
if (r <= n && (a[r] > a[largest]))
largest = r;
if (largest != i){
// p.countOperation("ATTbottomUp",n);
temp = a[i];
// p.countOperation("ATTbottomUp",n);
a[i] = a[largest];
//p.countOperation("ATTbottomUp",n);
a[largest] = temp;
maxHeapify(a, largest, n);
}
}
void buildMaxHeap(int a[], int n){
for (int i=n/2; i>=0; i--){
maxHeapify(a, i, n);
}
}
void heapSort(int a[],int n){
buildMaxHeap(a,n);
int n1=n;
int temp;
for(int i=n1;i>0;i--){
temp = a[0];
a[0] = a[i];
a[i] = temp;
n1--;
maxHeapify(a,0,n1);
}
}
int partitionArray(int arr[], int left, int right){
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
return i;
}
void quickSort(int arr[], int left, int right) {
int index;
index = partitionArray(arr, left, right);
if (left < index - 1)
quickSort(arr, left, index - 1);
if (index < right)
quickSort(arr, index, right);
}
int main(){
int x[8]= {5,87,21,4,12,7,44,3};
int a[8];
for(int i=0;i<8;i++){
a[i] = x[i];
}
heapSort(x,8);
quickSort(a,0,8);
for(int i=0;i<8;i++){
cout<<a[i]<<' ';
}
cout<<endl;
for(int j=0;j<8;j++){
cout<<x[j]<<' ';
}
return 0;
}
Example output:
1) When I compile only one algorithm the output is : 3,4,5,7,12,21,44,87 (which is good)
2) When I compile both of them in the code the output is: 87,4,5,7,12,21,44,87 (quickSort) and 3,3,4,5,7,12,21,44 (heapSort)
I think that should work:
heapSort(x,7);
quickSort(a,0,7);
Arrays a and x are right next to each others in stack. Seeing how you have duplicate value 87 in output, it seems your sort functions access memory outside the array you give to them. This is buffer overrun, a type of Undefined Behaviour. With that, your code could do anything because you have corrupted variable values (or worse, corrupted addresses/pointers).
Double check how you access arrays. Remember that C array indexes for your arrays of length 8 are 0..7!
I am trying to implement Quick Sort algorithm. Following code works for unique elements but it doesn't working for arrays having duplicate elements. Please tell me where I am doing wrong. Also when I change value of pivot to some other number other than 0 , program crashes. Here is the code:
#include <iostream>
#include <cstdlib>
using namespace std;
void swapme(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void quicksort(int *arr, int size)
{
// these two variables will take care of position of comparison
int lower = 0, upper = size - 1;
int pivot = 0; // assigns pivot
if (size <= 1)
return;
while (lower < upper)
{
while (arr[lower] < arr[pivot])
{
++lower;
}
}
while (arr[upper] > arr[pivot])
{
--upper;
}
if (upper > lower)
{
swapme(arr[upper], arr[lower]);
// upper--;
// lower++;
}
quicksort(arr, lower);
quicksort(&arr[lower + 1], size - 1 - lower);
}
int main()
{
int arr[30];
for(int j = 0; j < 30; j++)
{
arr[j] = 1 + rand() % 5000;
}
for(int j = 0; j < 30; j++)
{
cout << arr[j] << "\t";
}
cout << endl;
quicksort(arr, 30);
for(int j = 0; j < 30; j++)
{
cout << arr[j] << "\t";
}
cout << endl;
cin.get();
cin.get();
}
Update: I have finally managed to make it work. Here is the fixed version:
void swapme(int &a, int &b )
{
int temp = a;
a = b;
b = temp;
}
void quicksort(int *arr, int size)
{
if (size <= 1)
return;
// These two variables will take care of position of comparison.
int lower = 0;
int upper = size-1;
int pivot = arr[upper/2]; // assigns pivot
while (lower <= upper)
{
while (arr[lower] < pivot)
++lower;
while (arr[upper] > pivot)
--upper;
if (upper >= lower)
{
swapme(arr[upper],arr[lower]);
if(arr[upper] == arr[lower])
{
// Can either increment or decrement in case of duplicate entry
upper--; // lower++;
}
}
}
quicksort(arr, lower);
quicksort( &arr[lower+1], size-1-lower);
}
You are storing the index of your pivot element in the pivot variable, so swapping the elements can potentially change the choice of pivot element during the loop. Not a very good idea. I would suggest storing the actual value of the pivot element inside pivot instead.
Also, if this really isn't homework, why don't you simply use the standard library facilities?
#include <algorithm>
// ...
std::sort(arr + 0, arr + 30);
You will get heavily optimized and tested code that will outperform your handwritten Quicksort anytime.
Quick Sort that can implement any number of i/p integers. it also deal with duplicate keys
#include <conio.h>
#include <string>
using namespace std;
void InputArray(int*,int);
void QuickSort(int *,int,int);
int partition(int *,int,int);
void swap(int *,int,int);
void printArr(int *,int Siz=11);
void main(){
int siz;
cout<<"Enter Array length : "; cin>>siz;
int *a=new int[siz];
InputArray(a,siz);
QuickSort(a,0,siz-1);
int i=0,j=11;
printArr(a,siz);
system("pause");
}
void InputArray(int*a,int s){
for(int i=0; i<s; i++){
cout<<"ELement ["<<i<<"] = "; cin>>a[i];
}
}
void QuickSort(int *a,int start,int end){
if(start<end){
int pivot=partition(a,start,end);
QuickSort(a,start,pivot);
QuickSort(a,pivot+1,end);
}
}
int partition(int *a,int start,int end){
int currentPivotValue=a[start];
int i=start-1, j=end+1;
while(true){
i++;
while(i<j && a[i]<currentPivotValue){ i++; }
j--;
while(j>start && a[j]>currentPivotValue) {j--;}
if(i<j) swap(a,i,j);
else return j;
}
}
void swap(int *b,int i,int j){
int t=b[i];
b[i]=b[j];
b[j]=t;
}
void printArr(int *a,int Siz){
for(int i=0; i<Siz; i++) cout<<a[i]<<" ";
}