Issue with Merge Sort implementation in C++ - c++

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 :)

Related

What is wrong with my merge sort implementation refer to CLRS?

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.

MergeSort: Is my problem from incorrect indeces or instance variables with recursion

I'm currently reading CLRS while also trying to learn C++. As you can imagine everything is going great! I've been stuck on understanding MergeSort but over the last couple days have made good progress and feel like my code is almost there.
P.S. Java is my strong suit so if I mix up the vocab between languages I apologize!
I imagine the problem is stemming from one of the few issues below:
1)Because I'm still new to C++ I've read that during instances it's important to use the class variables in the way I have below in order to apply the size of the array throughout your code. With that being said, I think during my recursion the fact that my r variable is a read-only constant; it is making my code go through an extra iteration of the recursion which is leading to problems.-My solution to this was to just set that variable equal to a new one and have it adapt to the array as needed. This still lead to wrong solutions! 2)I've read numerous times to not use Arrays in C++ especially in situations such as this and to use vectors instead. Given that I haven't learned about vectors quite yet, I felt more comfortable using Arrays and believe an issue may be from incorrect coding of pointers and references in my program.3)My final assumption also comes from reading a lot of answers to similar questions which is to not use instance variables in recursive methods. I'm pretty sure this wasn't a problem in Java and felt like this may just be a big no-no in C++ specifically.
I've seen countless classes of MergeSort and am very confident that my mergeSort & merge methods are perfect which makes me believe that this problem is probably just a lack of knowledge to C++ syntax or rules! Any comments or help would be greatly appreciated! Lastly 2 results of arrays I got quite often were [2, 4, 4, 5, 4, 4, 5, 1] & [2, 4, 5, 4, 5, 7, 1]
#include <iostream>
#include <cmath>
using namespace std;
class MergeSort
{
static const size_t r = 8;
int A[r];
public:
MergeSort() : A{2, 4, 5, 7, 1, 2, 3, 6}
{
printMergeSort(A, r);
int p = 0;
mergeSort(A, p, r);
printMergeSort(A, r);
}
static void mergeSort(int *A, int p, int r)
{
if(p < r)
{
//int q = floor(((p + r) / 2));
int q = ((p + r) / 2);
mergeSort(A, p, q);
mergeSort(A, q + 1, r);
merge(A, p, q, r);
}
}
static void merge(int *A, int q, int p, int r)
{
int n1 = q - p + 1;
int n2 = r - q;
int L[n1+1];
int R[n2+1];
for(int i = 0; i < n1; i++)
{
L[i] = A[p + i];
}
for(int j = 0; j < n2; j++)
{
R[j] = A[q + j + 1];
}
//L[n1 + 1] = std::numeric_limits<int>::max();
L[n1] = INT_MAX;
R[n2] = INT_MAX;
int i = 0;
int j = 0;
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;
}
}
}
static void printMergeSort(int *A, size_t r)
{
for(int i = 0; i < r; i++)
{
cout << A[i] << " ";
}
cout << endl;
}
};
int main()
{
//MergeSort();
MergeSort x;
return 0;
}
You are on right track, the best approach to learning practice algorithms are implementing them in a programming language.
Regarding your question, i think the issue is in your merging function.
In merging function what you want to do is to move through both smaller arrays and pick the smallest value and store it in the temporary register.
For example if you have
{2, 7}
{4, 6}
you want to pick "2" first and increment the pointer pointing to beginning of first array and then pick "4" and "6", and finally "7".
I posted the corrected version of your code here with minor variable naming changes.
Here are also some notes:
constructor is not a good place to call your functions. I modified your code in a way that the merge sort and printing is called in main function.
To find mid, you do not need to use floor. When you divide two integer numbers the result will be an integer as well. i.e: (3+4)/2 = 3
#include
#include
using namespace std;
const int INT_MAX = 2147483647;
class MergeSort
{
static const size_t r = 8;
int A[r];
public:
MergeSort() : A{2, 4, 5, 7, 1, 2, 3, 6}
{
//printMergeSort(A, r);
int p = 0;
//mergeSort(A, p, r);
//printMergeSort(A, r);
}
void mergeSort () {
mergeSortUtil (A, 0, r-1);
}
static void mergeSortUtil(int *A, int p, int r)
{
if(p < r)
{
int mid = (p + (r-p) / 2);
mergeSortUtil(A, p, mid);
mergeSortUtil(A, mid + 1, r);
merge(A, p, mid, r);
}
}
static void merge(int *A, int low, int mid, int high)
{
int left=low;
int right = mid+1;
int i=low;
int res[high-low+1];
while (left<=mid && right <=high) {
if(A[left] < A[right]) {
res[i] = A[left];
left++;
} else {
res[i]=A[right];
right++;
}
++i;
}
while (left<=mid) {
res[i] = A[left];
left++;
i++;
}
while (right <=high) {
res[i]=A[right];
right++;
i++;
}
for (int i=low; i<=high; ++i) {
A[i]=res[i];
}
}
void printMergeSort()
{
for(int i = 0; i < r; i++)
{
cout << A[i] << " ";
}
cout << endl;
}
};
int main() {
//MergeSort();
MergeSort * x = new MergeSort();
x->printMergeSort();
x->mergeSort();
x->printMergeSort();
return 0;
}
One immediate problem is calling merge(A, p, q, r), while merge takes its arguments as (int *A, int q, int p, int r). See how p and q are mixed up?
Another problem, less obvious, is the semantics of r. An initial invocation passes 8, which implies that r is one-past the end-of-range, and does not belong to the range. However,
mergeSort(A, p, q);
mergeSort(A, q + 1, r);
imply that q in the first invocation does belong. You should make up your mind. Usually passing one-past leads to simpler and cleaner code. Consider
mergeSort(A, p, q);
mergeSort(A, q, r);
and
int n1 = q - p;
int n2 = r - q;
int L[n1];
int R[n2];
(no need to strange INT_MAX assignments anymore).
Of course, the main merge loop is wrong. This was addressed in the answer above. However, to emphasize the one-past argument, consider
while ((i < n1) && (j < n2)) {
if(L[i] <= R[j])
{
A[k++] = L[i++];
} else {
A[k++] = R[j++];
}
}
while (i < n1) {
A[k++] = L[i++];
}
while (j < n2) {
A[k++] = R[j++];
}

