Determining the time complexity of Java code - java

I am working on a Java practice interview problem for finding the number of Contiguous Subarrays. I have a working solution, and just want to make sure I understand the time complexity of my answer:
//solution is called with array of n length like this:
countSubarrays([3,4,1,6,2])
int[] countSubarrays(int[] arr) {
System.out.println(Arrays.toString(arr));
int[] ret = new int[arr.length];
//for each index:
for(int x = 0; x < arr.length; x++){
int max = arr[x];
System.out.println("arr["+x+"]="+max);
//try going forward
List<Integer> forwardList = new ArrayList<Integer>();
for(int y = x; y < arr.length; y++){
if(arr[y] <= max){
forwardList.add(arr[y]);
}else{
break;
}
}
System.out.println("forwardList="+forwardList);
//try going backwards
List<Integer> backwardList = new ArrayList<Integer>();
for(int y = x; y >= 0; y--){
if(arr[y] <= max){
//add to start of list
backwardList.add(0, arr[y]);
}else{
break;
}
}
System.out.println("backwardList="+backwardList);
//count number of contiguous subarrays
int count = (forwardList.size() + backwardList.size()) -1;
ret[x]=count;
System.out.println("count: "+count+"\n");
}
return ret;
}
If the input array is of n length, and my code solution features a for loop counting from 0 to n ( int x ), as well as two nested for loops counting forward and backwards until a larger int is found, would this mean my functions time complexity is O(n^2)?
I came to this solution when thinking that in the worst case scenario, my function would be going backwards and forwards the entire length of the array, but this would only happen once inside my int x for loop, so I wasn't sure if the time complexity was O(n) linear time or O(n^2) n squared.
Thanks

The simplified time complexity of this code would be O(n^2), where n is the size of the array. This is because you are iterating through 2 * (1 + 2 + ... + n) times. (There is a 2 because you have 2 for loops inside the first one). This would be O(2n(n-1)/2) = O(n*(n-1)), which simplifies to O(n^2).

Related

How can I utilize merge sort approach to reduce running time of my program

I have a function that takes in an int[] array with the expectation that all elements are sorted from largest to smallest.
I want to find how many pairs violate this expectation ( a larger element x in the array is present AFTER a smaller element x + array.length() while not going out of bounds)
For instance,
if array = [7,3,5,4,1] the answer would be 2 because the pairs that violate the order are 3 and 5 and 3 and 4, since 3 is smaller than them and should've been ahead of them.
Here is my current code:
public static int countBad(int[] array){
int counter = 0;
for (int x = 0; x < array.length; x++){
for (int y = x + 1; y <= x + array.length && y < array.length; y++){
if(array[x] < array[x+y]){
counter++;
}
}
}
return counter;
}
Even though my code works, it is extremely inefficient, which is why I wish to reduce the time complexity. I've been told about a merge sort approach to this issue. However, I'm struggling to grasp how to convert my existing method into a merge sort approach.
Can anyone please help explain how to do this?

How can there be two nested loops in this function and still have O(n) complexity?

