Interview Question
Country: United States
the post had the formatting messed up in a few places (some extra ;'s appear, like in abs macro):
Redo:
#define ABS(x) ( (x) < 0 ? -(x) : (x) )
Small adjustment:
On the first pass (zero check), keep the position of the zero:
int zeropos = -1;
for(i=0; i<N; i++)
{
if(a[i] == 0)
if(zeropos != -1) return true; //duplicate 0
else
zeropos = i //position of 0
}
Then add an extra check (either as part of the sign flipping for loop, or as an extra pass before hand, to see if there are duplicate "zeropos" values in the array:
Here I show the extra pass pre-signflipcode:
bool extracheck
for(i=0; i<N; i++)
{
if( a[i] == zeropos )
if( extracheck ) return true; //duplicate "zeropos" values in a[ ]
else
extracheck = true;
}
Again, I need to compile and test.
Sorry, but above I was talking "out loud" and building the solution by iteration (and explaining).
It's better if I drop a clean almalgamation of it.
This also should be "nondestructive" on the array.
collabedit.com/bbptb
Again, no compiling/testing done.
#define ABS(x) ((x)<0 ? -(x) : (x))
/* put this in a function that return true if duplicate is found in array a[] of size N */
int zeropos = -1; //position of any 0 found in array
for(i=0; i<N; i++)
if(a[i] == 0)
if(zeropos != -1) return true; //duplicate 0 found (and array is untouched, so return immediately )
else
zeropos = i //position of 0
a[zeropos] = 1; //set to 1 so the next for loop works for dup="zeropos"
bool dupfound; // true means we found duplicate
for(int i=0; i<N; i++)
{
// this means abs(a[i]) was used as an index into a[ ] already
if( a[ abs(a[i]) ] < 0 ) { dupfound = true; break; }
// first time abs(a[i]) used to index a[ ], change sign below to trap second time
a[ abs(a[i]) ] *= -1;
}
/* next part "recreates" the original array */
for(int i=0; i<N; i++)
if( a[i] < 0 ) a[i] *=-1;
a[zeropos]=0; //recreate the zero element
return dupfound;
//test the code
public static void main(String[] args) {
int [] array1 = {0,1,2,3,4,5};
int [] array2 = {0,5,2,3,4,5};
System.out.println("#1: "+duplicateChecker(array1));
System.out.println("#2: "+duplicateChecker(array2));
}
/**You have an array of length n, containing values of length 0 to n-1. Tell me if there exists a duplicate (return true or false). Do it in constant space and O(n) time.*/
public static boolean duplicateChecker(int[] array) {
int val;
int len = array.length;
boolean[] exists = new boolean[len];
for (int i = 0; i < len; i++) {
val = array[i];
if (exists[val]) return true;
exists[val] = true;
}
return false;
}
@Miguel True & thank you for the review.
I suppose it depends on the memory/processor constraints and expected input. Mine involves the least number of steps, but uses more memory. Yours has about 2x the steps, and destroys the original array in the process. The top-voted comment is quite clever, using a 'negative' flag, but in the process performs about 3x the operations without destroying the original array.
I only recently began these types of interviews, and noticed they're much more picky about memory usage than they are the number of steps in the process.
This also should be "nondestructive" on the array.
* O(n) time
* O(1) space
* array left untouched after result returned
collabedit.com/bbptb
Again, no compiling/testing done.
#define ABS(x) ((x)< 0 ? -(x) : (x))
/* put this in a function that return true if duplicate is found in array a[] of size N */
int zeropos = -1; //position of any 0 found in array
for(i=0; i<N; i++)
if(a[i] == 0)
if(zeropos != -1) return true; //duplicate 0 found (and array is untouched, so return immediately )
else
zeropos = i //position of 0
a[zeropos] = 1; //set to 1 so the next for loop works for dup="zeropos"
bool dupfound; // true means we found duplicate
for(int i=0; i<N; i++)
{
// this means abs(a[i]) was used as an index into a[ ] already
if( a[ abs(a[i]) ] < 0 ) { dupfound = true; break; }
// first time abs(a[i]) used to index a[ ], change sign below to trap second time
a[ abs(a[i]) ] *= -1;
}
/* next part "recreates" the original array */
for(int i=0; i<N; i++)
if( a[i] < 0 ) a[i] *=-1;
a[zeropos]=0; //recreate the zero element
return dupfound;
Basically, if there are no duplicates, we receive a permutation of the numbers 0..n-1.
We can rearrange the array such that number i is at position i.
Take this example: 0,3,2,5,4,1
0 is in the correct position.
3 is at position 1, we will swap it with the number at position 3 (5) to put it in the right position
0,5,2,3,4,1
now 5 is wrong, swap it with the number at position 5 (1)
0,1,2,3,4,5
now 1 is ok. 2, 3, 4 and 5 are too.
If at any step we try to swap a number into a position which already contains that number, then there are duplicates.
#include <iostream>
using namespace std;
bool Check(int* v, int n) {
for (int i = 0; i < n; i++)
if (v[i] != i) {
do {
if (v[v[i]] == v[i])
return true;
swap(v[i], v[v[i]]);
} while (v[i] != i);
}
return false;
}
int main() {
int v1[] = {0,1,2,3,4,5};
int v2[] = {0,5,2,3,4,5};
int v3[] = {0,3,2,5,4,1};
cout << Check(v1, 6) << endl;
cout << Check(v2, 6) << endl;
cout << Check(v3, 6) << endl;
return 0;
}
How about using XOR:
a[n+1] = {0};
for(int i =0; i < n; ++i
{
int k = array[i] + 1; // add one is for the element zero
if (a[k]^k == 0)
return true;
else
a[k] = k;
}
return false;
I just think of a way.
in C++, there is a "set" container.
it will only store unique value.
So go along the array and insert value into the container, with complexity O(n).
compare the size of this container and original array.
if they are the same, there are no duplicate.
2 problems:
1) Set operations run in O(log n), so the overall solution will work in O(n log n), not O(n).
2) You would be using extra O(n) memory, violating the constant space constraint.
Since the values are only in the interval [0, n[ we could just use a boolean array with n positions to keep it O(n), but it still violates 2).
It is not warranted that the array is sorted or if every element is placed in its correct index within the specified range. We can still have an O(n) algorithm using XOR of the elements of the array. If the final XOR-ed value is zero that means all elements are of even number, so they cancel each other out. Otherwise, the result of this XOR is the element with no duplicate.
bool CheckDuplicate(int *a, int n)
{
if(a==NULL || n<=0)
return false;
result=0;
for(i=0;i<n;i++)
result^=a[i];
return (result==0 ? true : false);
Assume all this is in a function that returns true if a duplicate is found:
0) Needed is an abs. value function or macro:
1) Have a boolean flag to check if you have duplicate 0's.
So the zero check is ~1n single pass, plus 1 boolean var. space.
Now we can assume the array has values in the range 1 to n-1 only.
This means a few things:
1) We can flip any value in the array from positive to negative and not "lose" information, because abs(a[i]) == original(a[i]) regardless if we flipped the sign.
So we can flip signs as we desire.
2) Since 0 can no longer be a value, we "know" if a[i] < 0, that "we" are the ones that had flipped it's sign. ( 0 would mess this up because -0 == 0 ).
3) Since the values are in the range 1 to n-1 and len(a)==n, all the original values of the array can index the array itself.
Now comes the code that someone has to do on paper to get it the first time:
Type this raw, so hope it's okay.
- S O U N D W A V E October 16, 2013Runtime O(n) , two passes with constant effort on each element during each pass
Aux. storage O(1) ... just the index i, and boolean seenzero (1 stack frame, registers)
Add 1 more pass if you need the array back to it's original form (take abs of each element).