C randomized pivot quicksort (improving the partition function) - c

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.

Related

inplace quicksort with random pivot C implementation

I need to implement a quicksort algorithm that uses random pivot; I'm working with big matrices, so i can't afford the worst case.
Now, I've found this implementation that works correctly, but it uses as pivot the first element.
I've modified it to fit my scenario (I'm working with Sparse Matrices, and I need to sort the elements by "row index, col index") and this is what I have:
void quicksortSparseMatrix(struct sparsematrix *matrix,int first,int last){
int i, j, pivot, temp_I, temp_J;
double temp_val;
if(first<last){
pivot=first; //(rand() % (last - first + 1)) + first;
i=first;
j=last;
while(i<j){
while(lessEqual(matrix,i, pivot)&&i<last)
i++;
while(greater(matrix,j, pivot))
j--;
if(i<j){
temp_I = matrix->I[i];
temp_J = matrix->J[i];
temp_val = matrix->val[i];
matrix->I[i] = matrix->I[j];
matrix->J[i] = matrix->J[j];
matrix->val[i] = matrix->val[j];
matrix->I[j]=temp_I;
matrix->J[j]=temp_J;
matrix->val[j]=temp_val;
}
}
temp_I = matrix->I[pivot];
temp_J = matrix->J[pivot];
temp_val = matrix->val[pivot];
matrix->I[pivot] = matrix->I[j];
matrix->J[pivot] = matrix->J[j];
matrix->val[pivot] = matrix->val[j];
matrix->I[j]=temp_I;
matrix->J[j]=temp_J;
matrix->val[j]=temp_val;
quicksortSparseMatrix(matrix,first,j-1);
quicksortSparseMatrix(matrix,j+1,last);
}
}
Now, the problem is that some of the matrices i'm working with are almost sorted and the algorithm runs extremely slow. I want to modify my algorithm to make it use random pivot, but if I apply the change you see commented in the code above pivot=(rand() % (last - first + 1)) + first;, the algorithm does not sort the data correctly.
Can anyone help me figure out how to change the algorithm to use a random pivot and sort the data correctly?
EDIT: this is the struct sparsematrix definition, I don't think you need it, but for completeness...
struct sparsematrix {
int M, N, nz;
int *I, *J;
double *val;
};
Pivot should be a value, not an index. The first comparison should be lessthan (not lessthanorequal), which will also eliminate the need for checking for i < last . After swapping, there should be i++ and j-- . The last two lines should be quicksortSparseMatrix(matrix,first,j); and quicksortSparseMatrix(matrix,i,last); , for this variation of Hoare partition scheme. Example code for array:
void QuickSort(int *a, int lo, int hi)
{
int i, j;
int p, t;
if(lo >= hi)
return;
p = a[lo + 1 + (rand() % (hi - lo))];
i = lo;
j = hi;
while (i <= j){
while (a[i] < p)i++;
while (a[j] > p)j--;
if (i > j)
break;
t = a[i];
a[i] = a[j];
a[j] = t;
i++;
j--;
}
QuickSort(a, lo, j);
QuickSort(a, i, hi);
}
A merge sort on an array of indexes to rows of matrix may be faster: more moves of the indexes, but fewer compares of rows of matrix. A second temp array of indexes will be needed for merge sort.

multithreading and merge sort, 3 threads for sort and merging