Here is the solution to the problem of finding the minimum size of a subarray which has a sum greater than or equal to given number S:
public static int findMinSubArray(int S, int[] arr) {
int windowSum = 0, minLength = Integer.MAX_VALUE;
int windowStart = 0;
for (int windowEnd = 0; windowEnd < arr.length; windowEnd++) {
windowSum += arr[windowEnd]; // add the next element
// shrink the window as small as possible until the 'windowSum' is smaller than 'S'
while (windowSum >= S) {
minLength = Math.min(minLength, windowEnd - windowStart + 1);
windowSum -= arr[windowStart]; // subtract the element going out
windowStart++; // slide the window ahead
}
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
How is this O(n) but not O(n^2) or O(windowLength*n)?
You'll never visit an element more than twice, once when it gets pushed onto the window and again when it leaves the window. That's two passes over arr; O(2n) == O(n).
Nested loops don't mean complexity is automatically quadratic. You have to look at the bound on the work being done. In this case, the inner loop only makes at most n total steps forward, not n steps for every element as is the case with a traditional doubly nested for loop arrangement.

Finding big O recursion

I'm trying to figure out what is Big O and big Omega from the following piece of code down below.
This code inputs an array of ints, and sorts them in ascending order.
The worst case would be all in descending order {5,4,3,2,1}
,and the best case would be ascending order {1,2,3,4,5}.
static int counter = 0;
static int counter1 = 0;
static int counter2 = 0;
public static int[] MyAlgorithm(int[]a) {
int n = a.length;
boolean done = true;
int j = 0;
while(j<=n-2) {
counter++;
if(a[j]>a[j+1]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
done = false;
}
j = j+1;
}
j = n-1;
while(j>=1) {
counter1++;
if(a[j]<a[j-1]) {
int temp = a[j-1];
a[j-1] = a[j];
a[j] = temp;
done = false;
}
j = j-1;
}
if(!done) {
counter2++;
MyAlgorithm(a);
}
return a;
}
Worst case for each while loop i got was n-1, and for the recursion it was n/2.
Best case is n-1 while loops, and zero recursion
So my big Omega is (n) ( no recursion )
but for Big O, here is the confusing part for me, since there are n/2 recursion calls, does this mean i do N X N (because of n/2 recursion) big O (n^2)? or does it stay big O(n)???
As you said the Omega is Omega(n). In case all numbers in the array a are already in sorted order the code iterates over the array twice, once per while loop. This are n steps O(1) times.
In the worst case you are correct in assuming O(n^2). As you saw, an array sorted in reverse order produces such a worst case scenario. We can also produce a worst case scenario by having a sorted array in increasing order and then only swap the first and last number. Then each run of MyAlgorithm moves that last/first number two positions. After n/2 steps (runs of MyAlgorithm) the numbers reach their final position. Hence, O(n/2 * n) = O(n^2).
Small side note, sorting in general is in O(n log n), so you can sort something only under some circumstances in O(n).

Finding smallest element in an integer array in java using divide and conquor algorithm

I tried to find the smallest element in an integer array using what i understood about divide and conquor algorithm.
I am getting correct results.
But i am not sure if it is a conventional way of using divide and conquor algorithm.
If there is any other smarter way of implementing divide and conquor algorithm than what i have tried then please let me know it.
public static int smallest(int[] array){
int i = 0;
int array1[] = new int[array.length/2];
int array2[] = new int[array.length - (array.length/2)];
for(int index = 0; index < array.length/2 ; index++){
array1[index] = array[index];
}
for(int index = array.length/2; index < array.length; index++){
array2[i] = array[index];
i++;
}
if(array.length > 1){
if(smallest(array1) < smallest(array2)){
return smallest(array1);
}else{
return smallest(array2);
}
}
return array[0];
}
Your code is correct, but You can write less code using existing functions like Arrays.copyOfRange and Math.min
public static int smallest(int[] array) {
if (array.length == 1) {
return array[0];
}
int array1[] = Arrays.copyOfRange(array, 0, array.length / 2);
int array2[] = Arrays.copyOfRange(array, array.length / 2, array.length);
return Math.min(smallest(array1), smallest(array2));
}
Another point. Testing for the length == 1 at the beginning is more readable version. Functionally it is identical. From a performance point of view it creates less arrays, exiting as soon as possible from the smallest function.
It is also possible to use a different form of recursion where it is not necessary to create new arrays.
private static int smallest(int[] array, int from, int to) {
if (from == to) {
return array[from];
}
int middle = from + (to - from) / 2;
return Math.min(smallest(array, from, middle), smallest(array, middle + 1, to));
}
public static int smallest(int[] array){
return smallest(array, 0, array.length - 1);
}
This second version is more efficient because it doesn't creates new arrays.
I don't find any use in using a divide and conquer in this paticular program.
Anyhow you search for the whole array from 1 to N, but in two steps
1. 1 to N / 2
2. N / 2 + 1 to N
This is equivalent to 1 to N.
Also you program check for few additional checks after the loops which aren't actually required when you do it directly.
int min = a[0];
for(int i = 1; i < arr.length; i++)
if(min < a[i])
a[i] = min;
This is considered most efficient in finding out the minimum value.
When do I use divide and conquer
A divide and conquer algorithm works by recursively breaking down a problem into two or more sub-problems, until these become simple enough to be solved directly.
Consider the Merge Sort Algorithm.
Here, we divide the problem step by step untill we get smaller problem and then we combine them to sort them. In this case this is considered optimal. The normal runs in a O(n * n) and this runs in O(n log n).
But in finding the minimum the original has O(n). So this is good.
Divide And Conquer
The book
Data Structures and Algorithm Analysis in Java, 2nd edtition, Mark Allen Weiss
Says that a D&C algorithm should have two disjoint recursive calls. I.e like QuickSort. The above algorithm does not have this, even if it can be implemented recursively.
What you did here with code is correct. But there are more efficient ways of solving this code, of which i'm sure you're aware of.
Although divide and conquer algorithm can be applied to this problem, but it is more suited for complex data problem or to understand a difficult data problem by dividing it into smaller fragments. One prime example would be 'Tower of Hanoi'.
As far as your code is concerned, it is correct. Here's another copy of same code-
public class SmallestInteger {
public static void main(String[] args) {
int small ;
int array[] = {4,-2,8,3,56,34,67,84} ;
small = smallest(array) ;
System.out.println("The smallest integers is = " + small) ;
}
public static int smallest(int[] array) {
int array1[] = new int[array.length/2];
int array2[] = new int[array.length - (array.length/2)];
for (int index = 0; index < array.length/2 ; index++) {
array1[index] = array[index];
}
for (int index = array.length/2; index < array.length; index++) {
array2[index - array.length/2] = array[index] ;
}
if (array.length > 1) {
if(smallest(array1) < smallest(array2)) {
return smallest(array1) ;
}
else {
return smallest(array2) ;
}
}
return array[0] ;
}
}
Result came out to be-
The smallest integers is = -2

Generating Random Permutation Uniformly in Java

Anyone know of a fast/the fastest way to generate a random permutation of a list of integers in Java. For example if I want a random permutation of length five an answer would be 1 5 4 2 3, where each of the 5! possibilities is equally likely.
My thoughts on how to tackle this are to run a method which generates random real numbers in an array of desired length and then sorts them returning the index i.e. 0.712 0.314 0.42 0.69 0.1 would return a permutation of 5 2 3 4 1. I think this is possible to run in O(n^2) and at the moment my code is running in approximately O(n^3) and is a large proportion of the running time of my program at the moment. Theoretically this seems OK but I'm not sure about it in practice.
Have you tried the following?
Collections.shuffle(list)
This iterates through each element, swapping that element with a random remaining element. This has a O(n) time complexity.
If the purpose is just to generate a random permutation, I don't really understand the need for sorting. The following code runs in linear time as far as I can tell
public static int[] getRandomPermutation (int length){
// initialize array and fill it with {0,1,2...}
int[] array = new int[length];
for(int i = 0; i < array.length; i++)
array[i] = i;
for(int i = 0; i < length; i++){
// randomly chosen position in array whose element
// will be swapped with the element in position i
// note that when i = 0, any position can chosen (0 thru length-1)
// when i = 1, only positions 1 through length -1
// NOTE: r is an instance of java.util.Random
int ran = i + r.nextInt (length-i);
// perform swap
int temp = array[i];
array[i] = array[ran];
array[ran] = temp;
}
return array;
}
And here is some code to test it:
public static void testGetRandomPermutation () {
int length =4; // length of arrays to construct
// This code tests the DISTRIBUTIONAL PROPERTIES
ArrayList<Integer> counts = new ArrayList <Integer> (); // filled with Integer
ArrayList<int[]> arrays = new ArrayList <int[]> (); // filled with int[]
int T = 1000000; // number of trials
for (int t = 0; t < T; t++) {
int[] perm = getRandomPermutation(length);
// System.out.println (getString (perm));
boolean matchFound = false;
for(int j = 0; j < arrays.size(); j++) {
if(equals(perm,arrays.get(j))) {
//System.out.println ("match found!");
matchFound = true;
// increment value of count in corresponding position of count list
counts.set(j, Integer.valueOf(counts.get(j).intValue()+1));
break;
}
}
if (!matchFound) {
arrays.add(perm);
counts.add(Integer.valueOf(1));
}
}
for(int i = 0; i < arrays.size(); i++){
System.out.println (getString (arrays.get (i)));
System.out.println ("frequency: " + counts.get (i).intValue ());
}
// Now let's test the speed
T = 500000; // trials per array length n
// n will the the length of the arrays
double[] times = new double[97];
for(int n = 3; n < 100; n++){
long beginTime = System.currentTimeMillis();
for(int t = 0; t < T; t++){
int[] perm = getRandomPermutation(n);
}
long endTime = System.currentTimeMillis();
times[n-3] = (double)(endTime-beginTime);
System.out.println("time to make "+T+" random permutations of length "+n+" : "+ (endTime-beginTime));
}
// Plotter.plot(new double[][]{times});
}
There is an O(n) Shuffle method that is easy to implement.
Just generate random number between 0 and n! - 1 and use
the algorithm I provided elsewhere (to generate permutation by its rank).

Categories