I have written parallel implementation of merge sort but for some reasons that I cannot understand sometimes it finishes properly, and sometimes it crashes. Whats more interesting following code sometimes seems to crash AFTER it has sorted the array (the array gets printed and then code stops working).
Could anyone help me diagnose a problem? I guess I messed something with threads and they are not ending properly?
void printArray (int* a, int elements) {
for(int i = 0; i < elements; i++) {
printf("%d\t", a[i]);
}
}
void merge2(int a[], int l, int middle, int r) {
int leftIterator = l;
int rightIterator = middle+1;
int k = 0;
int* tmp = new int[(r - l)];
while (leftIterator <= middle && rightIterator <= r) {
if (a[leftIterator] < a[rightIterator]) {
tmp[k] = a[leftIterator];
leftIterator++;
}
else {
tmp[k] = a[rightIterator];
rightIterator++;
}
k++;
}
if (leftIterator <= middle) {
while (leftIterator <= middle) {
tmp[k] = a[leftIterator];
leftIterator++;
k++;
}
}
else {
while (rightIterator <= r) {
tmp[k] = a[rightIterator];
rightIterator++;
k++;
}
}
for (int i = 0; i <= (r - l); i++)
a[l + i] = tmp[i];
}
void merge_sort(int a[], int l, int r) {
if (l != r) {
int middle= (l + r) / 2; //find middle element
#pragma omp parallel sections
{
#pragma omp section
merge_sort(a, l, middle);
#pragma omp section
merge_sort(a, middle + 1, r);
}
merge2(a, l, middle, r);
}
}
int main(void) {
int elements = 10;
int a[10] = { 10,2,1,5,6,3,4,9,8,7 };
merge_sort(a, 0, elements-1);
printArray(a, elements); // print sorted array
printf("\n");
return 0;
}
Related
I tried to implement merge sort using C++, however, something went wrong. I have no idea what is wrong.
The following code is what I wrote based on CLRS. I think it is quite easy to understand the meaning.
#include <iostream>
#include <vector>
using namespace std;
void merge(vector<int>& nums, int p, int q, int r);
void mergeSort(vector<int>& nums, int p, int r){
if (p < r) {
int q = (p + r) / 2;
mergeSort(nums, p, q);
mergeSort(nums, q + 1, r);
merge(nums, p, q, r);
}
}
void merge(vector<int>& nums, int p, int q, int r) {
int s1 = p, s2 = q + 1;
vector<int> l1, l2;
for (int i = s1; i <= q; i++) {
l1.push_back(nums[i]);
}
for (int i = s2; i <= r; i++) {
l2.push_back(nums[i]);
}
int left = 0, right = 0;
int idx = 0;
while (left < l1.size() && right < l2.size()) {
if (l1[left] < l2[right]) {
nums[idx] = l1[left++];
}
else {
nums[idx] = l2[right++];
}
idx++;
}
while (left < l1.size()) {
nums[idx++] = l1[left++];
}
while (right < l2.size()) {
nums[idx++] = l2[right++];
}
}
int main() {
vector<int> vect;
vect.push_back(1);
vect.push_back(3);
vect.push_back(12);
vect.push_back(23);
vect.push_back(4);
vect.push_back(11);
vect.push_back(44);
vect.push_back(322);
mergeSort(vect, 0, vect.size() - 1);
for (int i = 0; i < vect.size(); i++) {
cout << vect[i] << endl;
}
return 0;
}
I want to use the program to sort some integers, however, it only shows many duplicate numbers. What's going on? I don't think there is a problem of the merge function.
The code needs a one line fix:
int idx = p; // not idx = 0
Optimized top down using arrays from Wiki article (note bottom up is slightly faster):
void TopDownMerge(int A[], int bgn, int mid, int end, int B[])
{
int i, j, k;
i = bgn, j = mid, k = bgn;
while(1){
if(A[i] <= A[j]){ // if left smaller
B[k++] = A[i++]; // copy left element
if(i < mid) // if not end of left run
continue; // continue
do // else copy rest of right run
B[k++] = A[j++];
while(j < end);
break; // and break
} else { // else right smaller
B[k++] = A[j++]; // copy right element
if(j < end) // if not end of right run
continue; // continue
do // else copy rest of left run
B[k++] = A[i++];
while(i < mid);
break; // and break
}
}
}
void TopDownSplitMerge(int B[], int bgn, int end, int A[])
{
if (end - bgn <= 1) // if run size == 1
return; // consider it sorted
int mid = (end + bgn) / 2;
TopDownSplitMerge(A, bgn, mid, B);
TopDownSplitMerge(A, mid, end, B);
TopDownMerge(B, bgn, mid, end, A);
}
void TopDownMergeSort(int A[], int n) // n = size (not size-1)
{
if(n < 2)
return;
int *B = new int [n]; // 1 time allocate and copy
for(size_t i = 0; i < n; i++)
B[i] = A[i];
TopDownSplitMerge(B, 0, n, A); // sort data from B[] into A[]
delete B;
}
Afterwards, I finally get to fix the bugs of my program. After modification, here is the code:
class Solution {
public:
vector<int> temp;
vector<int> sortArray(vector<int>& nums) {
temp.resize((int)nums.size(), 0);
mergeSort(nums, 0, nums.size() - 1);
return nums;
}
void mergeSort(vector<int>& nums, int start, int end) {
if (start >= end) return;
int middle = (start + end) / 2;
mergeSort(nums, start, middle);
mergeSort(nums, middle + 1, end);
merge(nums, start, middle, end);
}
void merge(vector<int>& nums, int leftStart, int middle, int rightEnd) {
int leftEnd = middle;
int rightStart = middle + 1;
int i = leftStart, j = rightStart;
int index = 0;
while (i <= leftEnd && j <= rightEnd) {
if (nums[i] < nums[j]) {
temp[index] = nums[i++];
}
else {
temp[index] = nums[j++];
}
index++;
}
while (i <= leftEnd) {
temp[index++] = nums[i++];
}
while (j <= rightEnd) {
temp[index++] = nums[j++];
}
for (int i = 0; i < rightEnd - leftStart + 1; i++) {
nums[i + leftStart] = temp[i];
}
}
};
Here is something should be careful next time:
In the merge part, it is difficult to merge in place. It'd be better to use another temp array to store the merged results and update to the target array (nums in this case).
Readable identifers is very recommended (Although the pseudocode of CLRS may not use that part).
Need to use debuggers to find the bug of program {However, it takes like forever to load local variables of VS Code debugers.
Book page 1
Book page 2
The 4th question is my homework. I am asked to write 4 sort functions to compare the worst time they use. The length is n.
My code is as follows
#include<vector>
using namespace std;
void Swap(int &a,int &b)
{
int temp=a;
a=b;
b=temp;
}
void partition(int *a, int left, int right){
int mid = (left+right)/2;
int t1 = a[left], t2 = a[mid], t3 = a[right];
if((t1 <= t2 && t2 <= t3) || (t3 <= t2 && t2 <= t1))
{Swap(a[right], a[mid]);}
else if((t2 <= t1 && t1 <= t3) || (t3 <= t1 && t1 <=t2))
{Swap(a[right], a[left]);}
}
void QuickSort(int* a,int left,int right)
{
if(left<right)
{
int i;
i=left;
int j;
j=right+1;
partition(a,left,right);
int mid = (left+right)/2;
int t1 = a[left], t2 = a[mid], t3 = a[right];
if((t1 <= t2 && t2 <= t3) || (t3 <= t2 && t2 <= t1))
{Swap(a[right], a[mid]);}
else if((t2 <= t1 && t1 <= t3) || (t3 <= t1 && t1 <=t2))
{Swap(a[right], a[left]);}
int pivot=a[left];
do{
do i++;while(a[i]<pivot);
do j--;while(a[j]>pivot);
if(i<j) Swap(a[i],a[j]);
}while(i<j);
Swap(a[left],a[j]);
QuickSort(a,left,j-1);
QuickSort(a,j+1,right);
}
}
void InsertSort(int* arr, int n)
{
for (int i = 0; i < n; i++) {
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j-1]) {
Swap(arr[j], arr[j - 1]);
}
}
}
}
void Merge(int *initList,int* mergedList,int l,int m,int n)
{
int i1=l,iResult=l,i2=m+1;
for(;i1<=m&&i2<=n;iResult++)
{
if(initList[i1]<=initList[i2])
{
mergedList[iResult]=initList[i1];
i1++;
}
else
{
mergedList[iResult]=initList[i2];
i2++;
}
}
for(int temp=iResult;i1<m+1;i1++,temp++)
{
mergedList[temp]=initList[i1];
}
for(int temp=iResult;i2<n+1;i2++,temp++)
{
mergedList[temp]=initList[i2];
}
}
void MergePass(int *initList,int *resultList,int n,int s)
{
int i=1;
for(;i<=n-2*s+1;i+=2*s)
{
Merge(initList,resultList,i,i+s-1,i+2*s-1);
}
if((i+s-1)<n)Merge(initList,resultList,i,i+s-1,n);
else
for(int temp=i;temp<n+1;temp++)
{
resultList[temp]=initList[temp];
}
}
void MergeSort(int* a,int n)
{
int *tempList=new int[n+1];
for(int i=1;i<n;i*=2)
{
MergePass(a,tempList,n,i);
i*=2;
MergePass(tempList,a,n,i);
}
for(int i=0;i<n+1;i++)
{
a[i]=tempList[i];
}
delete[]tempList;
}
void adjustHeap(int* arr, int i, int length) {
int temp = arr[i];//先取出当前元素i
for (int k = i * 2 + 1; k <=length; k = k * 2 + 1) {
if (k + 1 <=length && arr[k] < arr[k + 1]) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;
}
void adjust_heap(int* a, int father, int len)
{
int left = 2 * father;
int right = 2 * father + 1;
int max = father;
if( left <= len && a[left] > a[max])
max = left;
if( right <= len && a[right] > a[max])
max = right;
if(max != father)
{
Swap( a[max], a[father]);
adjust_heap(a, max, len);
}
}
void HeapSort(int* a, int len)
{
for(int i = len / 2; i >= 1; --i)
adjust_heap(a, i, len);
for(int i = len; i >= 1; --i)
{
Swap(a[1], a[i]);
adjust_heap(a, 1, i - 1);
}
for(int i=0;i<len;i++)
a[i]=a[i+1];
}
void heapAdjust(int*nums, int idx,int len){
int temp=nums[idx];
for(int j=idx*2+1;j<len;j=j*2+1){
if(j+1<len&&nums[j]<nums[j+1]) {j++;}
if(temp>nums[j]) {break;}
nums[idx]=nums[j];
idx =j;
}
nums[idx]=temp;
}
void heapSort(int* nums, int n,int k){
for(int i=n/2-1;i>=0;i--)
{
heapAdjust(nums, i,n);
}
for(int i=n-1;i>0;i--)
{
if(k) {k--;}
else {break;}
swap(nums[0],nums[i]);
heapAdjust(nums,0,i);
}
}
void MaxMerge(int arr[],int l,int r)//transform the array so it is the most complex for merge sorting
{
if(r-l>1)
{
int *temp=new int[r-l+3];
temp[0]=0;
for(int i=0,j=1;i<=r-l;i++)
{
if(i%2==0){
temp[j+(r-l+1)/2]=arr[i+l];
}
else{
temp[j]=arr[i+l];
j++;
}
}
for(int i=l,j=1;i<=r;)
{
arr[i]=temp[j];
i++;
j++;
}
delete[]temp;
MaxMerge(arr,l+(r-l)/2,r);
MaxMerge(arr,l,l+(r-l)/2-1);
}
else
{
int t=arr[l];
arr[l]=arr[r];
arr[r]=t;
}
}
void Permute(int *a,int n)
{
srand(time(0));
for(int i=n;i>=2;i--)
{
int j=rand()%i+1;
swap(a[j],a[i]);
}
}
void TimeTest(int n)
{
if(n!=0){
int *QuickSortTest=new int[n+1];
int *InsertSortTest=new int[n+1];
int *HeapSortTest=new int[n+1];
int *MergeSortTest=new int[n+1];
for(int j=0;j<n+1;j++)
{
QuickSortTest[j]=j;
InsertSortTest[j]=n-j;
MergeSortTest[j]=j;
HeapSortTest[j]=j;
}
MaxMerge(MergeSortTest,1,n);
Permute(HeapSortTest,n);
clock_t begin1=clock();
for(int j=0;j<500;j++)
{
QuickSort(QuickSortTest,1,n);
}
clock_t end1=clock();
clock_t begin2=clock();
for(int j=0;j<500;j++)
{
heapSort(HeapSortTest,n,n-1);
}
clock_t end2=clock();
clock_t begin3=clock();
for(int j=0;j<500;j++)
{
InsertSort(QuickSortTest,n);
}
clock_t end3=clock();
clock_t begin4=clock();
for(int j=0;j<500;j++)
{
MergeSort(QuickSortTest,n);
}
clock_t end4=clock();
cout<<"n="<<n<<" QuickSort: "<<end1-begin1<<endl;
cout<<"n="<<n<<" HeapSort: "<<end2-begin2<<endl;
cout<<"n="<<n<<" InsertSort: "<<end3-begin3<<endl;
cout<<"n="<<n<<" MergeSort: "<<end4-begin4<<endl;
delete[]QuickSortTest;
delete[]InsertSortTest;
delete[]HeapSortTest;
delete[]MergeSortTest;
}
}
int main()
{
//int num1[]={500,1000,2000,4000,3000};
for(int i=0;i<=4000;i+=100)
{TimeTest(i);cout<<endl;}
}
I have already test for every function, and they are all right.
However,the result is that
I am confused that why merge sorting is always the quickest?
Both your algorithms and tests need some tender loving care.
For example I replace your InsertSort with less naive version of it
void InsertSort(int* arr, int n)
{
int i, key, j;
for (i = 1; i < n; i++)
{
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key)
{
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
After that InsertSort suddenly starts to wash floors with others in your tests. That is so because the test arrays are already sorted in most of the runs and it is rather good with such.
Among your original algorithms MergeSort was best with sorted input and so it won.
The quicksort function works perfectly fine as ive tried it with the standard array. When using vectors however, I get an error message saying swap function doesnt take 3 arguments. Any help would be appreciated.
void quicksort(vector<int> &vec, int L, int R) {
int i, j, mid, piv;
i = L;
j = R;
mid = L + (R - L) / 2;
piv = vec[mid];
while (i<R || j>L) {
while (vec[i] < piv)
i++;
while (vec[j] > piv)
j--;
if (i <= j) {
swap(vec, i, j); //error=swap function doesnt take 3 arguments
i++;
j--;
}
else {
if (i < R)
quicksort(vec, i, R);
if (j > L)
quicksort(vec, L, j);
return;
}
}
}
void swap(vector<int> v, int x, int y) {
int temp = v[x];
v[x] = v[y];
v[y] = temp;
}
int main() {
vector<int> vec1;
const int count = 10;
for (int i = 0; i < count; i++) {
vec1.push_back(1 + rand() % 100);
}
quicksort(vec1, 0, count - 1);
}
See
void quicksort(vector<int> &vec, int L, int R)
and
void swap(vector<int> v, int x, int y)
The first parameter does not use reference.
Like various comment say, problem is that your version of swap is being confused with std::swap. You can fix it by either moving the implementation of swap before you use it or add a declaration before you use it.
Also per Devin's answer, pass by reference so you get the swapped values back.
Here is the fixed code:
#include <vector>
using namespace std;
void swap(vector<int>& v, int x, int y);
void quicksort(vector<int> &vec, int L, int R) {
int i, j, mid, piv;
i = L;
j = R;
mid = L + (R - L) / 2;
piv = vec[mid];
while (i<R || j>L) {
while (vec[i] < piv)
i++;
while (vec[j] > piv)
j--;
if (i <= j) {
swap(vec, i, j); //error=swap function doesnt take 3 arguments
i++;
j--;
}
else {
if (i < R)
quicksort(vec, i, R);
if (j > L)
quicksort(vec, L, j);
return;
}
}
}
void swap(vector<int>& v, int x, int y) {
int temp = v[x];
v[x] = v[y];
v[y] = temp;
}
int main() {
vector<int> vec1;
const int count = 10;
for (int i = 0; i < count; i++) {
vec1.push_back(1 + rand() % 100);
}
quicksort(vec1, 0, count - 1);
}
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 getting two errors in implementing the algorithm from pseudocode:
One of my problems is int L[n1+1]; error: needs to be a constant; cannot allocate constant size 0. The only way to run this is to make the size a number like 10. I may be implementing the psuedocode wrong that is why I included the statement above that. This may be the cause of my next problem.
My other problem is I am printing only one line of code unsorted. My print function is flawless and works for all of the sorting programs. I believe the MERGE function is only running once. I posted the output of the Sort at the bottom.
I have a random number generator for the array A, from 0 to RAND_MAX.
Initial call is MERGESORT(A,1,n);
void MERGE(int *A, int p, int q, int r)
{
int n1 = q-(p+1);
int n2 = r-q;
//psuedocode states, let L[1..n1+1] & R[1..n1+1] be new arrays
int L[n1+1];
int R[n2+1];
for(int i=1; i<n1;i++)
{
L[i]=A[p+(i-1)];
}
for(int j=1; j<n2; j++)
{
R[j] = A[q+j];
}
L[n1+1]=NULL; //sentinel
R[n2+1]=NULL; //sentinel
int i=1;
int j=1;
for (int k=p; k<r; k++)
{
if(L[i]<=R[j])
{
A[k]=L[i];
i=i+1;
}
else
{
A[k]=R[j];
j=j+1;
}
}
}
void MERGESORT(int *A,int p, int r)
{
if (p<r)
{
int q=floor((p+r)/2);
MERGESORT(A,p,q);
MERGESORT(A,q+1,r);
MERGE(A,p,q,r);
}
}
With int L[10]; and my A[10]; my output is:
Sort: 7474 28268 32506 13774 14411
Press any key to continue . . .
If someone could just assist in the two problems, I more than likely will get it to work.
You are failing to detect the end of your merge arrays:
for (int k=p; k<r; k++)
{
// You need to check that i/j are still in range.
// otherwise the following test are not valid.
if ((i < n1) && (j < n2))
{
if(L[i]<=R[j])
{
A[k]=L[i];
i=i+1;
}
else
{
A[k]=R[j];
j=j+1;
}
}
else
{ /* More work here */
}
Other comments:
Identifiers that are all capitol MERGE MERGESORT are generally reserved for macros. If you use them you are likely to hit problems. Prefer function names of mixed case.
You can simulate arrays with vector:
// Simulate L[1..n1+1]
minI = 1;
maxI = n1-1;
std::vector<int> const L(A+(minI-1), A+(maxI-1));
Arrays in C++ are zero indexed. You seem to be having off by one errors (especially in accessing the end of the array). I would advice you to start the count at 0 rather than 1. Most C++ code is written in terms of iterators from [begining..1PastEnd). I think you will find your algorithm easier to implement if you adapt that style.
There are several issues with your code, I've pointed them out in comments. This is a solution closest to your code, and it's far from best. Consider using C++ containers, like std::vector for example. Naming is at least disputable, and of course merge sort should be implemented as an in place algorithm.
//L and R are auxiliary arrays
//preallocated with (inputSize/2 + 1) constant size
void MERGE(int *A, int p, int q, int r, int* L, int* R)
{
if (p > q || q > r)
{
return;
}
int n1 = q - p + 1;
int n2 = r - q;
// note 0-based indices
int i = 0;
int j = 0;
for(;i < n1;i++)
{
L[i] = A[p + i];
}
for(;j < n2;j++)
{
R[j] = A[q + j + 1]; //+1 because p + n1 - 1 == q + 0
}
//again - note 0-based indices
i = 0;
j = 0;
for (int k = p; k <= r; ++k)
{
// The most important fix - in your code you didn't check
// for left/right array bounds at all.
// Sentinel values aren't needed - size is known
if(i < n1 && (j >= n2 || L[i] <= R[j]))
{
A[k] = L[i];
++i;
}
else if (j < n2)
{
A[k] = R[j];
++j;
}
}
}
void MERGESORT(int* A, int p, int r, int* L, int* R)
{
if (p < r)
{
int q = (p + r) / 2; //floor was redundant
MERGESORT(A, p, q, L, R);
MERGESORT(A, q+1, r, L, R);
MERGE(A, p, q, r, L, R);
}
}
void MERGESORT(int* A, int size)
{
int*L = new int[size/2 + 1]; //preallocate auxiliary arrays
int*R = new int[size/2 + 1]; //size/2 + 1 is what will be needed at most
MERGESORT(A, 0, size - 1, L, R);
delete L;
delete R;
}
int main()
{
int A[5]{ 7474, 28268, 32506, 13774, 14411 };
MERGESORT(A, 5);
for (int i = 0;i < 5;++i)
{
std::cout << A[i] << std::endl;
}
return 0;
}
Output:
7474
13774
14411
28268
32506
Credit goes also to DyP for spotting all the mistakes in the previous version :)