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.
Related
Suppose I have a sequence represented by an array of n numbers.
Is it possible to check if that sequence is monotonically increasing and then monotonically decreasing with n-1 comparisons?
I have tried to use this link How to determine if a sequence is bitonic?, but I feel it doesn't lead me to an answer to my problem.
To reduce the total number of compares, both between elements and testing if in the array range, code could temporarily alter the array.
bool up_down(int *x, size_t n) {
int right = x[n-1];
x[n-1] = INT_MIN;
int *up = x;
while (up[0] < up[1]) {
up++;
}
x[n-1] = right; // restore
int left = x[0];
x[0] = INT_MIN;
int *down = x + n - 1;
while (down[0] < down[-1]) {
down--;
}
x[0] = left; // restore
return up == down;
// or per EOF comment to avoid a last "compare"
return !(up - down);
}
Is it possible to check if that sequence is monotonically increasing and then monotonically decreasing with n-1 comparisons?
I do not think so if all compares are counted: The above code needs a few more (2?) than n-1 compares and does not do well when n < 3 n < 2, but is the basis for a reduced compare count.
Not sure if this is homework question, so here's a fancy/confusing pointer version. :-)
Does at most n-1 comparisons (p goes from &s[0] to s[n-1]) between the elements of the array -- O(n).
Does up to 2n+1 comparisons overall.
int is_incdec(int *s, int n) {
int *p = &s[0];
int *last = &s[n-1];
while (p != last && *p < *(p+1)) p++;
while (p != last && *p > *(p+1)) p++;
return p == last;
}
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
Recently I had an interview, where they asked me a "searching" question.
The question was:
Assume there is an array of (positive) integers, of which each element is either +1 or -1 compared to its adjacent elements.
Example:
array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
Now search for 7 and return its position.
I gave this answer:
Store the values in a temporary array, sort them, and then apply binary search.
If the element is found, return its position in the temporary array.
(If the number is occurring twice then return its first occurrence)
But, they didn't seem to be satisfied with this answer.
What is the right answer?
You can do a linear search with steps that are often greater than 1. The crucial observation is that if e.g. array[i] == 4 and 7 hasn't yet appeared then the next candidate for 7 is at index i+3. Use a while loop which repeatedly goes directly to the next viable candidate.
Here is an implementation, slightly generalized. It finds the first occurrence of k in the array (subject to the +=1 restriction) or -1 if it doesn't occur:
#include <stdio.h>
#include <stdlib.h>
int first_occurence(int k, int array[], int n);
int main(void){
int a[] = {4,3,2,3,2,3,4,5,4,5,6,7,8,7,8};
printf("7 first occurs at index %d\n",first_occurence(7,a,15));
printf("but 9 first \"occurs\" at index %d\n",first_occurence(9,a,15));
return 0;
}
int first_occurence(int k, int array[], int n){
int i = 0;
while(i < n){
if(array[i] == k) return i;
i += abs(k-array[i]);
}
return -1;
}
output:
7 first occurs at index 11
but 9 first "occurs" at index -1
Your approach is too complicated. You don't need to examine every array element. The first value is 4, so 7 is at least 7-4 elements away, and you can skip those.
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
int array[] = {4,5,6,5,4,3,2,3,4,5,6,7,8};
int len = sizeof array / sizeof array[0];
int i = 0;
int steps = 0;
while (i < len && array[i] != 7) {
i += abs(7 - array[i]);
steps++;
}
printf("Steps %d, index %d\n", steps, i);
return 0;
}
Program output:
Steps 4, index 11
Edit: improved after comments from #Martin Zabel.
A variation of the conventional linear search could be a good way to go. Let us pick an element say array[i] = 2. Now, array[i + 1] will either be 1 or 3 (odd), array[i + 2] will be (positive integers only) 2 or 4 (even number).
On continuing like this, a pattern is observable - array[i + 2*n] will hold even numbers and so all these indices can be ignored.
Also, we can see that
array[i + 3] = 1 or 3 or 5
array[i + 5] = 1 or 3 or 5 or 7
so, index i + 5 should be checked next and a while loop can be used to determine the next index to check, depending on the value found at index i + 5.
While, this has complexity O(n) (linear time in terms of asymptotic complexity), it is better than a normal linear search in practical terms as all the indices are not visited.
Obviously, all this will be reversed if array[i] (our starting point) was odd.
The approach presented by John Coleman is what the interviewer was hoping for, in all probability.
If you are willing to go quite a bit more complicated, you can increase expected skip length:
Call the target value k. Start with the first element's value v at position p and call the difference k-v dv with absolute value av. To speed negative searches, have a peek at the last element as the other value u at position o: if dv×du is negative, k is present (if any occurrence of k is acceptable, you may narrow down the index range here the way binary search does). If av+au is greater than the length of the array, k is absent. (If dv×du is zero, v or u equals k.)
Omitting index validity: Probe the ("next") position where the sequence might return to v with k in the middle: o = p + 2*av.
If dv×du is negative, find k (recursively?) from p+av to o-au;
if it is zero, u equals k at o.
If du equals dv and the value in the middle isn't k, or au exceeds av,
or you fail to find k from p+av to o-au,
let p=o; dv=du; av=au; and keep probing.
(For a full flash-back to '60ies texts, view with Courier. My "1st 2nd thought" was to use o = p + 2*av - 1, which precludes du equals dv.)
STEP 1
Start with the first element and check if it's 7. Let's say c is the index of the current position. So, initially, c = 0.
STEP 2
If it is 7, you found the index. It's c. If you've reached the end of the array, break out.
STEP 3
If it's not, then 7 must be atleast |array[c]-7| positions away because you can only add a unit per index. Therefore, Add |array[c]-7| to your current index, c, and go to STEP 2 again to check.
In the worst case, when there are alternate 1 and -1s, the time complexity may reach O(n), but average cases would be delivered quickly.
Here I am giving the implementation in java...
public static void main(String[] args)
{
int arr[]={4,5,6,5,4,3,2,3,4,5,6,7,8};
int pos=searchArray(arr,7);
if(pos==-1)
System.out.println("not found");
else
System.out.println("position="+pos);
}
public static int searchArray(int[] array,int value)
{
int i=0;
int strtValue=0;
int pos=-1;
while(i<array.length)
{
strtValue=array[i];
if(strtValue<value)
{
i+=value-strtValue;
}
else if (strtValue==value)
{
pos=i;
break;
}
else
{
i=i+(strtValue-value);
}
}
return pos;
}
Here is a divide-and-conquer style solution. At the expense of (much) more bookkeeping, we can skip more elements; rather than scanning left-to-right, test in the middle and skip in both directions.
#include <stdio.h>
#include <math.h>
int could_contain(int k, int left, int right, int width);
int find(int k, int array[], int lower, int upper);
int main(void){
int a[] = {4,3,2,3,2,3,4,5,4,5,6,7,8,7,8};
printf("7 first occurs at index %d\n",find(7,a,0,14));
printf("but 9 first \"occurs\" at index %d\n",find(9,a,0,14));
return 0;
}
int could_contain(int k, int left, int right, int width){
return (width >= 0) &&
(left <= k && k <= right) ||
(right <= k && k <= left) ||
(abs(k - left) + abs(k - right) < width);
}
int find(int k, int array[], int lower, int upper){
//printf("%d\t%d\n", lower, upper);
if( !could_contain(k, array[lower], array[upper], upper - lower )) return -1;
int mid = (upper + lower) / 2;
if(array[mid] == k) return mid;
lower = find(k, array, lower + abs(k - array[lower]), mid - abs(k - array[mid]));
if(lower >= 0 ) return lower;
upper = find(k, array, mid + abs(k - array[mid]), upper - abs(k - array[upper]));
if(upper >= 0 ) return upper;
return -1;
}
const findMeAnElementsFunkyArray = (arr, ele, i) => {
const elementAtCurrentIndex = arr[i];
const differenceBetweenEleAndEleAtIndex = Math.abs(
ele - elementAtCurrentIndex
);
const hop = i + differenceBetweenEleAndEleAtIndex;
if (i >= arr.length) {
return;
}
if (arr[i] === ele) {
return i;
}
const result = findMeAnElementsFunkyArray(arr, ele, hop);
return result;
};
const array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
const answer = findMeAnElementsFunkyArray(array, 7, 0);
console.log(answer);
Wanted to include a recursive solution to the problem. Enjoy
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;
}
I'm a computer science student (just started), I was working on writing from pseudocode a randomized pivot version of Quicksort. I've written and tested it, and it all works perfectly however...
The partition part looks a bit too complicated, as it feels I have missed something or overthought it. I can't understand if it's ok or if I made some avoidable mistakes.
So long story short: it works, but how to do better?
Thanks in advance for all the help
void partition(int a[],int start,int end)
{
srand (time(NULL));
int pivotpos = 3; //start + rand() % (end-start);
int i = start; // index 1
int j = end; // index 2
int flag = 1;
int pivot = a[pivotpos]; // sets the pivot's value
while(i<j && flag) // main loop
{
flag = 0;
while (a[i]<pivot)
{
i++;
}
while (a[j]>pivot)
{
j--;
}
if(a[i]>a[j]) // swap && sets new pivot, and restores the flag
{
swap(&a[i],&a[j]);
if(pivotpos == i)
pivotpos = j;
else if(pivotpos == j)
pivotpos = i;
flag++;
}
else if(a[i] == a[j]) // avoids getting suck on a mirror of values (fx pivot on pos 3 of : 1-0-0-1-1)
{
if(pivotpos == i)
j--;
else if(pivotpos == j)
i++;
else
{
i++;
j--;
}
flag++;
}
}
}
This is the pseudo code of partition() from Introduction to Algorithms , which is called Lomuto's Partitioning Algorithm, and there's a good explanation below it in the book.
PARTITION(A, p, r)
1 x ← A[r]
2 i ← p - 1
3 for j ← p to r - 1
4 do if A[j] ≤ x
5 then i ←i + 1
6 exchange A[i] ↔ A[j]
7 exchange A[i + 1] ↔ A[r]
8 return i +1
You can implement a randomized partition implementation easily based on the pseudo code above. As the comment pointed out, move the srand() out of the partition.
// srand(time(NULL));
int partition(int* arr, int start, int end)
{
int pivot_index = start + rand() % (end - start + 1);
int pivot = arr[pivot_index ];
swap(&arr[pivot_index ], &arr[end]); // swap random pivot to end.
pivot_index = end;
int i = start -1;
for(int j = start; j <= end - 1; j++)
{
if(arr[j] <= pivot)
{
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[pivot_index]); // place the pivot to right place
return i + 1;
}
And there is another partition method mentioned in the book, which is called Hoare's Partitioning Algorithm, the pseudo code is as below:
Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
repeat
j = j - 1
until A[j] <= x
repeat
i = i + 1
until A[i] >= x
if i < j
swap( A[i], A[j] )
else
return j
After the partition, every element in A[p...j] ≤ every element in A[j+1...r]. So the quicksort would be:
QUICKSORT (A, p, r)
if p < r then
q = Hoare-Partition(A, p, r)
QUICKSORT(A, p, q)
QUICKSORT(A, q+1, r)
There are multiple ways to partition for quicksort, the following being likely the simplest I can muster. Generally two schools of partitioning are used:
The Squeeze - collapses both ends of the sequence until a suitable swap pair is found, then swaps two elements into proper sides of the partition. Not trivial to implement, but can be more efficient (reduced swap count) than the alternative...
The Sweep - uses a single left to right (or right to left) sweep the values, swapping values to an incrementing pivot index that moves as the algorithm runs. Very simple to implement, as you'll see below.
I prefer the Sweep algorithm for people learning quicksort and partitioning only because it is so dead-simple to implement. Both can be implemented to perform in-place partitioning, as is the case in the implementation below. At no time except in swap() will you see a value stored in temp-storage.
Using a random pivot selection is only a small part of this. The following shows how to initialize the random number generator, and demonstrates likely the simplest partition algorithm and quicksort usage therein you're going to find.
It demonstrates, among other things, that in C/C++, you don't need both ends of a partition since simple pointer arithmetic can be used to adjust the "top" half of a partition. See the quicksort() function for how this is done.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int *lhs, int *rhs)
{
if (lhs == rhs)
return;
int tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
int partition(int ar[], int len)
{
int i, pvt=0;
// swap random slot selection to end.
// ar[len-1] will hold the pivot value.
swap(ar + (rand() % len), ar+(len-1));
for (i=0; i<len; ++i)
{
if (ar[i] < ar[len-1])
swap(ar + i, ar + pvt++);
}
// swap the pivot value into position
swap(ar+pvt, ar+(len-1));
return pvt;
}
void quicksort(int ar[], int len)
{
if (len < 2)
return;
int pvt = partition(ar, len);
quicksort(ar, pvt++); // note increment. skips pivot slot
quicksort(ar+pvt, len-pvt);
}
int main()
{
srand((unsigned int)time(NULL));
const int N = 20;
int data[N];
for (int i=0; i<N; ++i)
{
data[i] = rand() % 50 + 1;
printf("%d ", data[i]);
}
puts("");
quicksort(data, N);
for (int i=0; i<N; ++i)
printf("%d ", data[i]);
puts("");
return 0;
}
Output (varies, obviously)
32 49 42 49 5 18 41 48 22 33 40 27 12 47 41 6 50 27 8 7
5 6 7 8 12 18 22 27 27 32 33 40 41 41 42 47 48 49 49 50
Note: this does NOT account for modulo bias for using rand() % len, and frankly it would be overkill to do so for this example. If it were critical, I would use another generator entirely. An outstanding discussion for methods of choosing random pivot locations for quicksort partitioning can be found at this post on this site, including many links to different methods. I suggest reviewing it.