this question been asked a few times but cannot find anything specific, the topic is I'm trying to perform a merge sort using 2 threads for sorting each sub list, each thread taking a half, and a third thread to merge the two lists afterwards,
but I'm having difficulty, understanding the merge sort, (first time trying to write a sort algorithm in
c)
i know i need to lets say take "apart" the merge sort and perform only each half in two separate functions, that I can pass into the thread, then perform the final merging process, but lets say I have lists
list1 11 22 1 6 9
list2 99 33 4 5 7
I know I could write a simple sort algorithm to put each in order, but the merging process is throwing me for a loop soheres what im thinking or visualizing the error, and what i am lost on, say i sort the two lists with their different thread functions
list1 1 6 9 11 22
list2 4 5 7 33 99
I keep thinking this will be the result if I go ahead and merge them at this stage
1 6 9 11 22 4 5 7 33 99
that isn't exactly in order is it?
and if I sort them in the final merger using a sort, I mean what was the point of sorting them initially? that kind of just makes the point of sorting them in the two subthreads pointess, when my goal is to use the 2 threads and 2 threads only, then use the final thread to merge like a merge sort would sort each array
can anyone help me visualize this better, either with code, or pseudocode?, assume that in my code I already have the array split and ready to be passed into the functions in my main(void) function, with pointers named first_half, second_half, the starter array is constructed, by taking a user input and passing that into a malloc function, and filled using random limited to 0-99
The way merge sort is working is by comparing the sorted element of each list together, it does not concatenate one array after another, see example code below (singlethreaded):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define _DEBUG 1
void insertion_sort(int n, int a[n])
{
int i, tmp, j;
for (i = 1; i < n; i++) {
tmp = a[i];
j = i - 1;
while (j >= 0 && a[j] > tmp) {
a[j + 1] = a[j];
--j;
}
a[j + 1] = tmp;
}
#ifdef _DEBUG
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
putchar('\n');
#endif
}
void merge_sort(int n, int a[n], int x, int b[x], int y, int c[y])
{
for (int i = 0; i < x; i++) // split values of a into two arrays
b[i] = a[i];
for (int i = 0; i < y; i++)
c[i] = a[i+x];
insertion_sort(x, b); // sort each of the sub arrays, you multithread this part.
insertion_sort(y, c);
int i = 0;
int j = 0;
int k = 0;
while (i < n) {
if (b[j] < c[k]) // you add to a the smallest between b and c.
a[i++] = b[j++];
else
a[i++] = c[k++];
if (x == j) { // if all the elements of b have filled a:
do {
a[i++] = c[k++]; // we proceed to fill a with c
} while (y != k);
break ; // the merge sorting is completed.
}
else if (y == k) {
do {
a[i++] = b[j++];
} while (x != j);
break ;
}
}
#ifdef _DEBUG
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
putchar('\n');
#endif
}
int main(void) {
srand ( time(NULL) );
const int n = 100;
int a[n];
int b[n/2];
int c[(n+1)/2];
for (int i = 0; i < n; i++) // initialize with random values
a[i] = rand() % 1000;
merge_sort(n, a, n/2, b, (n+1)/2, c);
}

Can quicksort be implemented in C without stack and recursion?

