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
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;
}
Let us suppose we have two arrays A[] and B[]. Each array contains n distinct integers which are not sorted. We need to find kth ranked element in the union of the 2 arrays in the most efficient way possible.
(Please dont post answers about merging the arrays and then sorting them to return kth index in the merged array)
You can use the selection algorithm to find the Kth item, in O(N) time, where N is the sum of the sizes of the arrays. Obviously, you treat the two arrays as a single large array.
Union of arrays can be done in linear time. I am skipping that part.
You can use the partition() algorithm which is used in the quick sort. In quick sort, the function will have to recurse two branches. However here we will just conditionally invoke the recursive call and thus only 1-branched recursion.
Main concept: partition() will place the chosen PIVOT element at its appropriate sorted position. Hence we can use this property to select that half of the array in which we are interested and just recurse on that half. This will prevent us from sorting the entire array.
I have written the below code based on the above concept. Assumption rank = 0 implies the smallest element in the array.
void swap (int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int partition (int a[], int start, int end)
{
/* choose a fixed pivot for now */
int pivot = a[end];
int i = start, j;
for (j = start; j <= end-1; j++) {
if (a[j] < pivot) {
swap (&a[i], &a[j]);
i++;
}
}
/* Now swap the ith element with the pivot */
swap (&a[i], &a[end]);
return i;
}
int find_k_rank (int a[], int start, int end, int k)
{
int x = partition (a, start, end);
if (x == k) {
return a[x];
} else if (k < x) {
return find_k_rank (a, start, x-1, k);
} else {
return find_k_rank (a, x+1, end, k);
}
}
int main()
{
int a[] = {10,2,7,4,8,3,1,5,9,6};
int N = 10;
int rank = 3;
printf ("%d\n", find_k_rank (a, 0, N-1, rank));
}
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;
}
Given two unsorted int arrays, find the kth element in the merged, sorted array.
example:
int[] a= [3 1 7]
int[] b = [4 9]
k: 3
return 4 (non-Zero based index)
Please do not provide the straight forward solution where merge two array and sort and find the Nth element. I am looking for more efficient solution i.e using Heap, QuickSort. and make sure arrays are unsorted.
P.S: I know there are many similar questions of above posted in SO but i couldn't find any correct way of implementation in any of them.
As it stands your example is wrong.
Assuming its int[] a= [3 1 7] int[] b = [4 9] k: 3 return 7.
Loop through both arrays and push to a min-heap. After your done, pop k times from the heap and you will have the kth (largest) element from the merged array.
PriorityQueue<Integer> minHeap=new PriorityQueue();
for (Integer i : a)
{
minHeap.add(i);
}
for (Integer i : b)
{
minHeap.add(i);
}
int count=1;// 1 based index
while(!minHeap.isEmpty())
{
Integer head=minHeap.poll();
count++;
if(count==k)
return head;
}
Code above is untested. But it should roughly look like the above.
The general term for this problem is Selection Algorithm, which Wikipedia has a great article on.
Finding the kth element of two unsorted arrays is equivalent to finding the kth element of one--just pretend you've concatenated the two. A simple algorithm from the linked page is Quickselect, which is essentially Quicksort but only recursing into the half that contains the kth element. Essentially,
Use the first element as the pivot
Iterate over the array, assigning items <= to the pivot to one array, and > to another, until an array has grown large enough that it must contain the desired element
Recurse on that array, offsetting k if necessary
C# code using quick select algorithm:
private void MergeUnsortedArray(int[] A1, int[] A2)
{
int[] c = new int[A1.Length + A2.Length];
int length = 0;
for (int i = 0; i < A1.Length; i++)
{
c[i] = A2[i];
length++;
}
for (int j = 0; j < A2.Length; j++)
{
c[length + j + 1] = A2[j];
}
quickselect(c, 0, c.Length, 3);
}
private int quickselect(int[] G, int first, int last, int k)
{
if (first <= last)
{
int pivot = partition(G, first, last);
if (pivot == k)
{
return G[k];
}
if (pivot < k)
{
return quickselect(G, first, pivot - 1, k);
}
return quickselect(G, pivot + 1, last, k);
}
return 0;
}
private int partition(int[] G, int first, int last)
{
int pivot = (first + last) / 2;
swap(G, last, pivot);
for (int i = first; i < last; i++)
{
if (G[i] < G[last])
{
swap(G, i, first);
first++;
}
}
swap(G, first, last);
return first;
}
private void swap(int[] G, int x, int y)
{
int tmp = G[x];
G[x] = G[y];
G[y] = tmp;
}