Analysis of array algorithm and its time complexity - java

I'm supposed to analyse this code and say something about its time complexity, but I am having trouble understanding what the code itself does, how does it change array a?
I also don't understand the following two operations:
1) foobar[a[i]]++;
So you replace the zeros in foobar with elements of array a? But what does the ++ do?
2)
a[outPos++]=1;
This increments outPos first, and leaves a[0] unchanged during the whole for loop?
public static void foo(int[] a, int b) {
int [] foobar = new int[b+1];
for (int i = 0; i<foobar.length; i++)
foobar[i]=0;
for (int i = 0; i<a.length; i++)
foobar[a[i]]++;
int outPos = 0;
for (int i=0; i<foobar.length; i++)
for (int j=0; j<foobar[i]; j++)
a[outPos++]=i;
}
As far as time complexity goes, I think it's O(n^2). The first two for loops run in constant time. But the worst case of the third nested loop seems to me that the last element in foobar is big, and then it would be quadratic?

It is an implementation of Counting Sort,
Where a[] stores the array to be sorted and b is the maximum integer in a[]
foobar[] is the counting array which is of size b
Let N = sizeof(a), M = b for common notation,
The first two loops:
Initializing counting array to zeros O(M)
Count the elements, say if there is 3 '10's in a[], foobar[10] = 3, O(N)
The tricky third loop:
Outer loop, without doubt, O(M)
Inner loop, you have to consider the total(maximum) # of time that j can be increased: which is Sum(foobar[]) = sizeof(a) = N, so indeed this loop, throughout whole outer loop iteration, is bound to be executed N times at most. So both loops as a whole is O(N+M), instead of intuitively O(NM)
So total complexity is: O(N) + O(M) + O(N+M) = O(N+M).
A tips if you found the third loop complexity is hard to understand, think in a way like this:
It is a 0-sum game. If some foobar[z] is large, then there is many foobar[j] = 0 which almost means skip the inner loop for such i. Otherwise, all foobar[j] will be about at average size.
It is hard to analysis per iteration of i, but it is easy to analysis the inner loop as a whole, as we know the total sum of foobar[] is a constant.

This looks like a counting sort implementation. Considering that N (number of items) is the length of array a, and M (largest possible item) is b, its time complexity is O(N+M).

Related

What will be the execution time in Big O notation in fnB?