I found this post How to do iterative quicksort without using stack in c?
but the answer suggested does use a inline stack array! (Only constant amount of extra space is permitted)
The code in the page in reference makes a bold claim:
STACK My implementation does not use the stack to store data...
Yet the function definition has many variables with automatic storage, among them 2 arrays with 1000 entries, which will end up using a fixed but substantial amount of stack space:
// quickSort
//
// This public-domain C implementation by Darel Rex Finley.
//
// * Returns YES if sort was successful, or NO if the nested
// pivots went too deep, in which case your array will have
// been re-ordered, but probably not sorted correctly.
//
// * This function assumes it is called with valid parameters.
//
// * Example calls:
// quickSort(&myArray[0],5); // sorts elements 0, 1, 2, 3, and 4
// quickSort(&myArray[3],5); // sorts elements 3, 4, 5, 6, and 7
bool quickSort(int *arr, int elements) {
#define MAX_LEVELS 1000
int piv, beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R ;
beg[0]=0; end[0]=elements;
while (i>=0) {
L=beg[i]; R=end[i]-1;
if (L<R) {
piv=arr[L]; if (i==MAX_LEVELS-1) return NO;
while (L<R) {
while (arr[R]>=piv && L<R) R--; if (L<R) arr[L++]=arr[R];
while (arr[L]<=piv && L<R) L++; if (L<R) arr[R--]=arr[L]; }
arr[L]=piv; beg[i+1]=L+1; end[i+1]=end[i]; end[i++]=L; }
else {
i--; }}
return YES; }
The indentation style is very confusing. Here is a reformatted version:
#define MAX_LEVELS 1000
bool quickSort(int *arr, int elements) {
int piv, beg[MAX_LEVELS], end[MAX_LEVELS], i = 0, L, R;
beg[0] = 0;
end[0] = elements;
while (i >= 0) {
L = beg[i];
R = end[i] - 1;
if (L < R) {
piv = arr[L];
if (i == MAX_LEVELS - 1)
return NO;
while (L < R) {
while (arr[R] >= piv && L < R)
R--;
if (L < R)
arr[L++] = arr[R];
while (arr[L] <= piv && L < R)
L++;
if (L < R)
arr[R--] = arr[L];
}
arr[L] = piv;
beg[i + 1] = L + 1;
end[i + 1] = end[i];
end[i++] = L;
} else {
i--;
}
}
return YES;
}
Note that 1000 is large but not sufficient for pathological cases on moderately large arrays that are already sorted. The function returns NO on such arrays with a size of 1000 only, which is unacceptable.
A much lower value would suffice with an improved version of the algorithm where the larger range is pushed into the array and the loop iterates on the smaller range. This ensures that an array of N entries can handle a set of 2N entries. It still has quadratic time complexity on sorted arrays but at least would sort arrays of all possible sizes.
Here is a modified and instrumented version:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_LEVELS 64
int quickSort(int *arr, size_t elements) {
size_t beg[MAX_LEVELS], end[MAX_LEVELS], L, R;
int i = 0;
beg[0] = 0;
end[0] = elements;
while (i >= 0) {
L = beg[i];
R = end[i];
if (L + 1 < R--) {
int piv = arr[L];
if (i == MAX_LEVELS - 1)
return -1;
while (L < R) {
while (arr[R] >= piv && L < R)
R--;
if (L < R)
arr[L++] = arr[R];
while (arr[L] <= piv && L < R)
L++;
if (L < R)
arr[R--] = arr[L];
}
arr[L] = piv;
if (L - beg[i] > end[i] - R) {
beg[i + 1] = L + 1;
end[i + 1] = end[i];
end[i++] = L;
} else {
beg[i + 1] = beg[i];
end[i + 1] = L;
beg[i++] = L + 1;
}
} else {
i--;
}
}
return 0;
}
int testsort(int *a, size_t size, const char *desc) {
clock_t t = clock();
size_t i;
if (quickSort(a, size)) {
printf("%s: quickSort failure\n", desc);
return 1;
}
for (i = 1; i < size; i++) {
if (a[i - 1] > a[i]) {
printf("%s: sorting error: a[%zu]=%d > a[%zu]=%d\n",
desc, i - 1, a[i - 1], i, a[i]);
return 2;
}
}
t = clock() - t;
printf("%s: %zu elements sorted in %.3fms\n",
desc, size, t * 1000.0 / CLOCKS_PER_SEC);
return 0;
}
int main(int argc, char *argv[]) {
size_t i, size = argc > 1 ? strtoull(argv[1], NULL, 0) : 1000;
int *a = malloc(sizeof(*a) * size);
if (a != NULL) {
for (i = 0; i < size; i++)
a[i] = rand();
testsort(a, size, "random");
for (i = 0; i < size; i++)
a[i] = i;
testsort(a, size, "sorted");
for (i = 0; i < size; i++)
a[i] = size - i;
testsort(a, size, "reverse sorted");
for (i = 0; i < size; i++)
a[i] = 0;
testsort(a, size, "constant");
free(a);
}
return 0;
}
Output:
random: 100000 elements sorted in 7.379ms
sorted: 100000 elements sorted in 2799.752ms
reverse sorted: 100000 elements sorted in 2768.844ms
constant: 100000 elements sorted in 2786.612ms
Here is a slighlty modified version more resistant to pathological cases:
#define MAX_LEVELS 48
int quickSort(int *arr, size_t elements) {
size_t beg[MAX_LEVELS], end[MAX_LEVELS], L, R;
int i = 0;
beg[0] = 0;
end[0] = elements;
while (i >= 0) {
L = beg[i];
R = end[i];
if (R - L > 1) {
size_t M = L + ((R - L) >> 1);
int piv = arr[M];
arr[M] = arr[L];
if (i == MAX_LEVELS - 1)
return -1;
R--;
while (L < R) {
while (arr[R] >= piv && L < R)
R--;
if (L < R)
arr[L++] = arr[R];
while (arr[L] <= piv && L < R)
L++;
if (L < R)
arr[R--] = arr[L];
}
arr[L] = piv;
M = L + 1;
while (L > beg[i] && arr[L - 1] == piv)
L--;
while (M < end[i] && arr[M] == piv)
M++;
if (L - beg[i] > end[i] - M) {
beg[i + 1] = M;
end[i + 1] = end[i];
end[i++] = L;
} else {
beg[i + 1] = beg[i];
end[i + 1] = L;
beg[i++] = M;
}
} else {
i--;
}
}
return 0;
}
Output:
random: 10000000 elements sorted in 963.973ms
sorted: 10000000 elements sorted in 167.621ms
reverse sorted: 10000000 elements sorted in 167.375ms
constant: 10000000 elements sorted in 9.335ms
As a conclusion:
yes quick sort can be implemented without recursion,
no it cannot be implemented without any local automatic storage,
yes only a constant amount of extra space is necessary, but only because we live is a small world where the maximum size of the array is bounded by available memory. A size of 64 for the local objects handles arrays larger than the size of the Internet, much larger than current 64-bit systems could address.
Apparently, it is possible to implement a non-recursive quicksort with only constant amount of extra space as stated here. This builds upon the Sedgewick's work for non-recursive formulation of quicksort. Instead of preserving the boundary values(low and high) it essentially performs a linear scan to determine these bounds.
Can quicksort be implemented in C without stack and recursion?
Quicksort requires two paths be followed forward from each non-trivial partitioning: a new partitioning of each (sub)partition. Information about the previous partitioning (the bounds of one of the resulting partitions) needs to be carried forward to each new partitioning. The question, then, is where does that information live? In particular, where does the information about one partition live while the program is working on the other?
For a serial algorithm, the answer is that the information is stored on a stack or a queue or a functional equivalent of one of those. Always, because those are our names for data structures that serve the needed purpose. In particular, recursion is a special case, not an alternative. In a recursive quicksort, the data are stored on the call stack. For an iterative implementation you can implement a stack in a formal sense, but it's possible to instead use a simple and relatively small array as a makeshift stack.
But stack and queue equivalents can go a lot farther than that. You could append data to a file, for example, for later read-back. You could write it to a pipe. You could transmit it to yourself asynchronously over a communications network.
If you're clever, you can even make the input array itself serve the need for a stack, by encoding the partition bounds using relative element order or some other element property, as described by Ďurian, for example. This involves a space vs speed tradeoff that is probably not a good deal in most cases. However, it has lower space overhead (O(1)) than do typical quicksort implementations (O(log N)), and it does not change the algorithm's O(N log N) asymptotic time complexity.
If you wanted to go crazy, you could even nest iterations in place of recursing. That would impose a hard upper bound on the size of the arrays that could be handled, but not as tight of one as you might think. With some care and a few tricks, you could handle billion-element arrays with a 25-loop nest. Such a deep nest would be ugly and crazy, but nevertheless conceivable. A human could write it by hand. And in that case, the series of nested loop scopes, with their block-scoped variables, serves as a stack equivalent.
So the answer depends on what exactly you mean by "without stack":
yes, you can use a queue instead, though it would need to have about the same capacity as there are elements to sort;
yes, you can use an array or some other kind of sequential data storage, including the input array itself, to emulate a formal stack or queue;
yes, you can encode a suitable stack equivalent directly into the structure of your program;
yes, you can probably come up with other, more esoteric versions of stacks and queues;
but no, you cannot perform a quicksort without something filling the multi-level data-storage role for which a stack or stack-equivalent is conventionally used.
Well, it can, because I implemented a quicksort in fortran IV (it was a long time ago, and before the language supported recursion - and it was for a bet). However you do need somewhere (a large array would do) to remember your state as you do individual bits of work.
It's a lot easier recursively...
Quicksort is by definition a "divide and conquer" searching algorithm, the idea is that you split the given array into smaller partitions. So you are dividing the problem into subproblems, that is easier to solve.
When using Quicksort without recursion you need a struct of some sort to store the partitions you are not using at the time.
That's why the answer of the post uses an array to make quicksort non recursive.