Juggling algorithm regarding Right Array rotation

Recently I learnt about the array rotation in linear time using Juggling algorithm. Here is the snippet regarding the left rotation of the array.
void ArrayRotate (int A[], int n, int k)
{
int d=-1,i,temp,j;
for(i=0;i<gcd(n,k);i++)
{
j=i;
temp=A[i];
while(1)
{
d=(j+k)%n;
if(d==i)
break;
A[j]=A[d];
j=d;
}
A[j]=temp;
}
}
but now I am stuck as how to use this Juggling algorithm to rotate the array in the Right Direction.
1,2,3,4,5 (given array)
5,1,2,3,4 (after 1 right rotation)
(I had solved this question using the brute force method and reversal method.)
As already mentioned, you should use std::rotate if you are allowed to.
Your implementation has a bug. Here is a fixed implementation.
void ArrayRotate(int A[], int n, int k) {
int d = -1, i, temp, j;
int g = gcd(n, k);
for (i = 0; i < g; ++i) {
j = i;
temp = A[i];
while (true) {
d = (j + k) % n;
if (d == i) {
break;
}
A[j] = A[d];
j = d;
}
A[j] = temp;
}
}
Also note that I took out gcd calculation out of loop condition. It does not technically affect complexity, but it's enough to compute the gcd only once.
To rotate the array k times to the right, just rotate it n - k times to the left.
void ArrayRotateRight(int A[], int n, int k) {
ArrayRotate(A, n, n - k);
}
Or change the 8th line to be d = (j - k + n) % n;
Not sure if you're doing this as an intellectual exercise, or for production code, but for production code use the STL rotate algorithm:
#include<iostream>
#include<algorithm>
using namespace std;
void display(int* a, int length)
{
for (int i = 0; i < length; ++i)
cout << a[i] << " ";
cout << endl;
}
int main()
{
const int len = 5;
int arr[] = { 1,2,3,4,5 };
display(arr, len);
rotate(arr, arr + 1, arr + len); // arr + x means left by x
display(arr, len);
rotate(arr, arr + len - 1, arr + len); // arr + len - x means right by x
display(arr, len);
}

