Azor234
Azor234

Reputation: 38

Is Time complexity O(n) or O(n^2)?

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

Answers (3)

BitTickler
BitTickler

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

Farhan Ali
Farhan Ali

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

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

Related Questions