How to calculate time complexity of a modified quick sort mixed with insertion sort?

I know how to calculate complexity for Quick Sort and Insertion Sort alone, but when I mix the two...
Here is the code:
#define THRESHOLD 50
// regular insertion sort
void isort(int *array, size_t n) {
int key, i, j;
for(j = 1; j < n; j++){
key = array[j];
i = j - 1;
while (array[i] > key && i>=0) {
array[i + 1] = array[i];
i--;
}
array[i + 1] = key;
}
}
// modified quick sort
static void sort(int *array, int start, int end) {
if (end - start > THRESHOLD) {
int pivot = array[start];
int l = start + 1;
int r = end;
while(l < r) {
if (array[l] < pivot) {
l += 1;
} else if ( array[r] >= pivot ) {
r -= 1;
} else {
swap(&array[l], &array[r]);
}
}
if (array[l] < pivot) {
swap(&array[start], &array[l]);
l -= 1;
} else {
l -= 1;
swap(&array[start], &array[l]);
}
sort(array, start, l);
sort(array, r, end);
} else {
isort(array, end - start + 1);
}
}
To summarize: this is a recursive Quick Sort, except that when the incoming vector is below the threshold, it uses the Insertion Sort.
How do I calculate the time complexity for best, average and worst cases?
The part that is confusing is that when the partition gets of size 50 or less we do an insertion sort. The particular value (50) is not that important so I will switch it to 64 for a bit easier calculations. When doing the recursive formula for the average case of qsort we assume that sorting a partition of size 64 takes log(64) * 64 operations, which apparently is a constant. Note that 64 is a constant and sorting 64 elements with insert will take in the order of O(64 * 64) even in the worst case. That is still a constant. So we will only change the constant of the asymptotic behavior complexity of qsort, but we will not change the function itself.
That being said my point is that performing another algorithm below a fixed threshold may change the constant factor of your algorithm but it will not change its complexity. It will remain the same for all cases you've mentioned.