Quick Sort program stopped working

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.

What is wrong in this implementation of merge sort?

I know that there are many implementations of merge sort but this is one which I have read in the book "Introduction to algorithms". The following code is an implementation of merge sort which is not working correctly:
#include <iostream>
using namespace std;
void merge(int*a, int p, int q, int r) { //function to merge two arrays
int n1 = (q - p); // size of first sub array
int n2 = (r - q); // size of second subarray
int c[n1], d[n2];
for (int i = 0; i <= n1; i++) {
c[i] = a[p + i];
}
for (int j = 0; j <= n2; j++) {
d[j] = a[q + j];
}
int i = 0, j = 0;
for (int k = p; k < r; k++) { // merging two arrays in ascending order
if (c[i] <= d[j]) {
a[k++] = c[i++];
} else {
a[k++] = d[j++];
}
}
}
void merge_sort(int*a, int s, int e) {
if (s < e) {
int mid = (s + e) / 2;
merge_sort(a, s, mid);
merge_sort(a, mid + 1, e);
merge(a, s, mid, e);
}
}
int main() {
int a[7] { 10, 2, 6, 8, 9, 10, 15 };
merge_sort(a, 0, 6);
for (auto i : a)
cout << i << endl;
}
This code is not working correctly. What's wrong in this code? How can it be fixed?
First of all you should be correctly set for the size of the array.
void merge(int*a, int p, int q, int r) { //function to merge two arrays
/* If i am not wrong , p is the starting index of the first sub array
q is the ending index of it also q+1 is the starting index of second
sub array and r is the end of it */
/* size of the sub array would be (q-p+1) think about it*/
int n1 = (q - p); // size of first sub array
/* This is right n2 = (r-(q+1)+1)*/
int n2 = (r - q); // size of second subarray
int c[n1], d[n2];
for (int i = 0; i < n1; i++) {
c[i] = a[p + i];
}
for (int j = 0; j < n2; j++) {
d[j] = a[q + 1 + j];
}
.
.
.
}
Now , after this you have copies the both arrays in locally defined arrays. Until this, it is correct .
Now the main part is merging of the two arrays which you are doing in the for loop. You are just comparing the ith element of first sub array with jth element of the second, but what you are missing here is that there may be a time when you have updated all the values of the first( or second) sub array in the main array but still some elements are remaining int the second ( first) one.
For example, take these two subarrays
sub1={2,3,4,5};
sub2={7,8,9,10};
in this case you should break from the loop as soon as you have traversed either of the array completely and copy the rest of the elements of the other array in the same order.
Also in the for loop you increasing k two times in a loop , one in the for statement and another while updating a value, Check that too.
Hope this may solve the problem.
There are couple of things gone wrong in the implementation of your logic. I have indicated them clearly below:
void merge(int*a,int p,int q,int r){ //function to merge two arrays
int n1= (q-p); // size of first sub array
int n2= (r-q); // size of second subarray
int c[n1+1],d[n2]; //you need to add 1 otherwise you will lose out elements
for(int i=0;i<=n1;i++){
c[i]=a[p+i];
}
for(int j=0;j<n2;j++){
d[j]=a[q+j+1];//This is to ensure that the second array starts after the mid element
}
int i=0,j=0;
int k;
for( k=p;k<=r;k++){ // merging two arrays in ascending order
if( i<=n1 && j<n2 ){//you need to check the bounds else may get unexpected results
if( c[i] <= d[j] )
a[k] = c[i++];
else
a[k] = d[j++];
}else if( i<=n1 ){
a[k] = c[i++];
}else{
a[k] = d[j++];
}
}
}