Code below merges 2 sorted arrays A and B of size n1 and n2 respectively.
Merged output needs to be stored in A.
(No need to go through entire code)
Doubt: While re-allocating A, I am getting a run-time error. Why?
int* temp = (int*)realloc(A,sizeof(int)*(n1+n2));
if(temp != NULL) A = temp;
Code for reference:
void putinend(int* num,int m,int n){
int i,j;
for(i=m-1,j=m+n-1;i>=0;i--)
num[j--] = num[i];
}
void merge(int* A, int n1, int* B, int n2) {
int* temp = (int*)realloc(A,sizeof(int)*(n1+n2));
if(temp != NULL) A = temp;
putinend(A,n1,n2);
int s1=n2,s2=0,i=0;
while(s1 < n1+n2 && s2 < n2){
if(A[s1] <= B[s2])
A[i++] = A[s1++];
else
A[i++] = B[s2++];
}
while(s1 < n1+n2)
A[i++] = A[s1++];
while(s2 < n2)
A[i++] = B[s2++];
printf("\n");
for(i=0;i<10;i++){
printf("%d ",A[i]);
}
}
int main() {
int *A = (int*)malloc(sizeof(int)*8);
int *B = (int*)malloc(sizeof(int)*2);
A[0]=1; A[1]=3; A[2] = 5; A[3] = 7; A[4] = 9; A[5] = 11; A[6] = 13; A[7] = 15;
B[0]=-2; B[1]=2;
int i;
merge(A,8,B,2);
printf("\n");
for(i=0;i<10;i++){
printf("%d ",A[i]);
}
return 0;
}
Edit:
I incorporated corrections given below.
But Output returned is
-2 1 2 3 5 7 9 11 13 15
0 3 5 7 9 11 13 15 0 17
Why does A change just before returning from merge() and just after returning from merge() in main()?
You call realloc() on an array allocated on the stack. The *alloc() functions work with the heap, though.
From man realloc:
Unless ptr is NULL, it must have been returned by an earlier call to
malloc(), calloc() or realloc().
Replace
int A[8];
with something like
int* A = malloc(8 * sizeof(int));
Don't forget to call free() if you need to.
here is the algorithm for a merge sort:
Notice is looks nothing like what the posted code is implementing.
Note: this is a recursive method of implementing a merge sort algorithm
--Merge Sort--
Merge sort is based on Divide and conquer method. It takes the list to be sorted and divide it
in half to create two unsorted lists. The two unsorted lists are then sorted and merged to get a
sorted list. The two unsorted lists are sorted by continually calling the merge-sort algorithm; we
eventually get a list of size 1 which is already sorted. The two lists of size 1 are then merged.
Algorithm:
This is a divide and conquer algorithm. This works as follows –
Divide the input which we have to sort into two parts in the middle. Call it the left part
and right part.
Example: Say the input is -10 32 45 -78 91 1 0 -16 then the left part will be -10 32 45 -
78 and the right part will be 91 1 0 6.
Sort each of them separately. Note that here sort does not mean to sort it using some other
method. We use the same function recursively.
Then merge the two sorted parts.
Input the total number of elements that are there in an array (number_of_elements). Input the
array (array[number_of_elements]). Then call the function MergeSort() to sort the input array.
MergeSort() function sorts the array in the range [left,right] i.e. from index left to index right
inclusive. Merge() function merges the two sorted parts. Sorted parts will be from [left, mid] and
[mid+1, right]. After merging output the sorted array.
MergeSort() function:
It takes the array, left-most and right-most index of the array to be sorted as arguments.
Middle index (mid) of the array is calculated as (left + right)/2. Check if (left
have to sort only when left
by calling MergeSort() function again over the left part MergeSort(array,left,mid) and the right
part by recursive call of MergeSort function as MergeSort(array,mid + 1, right). Lastly merge the
two arrays using the Merge function.
Merge() function:
It takes the array, left-most , middle and right-most index of the array to be merged as
arguments. A temporary array (tempArray[right-left+1]) is required to store the new sorted part.
The current index position (pos) of the temporary array is initialized to 0. The left index position
(lpos) is initialized to left and right index position (rpos) is initialized to mid+1, of the array.
Until lpos < mid and rpos < right
if(array[lpos] < array[rpos]), i.e value of array at position lpos is less than value of
array at position rpos, then store array[lpos] (value at the left index of array) at current
index position (pos) of temporary array and increments the position index (pos) and left
position index (lpos) by 1. tempArray[pos++] = array[lpos++]
Else, store array[rpos] (value at the right index of array) at current index position (pos) of
temporary array and increments the position index (pos) and right position index (rpos)
by 1. tempArray[pos++] = array[rpos++]
Until (lpos <= mid) i.e. elements in the left part of the array are left
tempArray[pos++] = array[lpos++],store array[lpos] (value at the left index of array) at
current index position (pos) of temporary array and increments the position index (pos)
and left position index (lpos) by 1.
Until (rpos <= right) i.e. elements in the right part of the array are left
tempArray[pos++] = array[rpos++],store array[rpos] (value at the right index of array) at
current index position (pos) of temporary array and increments the position index (pos)
and right position index (rpos) by 1.
Finally copy back the sorted array to the original array.
Property:
Best case – When the array is already sorted O(nlogn).
Worst case – When the array is sorted in reverse order O(nlogn).
Average case – O(nlogn).
Extra space is required, so space complexity is O(n) for arrays and O(logn) for linked
lists.
here is an example code, from http://www.thelearningpoint.net/computer-science/arrays-and-sorting-merge-sort--with-c-program-source-code
#include<stdio.h>
/*This is called Forward declaration of function */
void Merge(int * , int , int , int );
/* Logic: This is divide and conquer algorithm. This works as follows.
(1) Divide the input which we have to sort into two parts in the middle. Call it the left part
and right part.
Example: Say the input is -10 32 45 -78 91 1 0 -16 then the left part will be
-10 32 45 -78 and the right part will be 91 1 0 6.
(2) Sort Each of them seperately. Note that here sort does not mean to sort it using some other
method. We already wrote fucntion to sort it. Use the same.
(3) Then merge the two sorted parts.
*/
/*This function Sorts the array in the range [left,right].That is from index left to index right inclusive
*/
void MergeSort(int *array, int left, int right)
{
int mid = (left+right)/2;
/* We have to sort only when left<right because when left=right it is anyhow sorted*/
if(left<right)
{
/* Sort the left part */
MergeSort(array,left,mid);
/* Sort the right part */
MergeSort(array,mid+1,right);
/* Merge the two sorted parts */
Merge(array,left,mid,right);
}
}
/* Merge functions merges the two sorted parts. Sorted parts will be from [left, mid] and [mid+1, right].
*/
void Merge(int *array, int left, int mid, int right)
{
/*We need a Temporary array to store the new sorted part*/
int tempArray[right-left+1];
int pos=0,lpos = left,rpos = mid + 1;
while(lpos <= mid && rpos <= right)
{
if(array[lpos] < array[rpos])
{
tempArray[pos++] = array[lpos++];
}
else
{
tempArray[pos++] = array[rpos++];
}
}
while(lpos <= mid) tempArray[pos++] = array[lpos++];
while(rpos <= right)tempArray[pos++] = array[rpos++];
int iter;
/* Copy back the sorted array to the original array */
for(iter = 0;iter < pos; iter++)
{
array[iter+left] = tempArray[iter];
}
return;
}
int main()
{
int number_of_elements;
scanf("%d",&number_of_elements);
int array[number_of_elements];
int iter;
for(iter = 0;iter < number_of_elements;iter++)
{
scanf("%d",&array[iter]);
}
/* Calling this functions sorts the array */
MergeSort(array,0,number_of_elements-1);
for(iter = 0;iter < number_of_elements;iter++)
{
printf("%d ",array[iter]);
}
printf("\n");
return 0;
}
Related
I got a struct pair array whcih consist of timestamp and its occurrence. I want to sort it by the occurrence first, then if they have the same occurrence, sort it by the timestamp.(Both are sort in descending order)
sort by occurrence (completed):
1667473200 6
1658390400 6
1672596000 6
1677412800 6
1647604800 5
What I want:
1677412800 6
1672596000 6
1667473200 6
1658390400 6
1647604800 5
My heap sort function(sort by occurrence):
void heapify(struct pair *arr, int n, int i)
{
// Find largest among root, left child and right child
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && arr[left].occurrence > arr[largest].occurrence)
largest = left;
if (right < n && arr[right].occurrence > arr[largest].occurrence)
largest = right;
// Swap and continue heapifying if root is not largest
if (largest != i)
{
swapint(&arr[i].occurrence, &arr[largest].occurrence);
swaplong(&arr[i].timestamp, &arr[largest].timestamp);
heapify(arr, n, largest);
}
}
void heapSort(struct pair *arr, int n)
{
// Build max heap
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// Heap sort
for (int i = n - 1; i >= 0; i--)
{
swapint(&arr[0].occurrence, &arr[i].occurrence);
swaplong(&arr[0].timestamp, &arr[i].timestamp);
// Heapify root element to get highest element at root again
heapify(arr, i, 0);
}
}
How should I modify my heap sort function?
I think you should use a stable sorting algorithm. By applying the algorithm a first time (example Merge sort) you sort the array of struct by occurrences, then apply it a second time by sorting the array by timestamp.
Heap sort non-stability:
Why-isnt-heapsort-stable
Merge-sort explanation:
Merge-sort
I have to write a function in C which takes as input an array and the dimension of the array.
We assume that the array has the following characteristics:
Every element in the array is different
the first elements of the array are odd and the remaining are even
There are at least one odd element and one even element in the array
The function has to return the first index of the even elements using a divide and conquer approach and the cost of the algorithm should be O(log(n)).
In a normal case, I would use a function like this:
int foo(int v[], int n){
for(int i=0; i<n; i++){
if(v[i]%2==0)
return i;
}
}
But I have no idea how to solve this problem with the divide and conquer approach.
Is it possible to solve the problem using a modified version of the mergesort o quicksort algorithm?
Think at this:
your input is (1,3,5,7,......,2,4,6,8) and its length is n.
Your output will surely not be 0 (you know it is odd) but probably it would not either be the last.
The most important concept behind divide et impera is that is simpler to conquer something which is smaller. So divide you array in two parts and look just at one side, beeing sure that the other part will not contain your result.
Let's suppose that our array (from now on called "a") have indexes from 0 to n-1 (a[n-1] = 8). Let's check at the middle, so first of all we need a index.
int mid = (0 + n-1)/2
what is a[mid]?
is it odd? then we have to look at the right side, from mid+1 to n-1
is it even? we have two possibilities:
is mid-1 a valid index and is a[mid-1] odd? then a[mid] is the first even element and mid is the result
else look at the left side from 0 to mid-1
then just do it recursively :)
I'm not too used to C so I will write pseudo code
int exercise(int[] a, int n) {
return exerciseRecursive(a, 0, n-1);
}
int exerciseRecursive(int[] a, int start, int end) {
if (start>end) {
return -1; //there is no even element
}
int mid = (start + end)/2;
if (a[mid]%2==1) { //odd
return exerciseRecursive(a,mid+1,end);
}
else {
if (mid-1>=0 && a[mid-1]%2==1) { //the current element is even and the previous is odd
return mid;
}
else {
return exerciseRecursive(a,start,mid-1);
}
}
}
You can use a modified binary search to find the index where the even elements start.
At each step, we search either the left or right half of the remaining elements:
int foo(int v[], int n){
int l = 0;
int h = n-1;
while (l < h) {
int m = (l + h) / 2; // `l + h` may overflow, but ignoring that for simplicity...
if (v[m] % 2 != 0) {
l = m + 1; // Search in the left half if `v[m]` is odd.
// Note that the `+ 1` is important to prevent an infinite loop.
} else {
h = m; // Search in the right half if `v[m]` is even.
}
}
return l;
}
I am confused on why code only sorts 5 elements but not 10 elements. This method was described to me during my office visit with my professor. So I followed his instructions when I coded this. Can someone please help me fix this problem? My code works but it doesn't work on a bigger array. I know it is too late for someone to help before its due but I just want to understand what I had done in this code.
Thank You
/* Homework3.c
Qucksort arrays
Jared DaRocha
3/1/2020
*/
#include <stdio.h>// preprocessor
int partition(int arr[], int first, int end);// function declaration
void quickSort(int arr[], int first, int end);// function declaration
int main(void)// beginning of main
{
//Data
int array[5];// creates an array of 5 elements
int size = sizeof(array) / sizeof(array[0]); // calculates size of the array
// prompt user to input data to input int the array
printf("Enter 5 numbers to add in the array");
// prompts user to input 10 numbers
for(int i =0;i<size; i++)
{
scanf("%d", &array[i]);// stores user-defined data into the array
}
// call the function to sort the array
quickSort(array, 0, size -1);
// prints array
for (int i = 0; i < size; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
int partition(int arr[], int low, int high)// Function declaration
{
//Data
int partition = arr[low]; // partition number
// infinite for loop
for (;;)
{
// decrement high while partition is smaller than element of high
while(partition <arr[high])
{
high--;
}
// if partition is greater than element of high, swap high with low element since low element is the pivot
if(partition > arr[high]){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
low++;
}
// increment low while partition is greater than element of low
while(partition > arr[low])
{
low++;
}
// swap low and next element in the right if partition is less than element of low
if(partition < arr[low])
{
int temp = arr[low];
arr[low] = arr[low+1];
arr[low+1] = temp;
}
// if low is same as high then exit loop
if(high == low)
{
break;
}
}
return low;
}
void quickSort(int arr[], int first, int end) // function definition
{
if (first < end)
{
int partitionIndex = partition(arr, first, end);
quickSort(arr, first, (partitionIndex - 1));
quickSort(arr, (partitionIndex+1 ), end);
}
}
`
If you check the algorithm quicksort, you'll find how the array is divided into two slices that don't overlap, but also don't leave a single element hung around. You do this, just between both slices, you leave a single element. You divide the array in two slices and one single array element that doesn't participate in the recursive calls to the other slices.... and that's incorrect. What you know is that all elements of the inferior slice are less than the token, and the rest are greater than or equal than the token... but the point of division can be any element of the array subpart that corresponds.
By the way, the code or partition is completely incorrect. You first select one element that will act as a token, you can select it as any element of the array or as the average value of all of them, it doesn't matter... you are not going to put it in the array. then search from the end elements that are under the array, and search from the beginning elements that are over the token, once you have one such pair, switch them, but you exchange the found elements with the token, which not only doesn't do what is expected, but introduces copies of the token (and change its value) thing that will trash your array.
You are not searching for an array element... you are searching for a place (between to adjacent elements) where to divide your array, and the subarrays will be from beg, to pos-1, and from pos to end, (or from beg to pos, then from pos + 1 to end) but don't ignore the central element, because you know it belongs to an array, but it's not necessary the token.
Below is the partition() logic used by qSort(),
static void qSort(List *list, int low, int high, compareTo compare){
if(high <= low){
return; // no partition for sub array of size 1
}
int pivotIndex = partition(list, low, high, compare);
qSort(list, low, pivotIndex-1, compare);
qSort(list, pivotIndex+1, high, compare);
}
static int partition(List *list, int low, int high, compareTo compare){
int pivot = low;
int leftIndex = low + 1;
int rightIndex = high;
const void **array = list->array;
while(true){
while( leftIndex < high && (compare(array[leftIndex], array[pivot]) < 0) ){
leftIndex++;
}
while( rightIndex > pivot && (compare(array[rightIndex], array[pivot]) > 0) ){
rightIndex--;
}
if(leftIndex >= rightIndex){
break; // partition is done
}
if( compare(array[leftIndex], array[rightIndex]) == 0 ){
leftIndex++; rightIndex--;
continue; //Maintain stability
}
arraySwap(list, leftIndex, rightIndex);
}
if( compare(array[pivot], array[rightIndex]) != 0 ){
arraySwap(list, pivot, rightIndex); // Maintain stability
}
return rightIndex;
}
where arraySwap() && compare() is defined as,
void arraySwap(List *list, int i, int j){
const void **array = list->array;
const void *tempPointer = array[i];
array[i] = array[j];
array[j] = tempPointer;
}
int compare(const void *key, const void *item){
if( ((Person *)key)->age < ((Person *)item)->age ){
return -1;
}else if( ((Person *)key)->age > ((Person *)item)->age ){
return 1;
}else{
return 0;
}
}
partition() has to maintain stability by having extra checks before each arraySwap().
But below output shows that, stability is partially maintained(key 10 is stable unlike key 50),
$ ./sort.exe
Before sorting
Age,LastName,FirstName
50 B A
30 A B
20 X D
10 F A
50 A B
90 V E
60 N M
10 A B
After sorting
Age,LastName,FirstName
10 F A
10 A B
20 X D
30 A B
50 A B
50 B A
60 N M
90 V E
In the partition() function,
below code chunk is to just maintain stability,
while(true){
....
if( compare(array[leftIndex], array[rightIndex]) == 0 ){
leftIndex++; rightIndex--;
continue; //Maintain stability
}
....
}
...
if( compare(array[pivot], array[rightIndex]) != 0 ){
...
}
Question:
Why record with key 50 is not stable?
Quick-sort is unstable because the partition step may swap elements that compare equal to each other, and thus put them in a different order than in the original array.
Making quick-sort stable requires a comparison function that will always return non zero for different elements.
Avoiding swapping equal elements is not by any means sufficient to achieve a stable quicksort. Consider, for example, this simple case:
key value
2 A
3 B
3 C
1 D
1 E
Taking the first element as the pivot, the first partitioning involves three swaps: (1, 4) and (2, 3) in the main part of the partitioning, then (0, 2) to put the pivot in place. That yields:
1 D
1 E
2 A
3 C
3 B
No elements having equal keys were swapped, but the relative order of the two items with key 3 was reversed. This happens naturally, at both ends of the partition, as a result of the upper half of the array being traversed in reverse order.
Additionally, you have an opportunity for instability when you swap the pivot element into place. Since it starts at the far left of the array, if there are any other elements with the same key that end up in the left partition, but the element at the extreme right end of the left partition has a different key, then the partition element will be moved relative to others having the same key.
To be sure of a stable sort, the comparison function must take the original element order into account. That would usually require using O(N) additional metadata. It would be fair to interpret this as saying that quicksort cannot be made stable at all, since incorporating the original element order into the comparison function effectively makes all elements unequal, and the question of stability therefore moot.
I've got such an algorithmic problem: I need to make Quicksort to work like this:
1) Indexes of array which are odd numbers should be sorted from smallest to largest
2) Even indexes should be sorted from largest to smallest.
So if we've got array: 2 5 1 3 4 0 6 2 5,
we should get sth like: 6 0 5 2 4 3 2 5 1
Here is my implementation of quicksort in C:
void quicksort(int tab[], int start, int end) {
int i=start;
int j=end;
int x=tab[(i+j)/2];
do {
while(tab[i]<x) i++;
while(tab[j]>x) j--;
if(i<=j) {
int tmp=tab[i];
tab[i]=tab[j];
tab[j]=tmp;
i++;
j--;
}
} while(i<=j);
if(start<j) quicksort(tab,start,j);
if(i<end) quicksort(tab,i,end);
}
Is it possible to make it using just one quicksort or I should try sth like creating two functions: one will sort odd indexes and second one even indexes?
Is it possible to make it using just one quicksort or I should try sth like creating two functions: one will sort odd indexes and second one even indexes?
quick sort is generally used to sort elements in ascending or descending order so I don't think it'd be useful to just sort the elements in required pattern ( which is neither ascending nor descending and even no particular pattern is guaranteed in the answer array ) using only quick sort.
In my opinion creating an additional custom function say required_sort() and sort elements as required along with the help of qucksort() (here in my case it sorts in ascending order) would be the best way to go
void required_sort(int array[], int size_of_array)
{
int no_of_even_elements, no_of_odd_elements
if(size_of_array%2 == 0)
{
no_of_even_elements = no_of_odd_elements = n/2;
}
else
{
no_of_even_elements = (n/2)+1;
no_of_odd_elements = n/2;
}
int even[no_of_even_elements], odd_even[elements];
//inserting elements into new arrays
for(int index=0; index < size_of_array; index++)
{
if(index%2 == 0)
{
even[index/2] = array[index];
}
else
{
odd[index/2] = array[index];
}
}
//call quicksort function to sort the even[] array in ascending order
//call quicksort function to sort the odd[] array in ascending order
for(int index=0; index < size_of_array; index++)
{
if(index%2 == 0)
{
array[index] = even[(no_of_even_elements)-(index/2)];
}
else
{
array[index] = odd[index/2];
}
}
}
Explanation of required_sort :
first check whether size_of_array is even or odd
if size_of_array is even then there are equal number of elements at odd indices and even indices. so
no_of_even_elements = no_of_odd_elements = n/2
if size_of_array is odd then there are equal number of elements at odd indices and even indices. so
no_of_even_elements = (n/2)+1
no_of_odd_elements = n/2
create two more arrays. say odd[no_of_odd_elements] and even[no_of_even_elements]
in first array store the elements at odd indices and in the second the elements at even indices.
use quicksort() (in ascending order) to sort both the arrays
now using a for loop to update the values of original array[] this way :
for(int index=0; index < size_of_array; index++)
{
if(index%2 == 0)
{
array[index] = even[(no_of_even_elements)-(index/2)];
}
else
{
array[index] = odd[index/2];
}
}
hope this helps :)
You can parameterize your quicksort algorithm in order to support (1) partial sorting based on a step size and (2) the sorting direction.
void quicksort2(int tab[], int start, int end, int step, int (*comparer)(int, int))
introducing a parameter step that is used to access elements that are step elements away from start and end
Whenever an index is changed, use step size instead of 1: i+=step;, j-=step; and so on.
Computing the middle element for pivot becomes slightly more complex in order to support uneven indices with step size > 1: int mid = (end / step - start / step) / 2 * step + start; int x=tab[mid];
The start and end index are required to be a multiple of step apart.
changing the comparison to a comparer function instead of native < and > operator usage
The comparer function is expected to return negative values for a < b and positive values for b < a. Usage: while(comparer(tab[i],x) < 0) // ...
Putting it all together:
void quicksort(int tab[], int start, int end, int step, int (*comparer)(int, int))
{
int i=start;
int j=end;
int mid = (end / step - start / step) / 2 * step + start;
int x=tab[mid];
do {
while(comparer(tab[i],x) < 0) i+=step;
while(comparer(tab[j],x) > 0) j-=step;
if(i<=j) {
int tmp=tab[i];
tab[i]=tab[j];
tab[j]=tmp;
i+=step;
j-=step;
}
} while(i<=j);
if(start<j) quicksort(tab,start,j, step, comparer);
if(i<end) quicksort(tab,i,end, step, comparer);
}
I tried to stick close to your initial native quicksort implementation, so this code should look pretty familiar.
This can be used to execute the desired sorting as follows:
Define comparer functions for ascending and descending sortings.
int smaller(int a, int b)
{
return a - b;
}
int bigger(int a, int b)
{
return b - a;
}
And call quicksort twice for the two sub-sortings
int values[] = { 2, 5, 1, 3, 4, 0, 6, 2, 5 };
quicksort(values, 0, 8, 2, &smaller);
quicksort(values, 1, 7, 2, &bigger);
Be careful to get the start and end indices right or add a sanity check for them inside the quicksort function