I got 2 functions and I need to find the execution time for both functions in Big O, however I am confuse on fnB
int fnA(int n){
int sum = 0;
for(int i=0; i<n; i++){
for(int j=n; i<j; j=j-2){
sum += i*j;
}
return sum;
}
I got O(n^2) for fnA
int fnB(int n) {
int sum =0;
for(int size = 1; size<n; size=2*size){
sum+=fnA(size);
}
return sum;
}
Since within the for loop in fnB, size increase exponentially. I am leaning fnB has an O(n^3). Am I correct, if not please correct me, thank you
fnA has a running time of O(n2).
However, fnB has a running time of O(n2logn), since it has log2n iterations, and each iteration takes O(n2) time (it actually takes O(size2), but since size < n, we can bound it with O(n2)).
A more detailed explanation:
fnA(n) has n iterations in the outer loop and at most n/2 iterations in the inner loop, which gives O(n2) upper bound. Since each iteration of fnB(n) calls fnA(size), it takes O(size2) == O(n2) (since size < n).
Now, the loop of fnB(n) assigns the following values to size : 20, 21, 22, ..., 2k where 2k <= n. Therefore the number of iterations is k <= log2n, and the upper bound of fnB is O(n2log2n).
Big-O notation can be used to represent time-complexity or space-complexity of an algorithm.
In your program, the function fnA can be thought of having a time complexity of O(n^2) because it has two nested for loops. However, due to the condition i<j which always evaluates to false, the inner for loop would never execute. So the more realistic time-complexity of your fnA function is O(n).
Your fnB function calls fnA in a single for loop. Therefore, its time complexity is O(n^2).

when would a statement inside a loop affect the complexity?

for example, if I have
for(int i = 1; i < 500; i++){
for (int j = 0; j < N; j++){
if (array[j] == someNumber && i == someNumber)
counter++;
}
}
if all numbers were the same in the array, hence counter would increase for each of them, would this make the complexity O(2N), O(N squared) or still just O(N), where it never became O(2N)? I hope the question makes sense. I find it hard to understand how statements might affect the complexity.
Since the statements inside your loop take constant time, they don't have any impact on the overall complexity of your algorithm. The complexity of the code you posted is just O(N), since your outer loop is executed a constant number of times, and your inner loop executes N times.
An example of when it would matter would be if you did something inside a loop like call a function that searches for a value in an array, or sorts a list. Since those operations depend on the size of the array (or list), you'd have to take them into account when calculating the complexity of your algorithm.

Complexity of sorting algorithm

Here is a sorting algorithm, not a clever one. In this version, it works well when elements are non-negative and occur at most once. I'm confused about its time complexity. Is it O(n)? So is it better than quick sort in terms of that notation? Thanks. Here is the code:
public int[] stupidSort( int[] array ){
// Variables
int max = array[0];
int index = 0;
int[] lastArray = new int[array.length];
// Find max element in input array
for( int i = 0; i < array.length; i++ ){
if ( array[i] > max )
max = array[i];
}
// Create a new array. In this array, element n will represent number of n's in input array
int[] newArray = new int[max + 1];
for ( int j = 0; j < array.length; j++ )
newArray[array[j]]++;
// If element is bigger than 0, it means that number occured in input. So put it in output array
for( int k = 0; k < newArray.length; k++ ){
if( newArray[k] > 0 )
lastArray[index++] = k;
}
return lastArray;
}
What you wrote is the counting sort, and it has O(n) complexity indeed. However, it cannot be compared to QuickSort because QuickSort is an algorithm based on comparisons. These 2 algorithms belong to different categories (yours is a non-comparison, quicksort is a comparison algorithm). Your algorithm (counting sort) makes the assumption that the range of numbers in the array is known and that all numbers are integer, whereas QuickSort works for every number.
You can learn more for sorting algorithms here. In that link you can see the complexity for sorting algorithms divided in the 2 categories: comparison and non-comparison.
EDIT
As Paul Hankin pointed out the complexity isn't always O(n). It is O(n+k) where k is the max of the input array. Quoted below is the time complexity as explained in the wikipedia article for the counting sort:
Because the algorithm uses only simple for loops, without recursion or subroutine calls, it is straightforward to analyze. The initialization of the count array, and the second for loop which performs a prefix sum on the count array, each iterate at most k + 1 times and therefore take O(k) time. The other two for loops, and the initialization of the output array, each take O(n) time. Therefore, the time for the whole algorithm is the sum of the times for these steps, O(n + k).
The given algorithm is very much similar to Count sort. Whereas QuickSort is a comparison model based sorting algorithm. Only in the worst case QuickSort gives O(n^2) time complexity, otherwise it is O(nlogn). Also the QuickSort is used with it's randomized version that is pivot is selected randomly and hence the worst-case is avoided most often this way.
Edit: Correction as pointed by paulhankin in comment complexity=O(n+k):
The code you have put forth is using counting based sort, that is count sort and your code's time complexity is O(n+k). But what you must realize is that this algorithm is dependent on the range of the input and that range could be anything. Furthermore this algorithm is not InPlace but Stable. In many cases the data you want to sort is not only integer rather the data can be anything with a key that is required to be sorted with the help of the key. If Stable algorithm is not used than in such a case sorting can be problematic.
Just in case if someone does not know:
InPlace Algorithm: Is the one in which additional space required is not dependent on the given input.
Stable Algorithm: Is the one in which for example if there were two 5's in the data set before sorting, the 5 that came first before sorting comes first than the second even after the sorting.
EDIT: (Regarding aladinsane7's comment): Yes the formal version of countSort does handle this aspect also. It would be good if you have a look at CountSort. And its time complexity is O(n+k). Where K accounts for the range of data and n is complexity for remaining algorithm.

I believe this algorithm is O(N). Am I correct?

This algorithm reverses an array of N integers. I believe this algorithm is O(N) because for each loop iteration, the four lines of code are executed once thus completing the job in 4N time.
public static void reverseTheNumbers(int[] list) {
for (int i = 0; i < list.length / 2; i++) {
int j = list.length - 1 - i;
int temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}
There isn't such a thing as 4N time. The algorithm is linear because as you increase the size of the input the runtime of the algorithm increases proportionally. In other words if you doubled the size of list you would expect the algorithm to take twice as long.
It doesn't matter how many operations you do inside your loop - as long as they are each constant time (relative to the input) the runtime of the loop is determined simply by the number of iterations.
Put another way, these four statements are - all together - an O(1) operation.
int j = list.length - 1 - i;
int temp = list[i];
list[i] = list[j];
list[j] = temp;
There's nothing significant about the fact that this sequence of steps is expressed in four statements in Java syntax - experimenting with javap suggests these four lines compiles into ~20 bytecode commands, and who knows how many processor instructions that bytecode gets converted into. The good news is Big-O notation works the same regardless of the particular syntax - a sequence of operations is O(1) or constant time if its execution time is the same regardless of the input.
Therefore you're doing an O(1) operation N times; aka O(N).
Yes, you are correct. The number of operations is linearly dependent on the size of the array (N), making it an O(N) algorithm.
Yes, the complexity of the algorithm is O(n).
However, the exact "time" (because there are no constant factors in asymptotic complexity, check comment below) is not 4 times the size of the array, we could say it is 1/2*(c1+c2+c3+c3) times the size of the array, where 1/2 corresponds to each loop iteration and each c corresponds to the time needed for each operation inside theloop.
It would be 4 times the size of the array, if the algorithm was iterating the whole array 4 times.

repeated element in Array

I have an array of N elements and contain 1 to (N-1) integers-a sequence of integers starting from 1 to the max number N-1-, meaning that there is only one number is repeated, and I want to write an algorithm that return this repeated element, I have found a solution but it only could work if the array is sorted, which is may not be the case.
?
int i=0;
while(i<A[i])
{
i++
}
int rep = A[i];
I do not know why RC removed his comment but his idea was good.
With the knowledge of N you easy can calculate that the sum of [1:N-1]. then sum up all elementes in your array and subtract the above sum and you have your number.
This comes at the cost of O(n) and is not beatable.
However this only works with the preconditions you mentioned.
A more generic approach would be to sort the array and then simply walk through it. This would be O(n log(n)) and still better than your O(n²).
I you know the maximum number you may create a lookup table and init it with all zeros, walk through the array and check for one and mark the entries with one. The complexity is also just O(n) but at the expense of memory.
if the value range is unknown a simiar approach can be used but instead of using a lookup table a hashset canbe used.
Linear search will help you with complexity O(n):
final int n = ...;
final int a[] = createInput(n); // Expect each a[i] < n && a[i] >= 0
final int b[] = new int[n];
for (int i = 0; i < n; i++)
b[i]++;
for (int i = 0; i < n; i++)
if (b[i] >= 2)
return a[i];
throw new IllegalArgumentException("No duplicates found");
A possible solution is to sum all elements in the array and then to compute the sym of the integers up to N-1. After that subtract the two values and voila - you found your number. This is the solution proposed by vlad_tepesch and it is good, but has a drawback - you may overflow the integer type. To avoid this you can use 64 bit integer.
However I want to propose a slight modification - compute the xor sum of the integers up to N-1(that is compute 1^2^3^...(N-1)) and compute the xor sum of your array(i.e. a0^a1^...aN-1). After that xor the two values and the result will be the repeated element.

Categories