Reputation: 38
I feel that the time complexity of this js function I wrote is O(n) but at the same time it feels like its O(n^2). What's the correct time complexity? The function is supposed to find the last index of the first duplicate item found. For example in the first example, 1 was found at index 0 and 1 was also found at index 6. So the result would be 6 because thats the last index of the first duplicate values in that array. If no duplicates found then we return -1.
// [1, 2, 4, 5, 2, 3, 1] --> output: 6
// [1, 1, 3, 2, 4] --> output: 1
// [1, 2, 3, 4, 5, 6] --> output: -1(not found)
// It can be in any order, just need to find the last index of the first duplicate value thats there
const findFirstDuplicateIndex = (arr) => {
let ptr1 = 0
let ptr2 = 1
while (ptr1 < arr.length - 1) { // O(n)
if(arr[ptr1] === arr[ptr2]) {
return ptr2 + 1
} else {
ptr2++
}
if (ptr2 === arr.length - 1) {ptr1++; ptr2 = ptr1 + 1}
}
return -1
}
Upvotes: 0
Views: 2434
Reputation: 11885
No matter, how you write it down, eventually. Your code runs the variable arr1
from 0..n-2
and your variable arr2
for each arr1
from arr1+1..n-1
. Which yields the O(N^2)
(worst case) time complexity.
But since it is not as easy to spot for more complicated algorithms, one good way to assess the complexity is to have an instrumented version of the algorithm, where you simply count the number of steps. And use pathologically worst case data as input.
In your case, the worst case is, when the duplicate is positioned as the last 2 values in the array (e.g. [5 4 3 2 1 1]
).
So, in step 1, write yourself some test data generator:
(defun gen-test-data (n)
(make-array (+ n 1)
:initial-contents
(append
(loop for x from n downto 1
collecting x)
'(1))))
It produces the pathological pattern in an array of length (n+1).
(gen-test-data 20)
#(20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1)
Next, write your instrumented algorithm, which returns a little extra information:
(defun first-duplicate (data)
(let* ((arr (etypecase data
((simple-vector *) data)
(cons (make-array (length data)
:initial-contents data))))
(n (array-dimension arr 0)))
(loop
with counter = 0
for i0 below (- n 1)
do (loop
for i1 from (+ i0 1) below n
do (when (= (aref arr i0) (aref arr i1))
(return-from first-duplicate
(list :value (aref arr i0)
:i0 i0
:i1 i1
:counter counter
:n n)))
do (incf counter)))))
I picked the nested loop notation here, because that is what you do anyway.
The last step now is to run the function for various n, so you can see, how n relates to counter
:
(loop for n from 2 to 100 by 10 collecting (first-duplicate (gen-test-data n)))
((:VALUE 1 :I0 1 :I1 2 :COUNTER 2 :N 3)
(:VALUE 1 :I0 11 :I1 12 :COUNTER 77 :N 13)
(:VALUE 1 :I0 21 :I1 22 :COUNTER 252 :N 23)
(:VALUE 1 :I0 31 :I1 32 :COUNTER 527 :N 33)
(:VALUE 1 :I0 41 :I1 42 :COUNTER 902 :N 43)
(:VALUE 1 :I0 51 :I1 52 :COUNTER 1377 :N 53)
(:VALUE 1 :I0 61 :I1 62 :COUNTER 1952 :N 63)
(:VALUE 1 :I0 71 :I1 72 :COUNTER 2627 :N 73)
(:VALUE 1 :I0 81 :I1 82 :COUNTER 3402 :N 83)
(:VALUE 1 :I0 91 :I1 92 :COUNTER 4277 :N 93))
The output now clearly shows, that it cannot be O(N)
and is rather O(N^2)
.
Upvotes: 0
Reputation: 116
The time complexity of your code is O(n^2).
Your code is another version of two nested loops.
if (ptr2 === arr.length - 1) {ptr1++; ptr2 = ptr1 + 1}
This code is equal to adding a nesting loop i.e
for(ptr2 = ptr1+1; ptr2 < arr.length; ; ++ptr2)
If we rewrite your code with two nested for loops, we have
const findFirstDuplicateIndex = (arr) => {
for(let ptr1=0; ptr1 < arr.length - 1; ++ptr1) {
for(let ptr2=ptr1+1; ptr2 < arr.length; ++ptr2){
if(arr[ptr1] === arr[ptr2]) {
return ptr2
}
}
}
return -1;
}
Now,
For the 1st iteration: the inner loop will cost N-1.
For the 2nd iteration: the inner loop will cost N-2.
For the 3rd iteration: the inner loop will cost N-3.
........................
........................
For the (N-1)th iteration: the inner loop will cost 1.
So, the total time complexity is the sum of the cost which is
(N-1) + (N-2) + (N-3) + . . . . + 1
which is an arithmetic series and by using the arithmetic sum formula we have
(N-1) + (N-2) + (N-3) + . . . . + 1 = (N-1)*(N-2)/2 = O(N^2)
Hence, the time complexity of your code is O(n^2).
Upvotes: 3
Reputation: 1
In time complexity concept we have to asign a 1 value for declaring in loops we have to check the condition and after that we have to check how many times it will run and for return any value we have to add 1 and after completing we have to add alll .
Upvotes: -1