Finding for each array element, the left smaller elements. O(n) - arrays

There is a famous problem which I've been looking for, which is:
On a given array, we are trying to build another array at the same size,
which each element in the new array will be the number of the smaller elements from its left in the original array (in a row). I've been searching here in StackOverflow, and I've only found solutions in O(nlogn). I think I've found a solution at O(n).
int[] arr = {8, 4, 3, 2, 10, 9, 7, 6, 12};
Stack<Integer> stack = new Stack<>();
int[] newArr = new int[arr.length];
// Each sequence is at least 1;
for (int i = 0; i < newArr.length; i++) {
newArr[i] = 1;
}
// For each element, if it is smaller than
// the previous one, push it into the stack.
// Otherwise, compare it to all the elements
// in the stack which are smaller or equals to it,
// and summarize their values in the newArr.
stack.push(arr[0]);
for (int i = 1; i < arr.length; i++) {
if (arr[i] >= arr[i-1]) {
int j = i - 1;
while (!stack.isEmpty() && stack.top() <= arr[i]) {
arr[i] += newArr[j];
stack.pop();
j--;
}
}
stack.push(arr[i);
}
Now, the complexity time, is O(n), because any value is compared only once,
and in the worst case, where there are 'n' numbers, and they are divided into
'k' descending groups (Ex. {18, 12, 11, 9, 17, 8, 6, 4, 15, 3, 2, 1}), we're
activating the second loop only 'k' times, for 'n/k' elements. That's why
the 'k' variable does not matters and we are left with O(n) in the worst case.
* I forgot to mention, that the newArr in the code should look like this:
{1, 1, 1, 1, 5, 1, 1, 1, 9} *
Let me know if I'm right, it's really important for me.
Sincerely,
Uriah.

Yes, absolutely right with your argument. The time complexity of your code is O(n).
It can also be easily proven by the fact that each number is added and popped from the stack exactly once. Thus making it O(n).
Alternate algorithm to do the same task (lot more intuitive):
// push index instead of number itself
stack.push(0);
for (int i = 1; i < arr.length; i++) {
while (!stack.isEmpty() && arr[stack.top()] <= arr[i])
stack.pop();
if(stack.isEmpty())
new_arr[i]=i+1;
else
new_arr[i]=i-stack.top()
stack.push(i);
}
As you can notice, I push the index instead of number. That makes calculation of new array intuitive (just calculate difference between two indexes).

Related

deleting the element that is smaller than left side element in array

I'm trying to write a program whose input is an array of integers, and its size. This code has to delete the elements which are smaller than the element to the left. We want to find number of times that we have repeat this action to not be able to delete any more elements. Here is my code, it works but I want it to be faster.
Do you have any idea to make this code faster, or another way that is faster than this?
For example, given the array [10, 9, 7, 8, 6, 5, 3, 4, 2, 1], the function should return 2 because [10,9,7,8,6,5,3,4,2,1] → [10,8,4] → [10]
int numberOfTimes(int array[] , int n) {
int count = 0;
int flag = 0;
int sizeCounter = 0;
while (true){
for (int i = 0; i < n-1; ++i) {
if (array[i]<= array[i+1]){
sizeCounter++;
array[sizeCounter] = array[i+1];
} else{
flag = 1;
}
}
if (flag == 0)
return count;
count++;
flag = 0;
n = (sizeCounter+1);
sizeCounter = 0;
}
}
This problem can be solved in O(n) time and O(n) space using "monotonic stack".
For each element of the array we will find the number of "actions/turns" it takes to delete the element. In other words, how many turns have to pass, so that all elements between current element (inclusive) and the closest larger element to the left are deleted.
If we know that number (let's call it turns) then we can find maximum of this value for all elements of our array and we'll know the number of turns it takes to remove all elements from the array that can be removed. We'll have our answer.
Now, how do we find that turns value? It's easy, if we have these turns values computed for all elements to the left of the current element. We just find the closest element that is greater than current element and find the maximum number of turns for every element in between that element and the current element. I.e. if current element is at index i, and closest greater element is at index j (j < i and array[j] > array[i]), turns[i] = max(turns[k]+1), for k in [j+1..i-1].
If we do this naively, finding turns for each element would be O(n). Fortunately, it's easy to see, that when we've found j for some i, we won't ever need to consider elements between j and i ever again. Remember, array[j] > array[i] and everything in between j and i is smaller than array[i]. We're looking for the closest array element that is greater than some value, so, if array[i] is not an answer, the whole [j+1..i-1] range is also not an answer, we can go straight to j.
Having this in mind, we arrive to the monotonic stack. Instead of storing turns for every element of array, we store it only for the strictly decreasing subsequence of array that we've visited so far.
Before adding new element to the stack, first we need to remove every element that is smaller than the current element. Then the top of the stack will be our array[j].
As each element is added to the stack and removed exactly once, amortized cost of finding turns for each element is O(1), so the whole algorithm is O(n). In worst case the size of the stack grows to the same size as the array (if array is strictly decreasing). So the space complexity is O(n).
Here is the code (python):
array = [10, 9, 7, 8, 6, 5, 3, 4, 2, 1]
s = [] # monotonic stack of pairs (array[j],turns[j])
count = 0 # result: number of turns to delete all deletable elements
for el in array:
# initially assuming current element can be deleted
turns = 1
# peeking at the top of the stack
while len(s) > 0 and s[-1][0] <= el:
_,t = s.pop()
turns = max(t+1, turns)
# corner case: current element is the largest so far, cant be deleted
if len(s) == 0:
turns = 0
s.append( (el, turns) )
count = max(count, turns)
print(count)
Tests:
[10, 9, 7, 8, 6, 5, 3, 4, 2, 1] → 2
[10, 9, 7, 8, 6, 5, 3, 4, 2, 1, 9] → 3
[10, 9, 7, 8, 6, 5, 3, 4, 2, 1, 11] → 2
[] → 0
[1, 2, 3] → 0
[1, 2, 3, 1] → 1
[10, 1, 2, 3, 4, 5] → 5
UPD: I've just read the comments and I'd like to give kudos to #wLui155, who came up with the same core idea before me.

Permuting an array with a given order without making a copy of the Array or a change to the order

I already found almost the same question asked here. But I need to do it a bit more complicated.
So here is the problem. You have an array with elements and another array with the specific order the elements of the first array should be in. Here is an example:
int[] a = {5, 35, 7, 2, 7};
int[] order = {3, 0, 2, 4, 1};
After the algorithm a should look like this:
a = {2, 5, 7, 7, 35};
The array named order must not be changed in any way and all copies of an array are forbidden. Only constant variables like a normal integer are allowed.
Note that this problem is not based on a specific language. It should be in a pseudocode-like language. Just understandable.
So does anyone here have an idea? I am sitting in front of this problem for 3 days now and hope to get some help because I think I am really stuck now.
Thank you in advance.
Given the ranges of numbers shown you could:
Add 100 times the corresponding order value to each item of a.
Sort a.
Replace every item of a by item modulo 100.
Some Python:
a = [5, 35, 7, 2, 7]
order = [3, 0, 2, 4, 1]
mult = max(a) + 1
a = [a_item + order_item * mult
for a_item, order_item in zip(a, order)]
a.sort(reverse=True)
a = [a_item % mult for a_item in a]
print(a) # [2, 5, 7, 7, 35]
I should emphasize that it works for the numbers shown; negatives and overflow considerations may limit more general applicability.
The permutation defined by order consists of one or more cycles. It is straightforward to apply one cycle to array a, but the challenge is to somehow know which array elements belong to a cycle that you already processed in that way. If there is a way to mark visited elements, like with an extra bit, then that problem is solved. But using an extra bit is a cover-up for an array with additional data. So that must be ruled out.
When no possibilities exist to perform such marking, then there still is a way out: only perform the cycle operation on array a when you are at the left-most index of that cycle (or right most). The downside is that at every index you need to go through the cycle that index belongs to, to check whether you are indeed at its left-side extreme or not. This means that you'll cycle through the same cycle several times.
Here is how that looks in JavaScript:
function isLeftOfCycle(order, i) {
let j = order[i];
while (j > i) {
j = order[j];
}
return (j === i); // a boolean
}
function applyCycle(arr, order, i) {
let temp = arr[i];
let k = i;
let j = order[i];
while (j > i) {
arr[k] = arr[j];
k = j;
j = order[j];
}
arr[k] = temp;
}
function sort(a, order) {
for (let i = 0; i < order.length; i++) {
if (isLeftOfCycle(order, i)) {
applyCycle(a, order, i);
}
}
}
// Example run:
let a = [5, 35, 7, 2, 7];
let order = [3, 0, 2, 4, 1];
sort(a, order);
console.log(a);
Obviously, this comes at a price: the time complexity is no longer O(n), but O(n²).

Insert a smallest possible positive integer into an array of unique integers [duplicate]

This question already has answers here:
Find the Smallest Integer Not in a List
(28 answers)
Closed 3 years ago.
I am trying to tackle this interview question: given an array of unique positive integers, find the smallest possible number to insert into it so that every integer is still unique. The algorithm should be in O(n) and the additional space complexity should be constant. Assigning values in the array to other integers is allowed.
For example, for an array [5, 3, 2, 7], output should be 1. However for [5, 3, 2, 7, 1], the answer should then be 4.
My first idea is to sort the array, then go through the array again to find where the continuous sequence breaks, but sorting needs more than O(n).
Any ideas would be appreciated!
My attempt:
The array A is assumed 1-indexed. We call an active value one that is nonzero and does not exceed n.
Scan the array until you find an active value, let A[i] = k (if you can't find one, stop);
While A[k] is active,
Move A[k] to k while clearing A[k];
Continue from i until you reach the end of the array.
After this pass, all array entries corresponding to some integer in the array are cleared.
Find the first nonzero entry, and report its index.
E.g.
[5, 3, 2, 7], clear A[3]
[5, 3, 0, 7], clear A[2]
[5, 0, 0, 7], done
The answer is 1.
E.g.
[5, 3, 2, 7, 1], clear A[5],
[5, 3, 2, 7, 0], clear A[1]
[0, 3, 2, 7, 0], clear A[3],
[0, 3, 0, 7, 0], clear A[2],
[0, 0, 0, 7, 0], done
The answer is 4.
The behavior of the first pass is linear because every number is looked at once (and immediately cleared), and i increases regularly.
The second pass is a linear search.
A= [5, 3, 2, 7, 1]
N= len(A)
print(A)
for i in range(N):
k= A[i]
while k > 0 and k <= N:
A[k-1], k = 0, A[k-1] # -1 for 0-based indexing
print(A)
[5, 3, 2, 7, 1]
[5, 3, 2, 7, 0]
[0, 3, 2, 7, 0]
[0, 3, 2, 7, 0]
[0, 3, 0, 7, 0]
[0, 0, 0, 7, 0]
[0, 0, 0, 7, 0]
Update:
Based on גלעד ברקן's idea, we can mark the array elements in a way that does not destroy the values. Then you report the index of the first unmarked.
print(A)
for a in A:
a= abs(a)
if a <= N:
A[a-1]= - A[a-1] # -1 for 0-based indexing
print(A)
[5, 3, 2, 7, 1]
[5, 3, 2, 7, -1]
[5, 3, -2, 7, -1]
[5, -3, -2, 7, -1]
[5, -3, -2, 7, -1]
[-5, -3, -2, 7, -1]
From the question description: "Assigning values in the array to other integers is allowed." This is O(n) space, not constant.
Loop over the array and multiply A[ |A[i]| - 1 ] by -1 for |A[i]| < array length. Loop a second time and output (the index + 1) for the first cell not negative or (array length + 1) if they are all marked. This takes advantage of the fact that there could not be more than (array length) unique integers in the array.
I will use 1-based indexing.
The idea is to reuse input collection and arrange to swap integer i at ith place if its current position is larger than i. This can be performed in O(n).
Then on second iteration, you find the first index i not containing i, which is again O(n).
In Smalltalk, implemented in Array (self is the array):
firstMissing
self size to: 1 by: -1 do: [:i |
[(self at: i) < i] whileTrue: [self swap: i with: (self at: i)]].
1 to: self size do: [:i |
(self at: i) = i ifFalse: [^i]].
^self size + 1
So we have two loops in O(n), but we also have another loop inside the first loop (whileTrue:). So is the first loop really O(n)?
Yes, because each element will be swapped at most once, since they will arrive at their right place. We see that the cumulated number of swap is bounded by array size, and the overall cost of first loop is at most 2*n, the total cost incuding last seatch is at most 3*n, still O(n).
You also see that we don't care to swap case of (self at: i) > i and: [(self at:i) <= self size], why? Because we are sure that there will be a smaller missing element in this case.
A small test case:
| trial |
trial := (1 to: 100100) asArray shuffled first: 100000.
self assert: trial copy firstMissing = trial sorted firstMissing.
You could do the following.
Find the maximum (m), sum of all elements (s), number of elements (n)
There are m-n elements missing, their sum is q = sum(1..m) - s - there is a closed-form solution for the sum
If you are missing only one integer, you're done - report q
If you are missing more than one (m-n), you realize that the sum of the missing integers is q, and at least one of them will be smaller than q/(m-n)
You start from the top, except you will only take into account integers smaller than q/(m-n) - this will be the new m, only elements below that maximum contribute to the new s and n. Do this until you are left with only one missing integer.
Still, this may not be linear time, I'm not sure.
EDIT: you should use the candidate plus half the input size as a pivot to reduce the constant factor here – see Daniel Schepler’s comment – but I haven’t had time to get it working in the example code yet.
This isn’t optimal – there’s a clever solution being looked for – but it’s enough to meet the criteria :)
Define the smallest possible candidate so far: 1.
If the size of the input is 0, the smallest possible candidate is a valid candidate, so return it.
Partition the input into < pivot and > pivot (with median of medians pivot, like in quicksort).
If the size of ≤ pivot is less than pivot itself, there’s a free value in there, so start over at step 2 considering only the < pivot partition.
Otherwise (when it’s = pivot), the new smallest possible candidate is the pivot + 1. Start over at step 2 considering only the > pivot partition.
I think that works…?
'use strict';
const swap = (arr, i, j) => {
[arr[i], arr[j]] = [arr[j], arr[i]];
};
// dummy pivot selection, because this part isn’t important
const selectPivot = (arr, start, end) =>
start + Math.floor(Math.random() * (end - start));
const partition = (arr, start, end) => {
let mid = selectPivot(arr, start, end);
const pivot = arr[mid];
swap(arr, mid, start);
mid = start;
for (let i = start + 1; i < end; i++) {
if (arr[i] < pivot) {
mid++;
swap(arr, i, mid);
}
}
swap(arr, mid, start);
return mid;
};
const findMissing = arr => {
let candidate = 1;
let start = 0;
let end = arr.length;
for (;;) {
if (start === end) {
return candidate;
}
const pivotIndex = partition(arr, start, end);
const pivot = arr[pivotIndex];
if (pivotIndex + 1 < pivot) {
end = pivotIndex;
} else {
//assert(pivotIndex + 1 === pivot);
candidate = pivot + 1;
start = pivotIndex + 1;
}
}
};
const createTestCase = (size, max) => {
if (max < size) {
throw new Error('size must be < max');
}
const arr = Array.from({length: max}, (_, i) => i + 1);
const expectedIndex = Math.floor(Math.random() * size);
arr.splice(expectedIndex, 1 + Math.floor(Math.random() * (max - size - 1)));
for (let i = 0; i < size; i++) {
let j = i + Math.floor(Math.random() * (size - i));
swap(arr, i, j);
}
return {
input: arr.slice(0, size),
expected: expectedIndex + 1,
};
};
for (let i = 0; i < 5; i++) {
const test = createTestCase(1000, 1024);
console.log(findMissing(test.input), test.expected);
}
The correct method I almost got on my own, but I had to search for it, and I found it here: https://www.geeksforgeeks.org/find-the-smallest-positive-number-missing-from-an-unsorted-array/
Note: This method is destructive to the original data
Nothing in the original question said you could not be destructive.
I will explain what you need to do now.
The basic "aha" here is that the first missing number must come within the first N positive numbers, where N is the length of the array.
Once you understand this and realize you can use the values in the array itself as markers, you just have one problem you need to address: Does the array have numbers less than 1 in it? If so we need to deal with them.
Dealing with 0s or negative numbers can be done in O(n) time. Get two integers, one for our current value, and one for the end of the array. As we scan through, if we find a 0 or negative number, we perform a swap using the third integer, with the final value in the array. Then we decrement our end of an array pointer. We continue until our current pointer is past the end of the array pointer.
Code example:
while (list[end] < 1) {
end--;
}
while (cur< end) {
if (n < 1) {
swap(list[cur], list[end]);
while (list[end] < 1) {
end--;
}
}
}
Now we have the end of the array, and a truncated array. From here we need to see how we can use the array itself. Since all numbers that we care about are positive, and we have a pointer to the position of how many of them there are, we can simply multiply one by -1 to mark that place as present if there was a number in the array there.
e.g. [5, 3, 2, 7, 1] when we read 3, we change it to [5, 3, -2, 7, 1]
Code example:
for (cur = 0; cur <= end; begin++) {
if (!(abs(list[cur]) > end)) {
list[abs(list[cur]) - 1] *= -1;
}
}
Now, note: You need to read the absolute value of the integer in the position because it might be changed to be negative. Also note, if an integer is greater than your end of list pointer, do not change anything as that integer will not matter.
Finally, once you have read all the positive values, iterate through them to find the first one that is currently positive. This place represents your first missing number.
Step 1: Segregate 0 and negative numbers from your list to the right. O(n)
Step 2: Using the end of list pointer iterate through the entire list marking
relevant positions negative. O(n-k)
Step 3: Scan the numbers for the position of the first non-negative number. O(n-k)
Space Complexity: The original list is not counted, I used 3 integers beyond that. So
it is O(1)
One thing I should mention is the list [5, 4, 2, 1, 3] would end up [-5, -4, -2, -1, -3] so in this case, you would choose the first number after the end position of the list, or 6 as your result.
Code example for step 3:
for (cur = 0; cur < end; cur++) {
if (list[cur] > 0) {
break;
}
}
print(cur);
use this short and sweet algorithm:
A is [5, 3, 2, 7]
1- Define B With Length = A.Length; (O(1))
2- initialize B Cells With 1; (O(n))
3- For Each Item In A:
if (B.Length <= item) then B[Item] = -1 (O(n))
4- The answer is smallest index in B such that B[index] != -1 (O(n))

Move items to the front of an array

I have items in the centre of an array and I want to move them to the front of this array. For example:
array[8] = {10, 38, 38, 0, 8, 39, 10, 22}
and I have an index array
index[6] = {0, 3, 4, 6, 7, 1}
and I want to move these 6 items to the front of the array
result[8] = {10, 0, 8, 10, 22, 38, 38, 39}
Actually the order doesn't matter, just make sure the item whose index is in the index array should always before the item whose index is not in the index array.
Can anyone give me a fast algorithm? Actually this is one step in an KNN problem, the data array could be very large. The algorithm should run as fast as possible and the extra space needed should be as small as possible. It is better if you can give me CUDA implementation.
Update: Compare to the data array, the size of the index array is very small. In my case, it is only about 200.
Update: Please note that the size of the data array could be very very very large! It goes to 1M, 10M even higher(The data array is loaded to GPU memory which is quite limited). Any algorithm needs a temp array which has the same size with data array is not acceptable.
Sort the index array in increasing order, this step will make sure that we will not make any unnecessary swap.
Starting from 0 to n - 1 (n is the length of array index), swap the ith element in the array with index[i]th element.
Pseudo Code
sort(index);
for(int i = 0; i < index.length; i++){
swap(array, i , index[i]);
}
If you don't want to sort index, we can always find the smallest element in the index array which is not at the beginning of the array. (as the size of index is small)
Use an boolean used to mark which position in the array index is already put at the correct position.
Pseudocode:
bool []used = //
for(int i = 0; i < index.length; i++){
int nxt = -1;
for(int j = 0; j < index.length; j++){
if(!used[j]){
if(nxt == -1 || index[j] < index[nxt]){
nxt = j;
}
}
}
used[nxt] = true;
swap(array, i, nxt);
}
const int ARRAY_SIZE = sizeof(array) / sizeof(array[0]);
const int INDEX_SIZE = sizeof(index) / sizeof(index[0]);
bool used[ARRAY_SIZE] = {};
for (int i = 0; i < INDEX_SIZE; ++i)
{
int id = index[i];
result[i] = array[id];
used[id] = 1;
}
for (int i = 0, id = INDEX_SIZE; i < ARRAY_SIZE; ++i)
{
if (!used[i])
{
result[id] = array[i];
++id;
}
}
Approach 1
You can modify insertion sort to solve your problem which will eventually give you O(n^2) time complexity.
But if you want to keep run time in order of N then you can use following approach.
Approach 2
Here we can use index array as auxiliary space as follows :
step 1
store all actual values instead of indexes in index table(array) and replace the value array with negative/non accepting value.
Value Array
[ -1, -1, 38, -1, -1, 39, -1, -1 ]
Index Array
[ 10, 0, 8, 10, 22, 38 ]
complexity in this operation is O(n)
step 2
shift all the remaining at last which will take O(n) time complexity.
Value Array ###
[ -1, -1, -1, -1, -1, -1, 38, 39 ]
Index Array
[ 10, 0, 8, 10, 22, 38 ]
step 3
not put the element from index array to value array.
Value Array
[ 10, 0, 8, 10, 22, 38, 38, 39 ]
Index Array
[ 10, 0, 8, 10, 22, 38 ]
time complexity for this operation is O(n)
Total run time complexity for this approach : O(n)
Improvement
Here in this approach you are not able to preserve your index array. While you can preserve it using O(index array size) space complexity OR with the condition that value array does not contain any non negative values then while keeping -1/non accepting value in it you can use storing index with -ve and in third step you can recover your index array as it is.

Remove duplicates from Array without using Hash Table

i have an array which might contain duplicate elements(more than two duplicates of an element). I wonder if it's possible to find and remove the duplicates in the array:
without using Hash Table (strict requirement)
without using a temporary secondary array. No restrictions on complexity.
P.S: This is not Home work question
Was asked to my friend in yahoo technical interview
Sort the source array. Find consecutive elements that are equal. (I.e. what std::unique does in C++ land). Total complexity is N lg N, or merely N if the input is already sorted.
To remove duplicates, you can copy elements from later in the array over elements earlier in the array also in linear time. Simply keep a pointer to the new logical end of the container, and copy the next distinct element to that new logical end at each step. (Again, exactly like std::unique does (In fact, why not just download an implementation of std::unique and do exactly what it does? :P))
O(NlogN) : Sort and replace consecutive same element with one copy.
O(N2) : Run nested loop to compare each element with the remaining elements in the array, if duplicate found, swap the duplicate with the element at the end of the array and decrease the array size by 1.
No restrictions on complexity.
So this is a piece of cake.
// A[1], A[2], A[3], ... A[i], ... A[n]
// O(n^2)
for(i=2; i<=n; i++)
{
duplicate = false;
for(j=1; j<i; j++)
if(A[i] == A[j])
{duplicate = true; break;}
if(duplicate)
{
// "remove" A[i] by moving all elements from its left over it
for(j=i; j<n; j++)
A[j] = A[j+1];
n--;
}
}
In-place duplicate removal that preserves the existing order of the list, in quadratic time:
for (var i = 0; i < list.length; i++) {
for (var j = i + 1; j < list.length;) {
if (list[i] == list[j]) {
list.splice(j, 1);
} else {
j++;
}
}
}
The trick is to start the inner loop on i + 1 and not increment the inner counter when you remove an element.
The code is JavaScript, splice(x, 1) removes the element at x.
If order preservation isn't an issue, then you can do it quicker:
list.sort();
for (var i = 1; i < list.length;) {
if (list[i] == list[i - 1]) {
list.splice(i, 1);
} else {
i++;
}
}
Which is linear, unless you count the sort, which you should, so it's of the order of the sort -- in most cases n × log(n).
In functional languages you can combine sorting and unicification (is that a real word?) in one pass.
Let's take the standard quick sort algorithm:
- Take the first element of the input (x) and the remaining elements (xs)
- Make two new lists
- left: all elements in xs smaller than or equal to x
- right: all elements in xs larger than x
- apply quick sort on the left and right lists
- return the concatenation of the left list, x, and the right list
- P.S. quick sort on an empty list is an empty list (don't forget base case!)
If you want only unique entries, replace
left: all elements in xs smaller than or equal to x
with
left: all elements in xs smaller than x
This is a one-pass O(n log n) algorithm.
Example implementation in F#:
let rec qsort = function
| [] -> []
| x::xs -> let left,right = List.partition (fun el -> el <= x) xs
qsort left # [x] # qsort right
let rec qsortu = function
| [] -> []
| x::xs -> let left = List.filter (fun el -> el < x) xs
let right = List.filter (fun el -> el > x) xs
qsortu left # [x] # qsortu right
And a test in interactive mode:
> qsortu [42;42;42;42;42];;
val it : int list = [42]
> qsortu [5;4;4;3;3;3;2;2;2;2;1];;
val it : int list = [1; 2; 3; 4; 5]
> qsortu [3;1;4;1;5;9;2;6;5;3;5;8;9];;
val it : int list = [1; 2; 3; 4; 5; 6; 8; 9]
Since it's an interview question it is usually expected by the interviewer to be asked precisions about the problem.
With no alternative storage allowed (that is O(1) storage allowed in that you'll probably use some counters / pointers), it seems obvious that a destructive operation is expected, it might be worth pointing it out to the interviewer.
Now the real question is: do you want to preserve the relative order of the elements ? ie is this operation supposed to be stable ?
Stability hugely impact the available algorithms (and thus the complexity).
The most obvious choice is to list Sorting Algorithms, after all, once the data is sorted, it's pretty easy to get unique elements.
But if you want stability, you cannot actually sort the data (since you could not get the "right" order back) and thus I wonder if it solvable in less than O(N**2) if stability is involved.
doesn't use a hash table per se but i know behind the scenes it's an implementation of one. Nevertheless, thought I might post in case it can help. This is in JavaScript and uses an associative array to record duplicates to pass over
function removeDuplicates(arr) {
var results = [], dups = [];
for (var i = 0; i < arr.length; i++) {
// check if not a duplicate
if (dups[arr[i]] === undefined) {
// save for next check to indicate duplicate
dups[arr[i]] = 1;
// is unique. append to output array
results.push(arr[i]);
}
}
return results;
}
Let me do this in Python.
array1 = [1,2,2,3,3,3,4,5,6,4,4,5,5,5,5,10,10,8,7,7,9,10]
array1.sort()
print(array1)
current = NONE
count = 0
# overwriting the numbers at the frontal part of the array
for item in array1:
if item != current:
array1[count] = item
count +=1
current=item
print(array1)#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, 10, 10]
print(array1[:count])#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
The most Efficient method is :
array1 = [1,2,2,3,3,3,4,5,6,4,4,5,5,5,5,10,10,8,7,7,9,10]
array1.sort()
print(array1)
print([*dict.fromkeys(array1)])#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#OR#
aa = list(dict.fromkeys(array1))
print( aa)#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Resources