```
procedure max (a[1..n]: integers)
max := a[1]
for i := 2 to n
if max < a[i] then max := a[i]
```

Is the complexity `O(1)`

or `O(n)`

with the best case scenario? The sequence contains `n`

elements. It is pseudocode.

Skip to content
# R – the complexity of this algorithm

#### Related Solutions

###### Related Question

big-ocomplexity-theory

```
procedure max (a[1..n]: integers)
max := a[1]
for i := 2 to n
if max < a[i] then max := a[i]
```

Is the complexity `O(1)`

or `O(n)`

with the best case scenario? The sequence contains `n`

elements. It is pseudocode.

I'll do my best to explain it here on simple terms, but be warned that this topic takes my students a couple of months to finally grasp. You can find more information on the Chapter 2 of the Data Structures and Algorithms in Java book.

There is no mechanical procedure that can be used to get the BigOh.

As a "cookbook", to obtain the BigOh from a piece of code you first need to realize that you are creating a math formula to count how many steps of computations get executed given an input of some size.

The purpose is simple: to compare algorithms from a theoretical point of view, without the need to execute the code. The lesser the number of steps, the faster the algorithm.

For example, let's say you have this piece of code:

```
int sum(int* data, int N) {
int result = 0; // 1
for (int i = 0; i < N; i++) { // 2
result += data[i]; // 3
}
return result; // 4
}
```

This function returns the sum of all the elements of the array, and we want to create a formula to count the computational complexity of that function:

```
Number_Of_Steps = f(N)
```

So we have `f(N)`

, a function to count the number of computational steps. The input of the function is the size of the structure to process. It means that this function is called such as:

```
Number_Of_Steps = f(data.length)
```

The parameter `N`

takes the `data.length`

value. Now we need the actual definition of the function `f()`

. This is done from the source code, in which each interesting line is numbered from 1 to 4.

There are many ways to calculate the BigOh. From this point forward we are going to assume that every sentence that doesn't depend on the size of the input data takes a constant `C`

number computational steps.

We are going to add the individual number of steps of the function, and neither the local variable declaration nor the return statement depends on the size of the `data`

array.

That means that lines 1 and 4 takes C amount of steps each, and the function is somewhat like this:

```
f(N) = C + ??? + C
```

The next part is to define the value of the `for`

statement. Remember that we are counting the number of computational steps, meaning that the body of the `for`

statement gets executed `N`

times. That's the same as adding `C`

, `N`

times:

```
f(N) = C + (C + C + ... + C) + C = C + N * C + C
```

There is no mechanical rule to count how many times the body of the `for`

gets executed, you need to count it by looking at what does the code do. To simplify the calculations, we are ignoring the variable initialization, condition and increment parts of the `for`

statement.

To get the actual BigOh we need the Asymptotic analysis of the function. This is roughly done like this:

- Take away all the constants
`C`

. - From
`f()`

get the polynomium in its`standard form`

. - Divide the terms of the polynomium and sort them by the rate of growth.
- Keep the one that grows bigger when
`N`

approaches`infinity`

.

Our `f()`

has two terms:

```
f(N) = 2 * C * N ^ 0 + 1 * C * N ^ 1
```

Taking away all the `C`

constants and redundant parts:

```
f(N) = 1 + N ^ 1
```

Since the last term is the one which grows bigger when `f()`

approaches infinity (think on limits) this is the BigOh argument, and the `sum()`

function has a BigOh of:

```
O(N)
```

There are a few tricks to solve some tricky ones: use summations whenever you can.

As an example, this code can be easily solved using summations:

```
for (i = 0; i < 2*n; i += 2) { // 1
for (j=n; j > i; j--) { // 2
foo(); // 3
}
}
```

The first thing you needed to be asked is the order of execution of `foo()`

. While the usual is to be `O(1)`

, you need to ask your professors about it. `O(1)`

means (almost, mostly) constant `C`

, independent of the size `N`

.

The `for`

statement on the sentence number one is tricky. While the index ends at `2 * N`

, the increment is done by two. That means that the first `for`

gets executed only `N`

steps, and we need to divide the count by two.

```
f(N) = Summation(i from 1 to 2 * N / 2)( ... ) =
= Summation(i from 1 to N)( ... )
```

The sentence number *two* is even trickier since it depends on the value of `i`

. Take a look: the index i takes the values: 0, 2, 4, 6, 8, ..., 2 * N, and the second `for`

get executed: N times the first one, N - 2 the second, N - 4 the third... up to the N / 2 stage, on which the second `for`

never gets executed.

On formula, that means:

```
f(N) = Summation(i from 1 to N)( Summation(j = ???)( ) )
```

Again, we are counting **the number of steps**. And by definition, every summation should always start at one, and end at a number bigger-or-equal than one.

```
f(N) = Summation(i from 1 to N)( Summation(j = 1 to (N - (i - 1) * 2)( C ) )
```

(We are assuming that `foo()`

is `O(1)`

and takes `C`

steps.)

We have a problem here: when `i`

takes the value `N / 2 + 1`

upwards, the inner Summation ends at a negative number! That's impossible and wrong. We need to split the summation in two, being the pivotal point the moment `i`

takes `N / 2 + 1`