In less-than-linear time, find the duplicate in a sorted array

Today, an interviewer asked me this question. My immediate response was that we could simply do a linear search, comparing the current element with the previous element in the array. He then asked me how the problem could be solved in less-than-linear time.
Assumptions
The array is sorted
There is only one duplicate
The array is only populated with numbers [0, n], where n is the length of the array.
Example array: [0,1,2,3,4,5,6,7,8,8,9]
I attempted to come up with a divide-and-conquer algorithm to solve this, but I'm not confident that it was the right answer. Does anyone have any ideas?
Can be done in O(log N) with a modified binary search:
Start in the middle of the array: If array[idx] < idx the duplicate is to the left, otherwise to the right. Rinse and repeat.
If no number is missing from the array, as in the example, it's doable in O(log n) with a binary search. If a[i] < i, the duplicate is before i, otherwise it's after i.
If there is one number absent and one duplicate, we still know that if a[i] < i the duplicate must be before i and if a[i] > i, the absent number must be before i and the duplicate after. However, if a[i] == i, we don't know if missing number and duplicate are both before i or both after i. I don't see a way for a sublinear algorithm in that case.
I attempted to come up with a divide-and-conquer algorithm to solve this, but I'm not confident that it was the right answer.
Sure, you could do a binary search.
If arr[i/2] >= i/2 then the duplicate is located in the upper half of the array, otherwise it is located in the lower half.
while (lower != upper)
mid = (lower + upper) / 2
if (arr[mid] >= mid)
lower = mid
else
upper = mid-1
Since the array between lower and upper is halved in each iteration, the algorithm runs in O(log n).
ideone.com demo in Java
Difference between sum of given array elements and sum of 0 to n-1 natural numbers gives you the duplicated element.
Sum of 0 to n-1 elements is (N * N-1)/2
example array is [0,1,2,3,4,5,6,7,8,8,9]
sum of 0 to 9 natural numbers is : 45
sum of given array elements : 53
53-45 = 8 Which is the duplicated element
#include <bits/stdc++.h>
using namespace std;
int find_only_repeating_element(int arr[] , int n){
int low = 0;
int high = n-1;
while(low <= high){
int mid = low + (high - low)/2;
if(arr[mid] == arr[mid + 1] || arr[mid] == arr[mid - 1]){
return arr[mid];
}
if(arr[mid] < mid + 1){
high = mid - 2;
}else{
low = mid + 1;
}
}
return -1;
}
int main(int argc, char const *argv[])
{
int n , *arr;
cin >> n;
arr = new int[n];
for(int i = 0 ; i < n ; i++){
cin >> arr[i];
}
cout << find_only_repeating_element(arr , n) << endl;
return 0;
}
How about that? (recursion style)
public static int DuplicateBinaryFind(int[] arr, int left, int right)
{
int dup =0;
if(left==right)
{
dup = left;
}
else
{
int middle = (left+right)\2;
if(arr[middle]<middle)
{
dup = DuplicateBinaryFind(arr,left, middle-1);
}
else
{
dup = DuplicateBinaryFind(arr, middle+1, right);
}
}
return dup;
}
The example array is a little bit different from your question. Since n is the length of array and there are one and only duplicate in array, the value of each element in array should be in [0,n-1].
If that is true, then this question is the same one with How to find a duplicate element in an array of shuffled consecutive integers?
The following code should find the duplicate in O(n) time and O(1) space.
public static int findOnlyDuplicateFromArray(int[] a, boolean startWithZero){
int xor = 0;
int offset = 1;
for(int i=0; i < a.length; i++){
if(startWithZero)
xor = xor ^ (a[i] + offset) ^ i;
else
xor = xor ^ a[i] ^ i;
}
if(startWithZero)
xor = xor - offset;
return xor;
}

Resources