.

```
f(N) = Summation(i from 1 to N / 2)( Summation(j = 1 to (N - (i - 1) * 2)) * ( C ) ) + Summation(i from 1 to N / 2) * ( C )
```

Since the pivotal moment `i > N / 2`

, the inner `for`

won't get executed, and we are assuming a constant C execution complexity on its body.

Now the summations can be simplified using some identity rules:

- Summation(w from 1 to N)( C ) = N * C
- Summation(w from 1 to N)( A (+/-) B ) = Summation(w from 1 to N)( A ) (+/-) Summation(w from 1 to N)( B )
- Summation(w from 1 to N)( w * C ) = C * Summation(w from 1 to N)( w ) (C is a constant, independent of
`w`

) - Summation(w from 1 to N)( w ) = (N * (N + 1)) / 2

Applying some algebra:

```
f(N) = Summation(i from 1 to N / 2)( (N - (i - 1) * 2) * ( C ) ) + (N / 2)( C )
f(N) = C * Summation(i from 1 to N / 2)( (N - (i - 1) * 2)) + (N / 2)( C )
f(N) = C * (Summation(i from 1 to N / 2)( N ) - Summation(i from 1 to N / 2)( (i - 1) * 2)) + (N / 2)( C )
f(N) = C * (( N ^ 2 / 2 ) - 2 * Summation(i from 1 to N / 2)( i - 1 )) + (N / 2)( C )
=> Summation(i from 1 to N / 2)( i - 1 ) = Summation(i from 1 to N / 2 - 1)( i )
f(N) = C * (( N ^ 2 / 2 ) - 2 * Summation(i from 1 to N / 2 - 1)( i )) + (N / 2)( C )
f(N) = C * (( N ^ 2 / 2 ) - 2 * ( (N / 2 - 1) * (N / 2 - 1 + 1) / 2) ) + (N / 2)( C )
=> (N / 2 - 1) * (N / 2 - 1 + 1) / 2 =
(N / 2 - 1) * (N / 2) / 2 =
((N ^ 2 / 4) - (N / 2)) / 2 =
(N ^ 2 / 8) - (N / 4)
f(N) = C * (( N ^ 2 / 2 ) - 2 * ( (N ^ 2 / 8) - (N / 4) )) + (N / 2)( C )
f(N) = C * (( N ^ 2 / 2 ) - ( (N ^ 2 / 4) - (N / 2) )) + (N / 2)( C )
f(N) = C * (( N ^ 2 / 2 ) - (N ^ 2 / 4) + (N / 2)) + (N / 2)( C )
f(N) = C * ( N ^ 2 / 4 ) + C * (N / 2) + C * (N / 2)
f(N) = C * ( N ^ 2 / 4 ) + 2 * C * (N / 2)
f(N) = C * ( N ^ 2 / 4 ) + C * N
f(N) = C * 1/4 * N ^ 2 + C * N
```

And the BigOh is:

```
O(N²)
```

You model the time function to calculate `Fib(n)`

as sum of time to calculate `Fib(n-1)`

plus the time to calculate `Fib(n-2)`

plus the time to add them together (`O(1)`

). This is assuming that repeated evaluations of the same `Fib(n)`

take the same time - i.e. no memoization is use.

`T(n<=1) = O(1)`

`T(n) = T(n-1) + T(n-2) + O(1)`

You solve this recurrence relation (using generating functions, for instance) and you'll end up with the answer.

Alternatively, you can draw the recursion tree, which will have depth `n`

and intuitively figure out that this function is asymptotically `O(2`

^{n}`)`

. You can then prove your conjecture by induction.

Base: `n = 1`

is obvious

Assume `T(n-1) = O(2`

^{n-1}`)`

, *therefore*

`T(n) = T(n-1) + T(n-2) + O(1)`

*which is equal to*

`T(n) = O(2`

^{n-1}`) + O(2`

^{n-2}`) + O(1) = O(2`

^{n}`)`

However, as noted in a comment, this is not the tight bound. An interesting fact about this function is that the T(n) is asymptotically **the same** as the value of `Fib(n)`

since both are defined as

`f(n) = f(n-1) + f(n-2)`

.

The leaves of the recursion tree will always return 1. The value of `Fib(n)`

is sum of all values returned by the leaves in the recursion tree which is equal to the count of leaves. Since each leaf will take O(1) to compute, `T(n)`

is equal to `Fib(n) x O(1)`

. Consequently, the tight bound for this function is the Fibonacci sequence itself (~`θ(1.6`

^{n}`)`

). You can find out this tight bound by using generating functions as I'd mentioned above.

- A plain English explanation of “Big O” notation
- What are the differences between NP, NP-Complete and NP-Hard
- What does O(log n) mean exactly
- How to building a heap be O(n) time complexity
- How to find time complexity of an algorithm
- Determining complexity for recursive functions (Big O notation)
- Are 2^n and n*2^n in the same time complexity

## Best Solution

There's no difference between the best case and worst case asymptotic running times for this algorithm. In all cases, you have to traverse the whole array (

`n`

elements) and your algorithm would be O(n).Theoretically, there's no way you can find the maximum element of an arbitrary array in less than O(n) since you should always visit each element at least once.