Time complexity for a recursion inside for loop java - java

Hello i encountered this problem. I try to solve this for three days now with the help of several stack overflow posts, google and youtube but i can´t solve this.
My Question is. How do i get T(N) = ? for perm(1). I guess it is somehow O(n^2) or even worse because of the following. My for-loop depends on a variable n and my recursion inside takes n+1 so this would be n*(n+1) ~ n^2. But how do i get through this program and proove this? I know i can ignore all constant factors like addition etc but i would be nice if someone takes the time to explain every time-unit-cost what ever in the code and sum it up until we have a recursiv equation.
To get every single permutation we change perm(1) to perm(0).
1) How many calls do we have if we permute n-Numbers
2) How many calls are being omitted in average for a single permutation if n gets very big.
Explanation. We give this program n-Numbers to permute. If we want to permute the 0 as well, we call perm(0), else we call perm(1).
private void perm(int i) { // permute from index i; 1 or 0(all permuts).
if (i >= max) put(); // one permutation finished, max = n-1
else {
for (int j = i; j <= max; j++) { // everyone in front
swap(i, j); // swap
perm(i + 1); // and recursion
}
int h = a[i]; // restore Array
System.arraycopy(a, i + 1, a, i, max - i); // shift left
a[max] = h;
}
} // end perm
private void swap(int i, int j) { // swap a[i] <-> a[j]
if (i != j) {
int h = a[i];
a[i] = a[j];
a[j] = h;
}
} // end swap
private synchronized void put() { // give over to array
mayread = true; // read a (array)
notify(); // notify the other Thread
try {
if (a != null)
while (mayread) wait(); // non busy waiting
} catch (InterruptedException e) {
}
} // end put
And my final question. What the heck happens when we are inside the for-loop and we call swap(1,1), as j=i, or swap(2,2) and after that the recursion.

swap is O(1).
perm executes a loop of max-i+1 iterations, then, on each iteration executes perm(i+1). Then, after all, it does an arraycopy of max-i items.
Let's call max as n.
perm(1) executes the loop n times
perm(2) executes the loop n-1 times
perm(3) executes the loop n-2 times
and so on...
This leads to a n*(n-1)*(n-2)*...*1 iterations. O(n!)
Also, swap(1, 1) does nothing.

Related

Not understanding big O notation O(∑ i=0 n−1 (∑ j=i+1 n (j−i)))=O(∑ i=0 n−1 2 (1+n−i)(n−i))=O(n^3)

Working on the following problem:
Given a string s, find the length of the longest substring without repeating characters.
I'm using this brute force solution:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if (checkRepetition(s, i, j)) {
res = Math.max(res, j - i + 1);
}
}
}
return res;
}
private boolean checkRepetition(String s, int start, int end) {
int[] chars = new int[128];
for (int i = start; i <= end; i++) {
char c = s.charAt(i);
chars[c]++;
if (chars[c] > 1) {
return false;
}
}
return true;
}
}
Tbe big O notation is as follows:
I understand that three nested iterations would result in a time complexity O(n^3).
I only see two sigma operators being used on the start of the formula, could someone enlighten me on where the third iteration comes to play in the beginning of the formula?
The first sum from i=0 to n-1 corresponds to the outer for loop of lengthOfLongestSubstring, which you can see iterates from i=0 to n-1.
The second sum from j = i+1 to n corresponds to the second for loop (you could be starting j at i+1 rather than i as there's no need to check length 0 sub-strings).
Generally, we would expect this particular double for loop structure to produce O(n^2) algorithms and a third for loop (from k=j+1 to n) to lead to O(n^3) ones. However, this general rule (k for loops iterating through all k-tuples of indices producing O(n^k) algorithms) is only the case when the work done inside the innermost for loop is constant. This is because having k for loops structured in this way produces O(n^k) total iterations, but you need to multiply the total number of iterations by the work done in each iteration to get the overall complexity.
From this idea, we can see that the reason lengthOfLongestSubstring is O(n^3) is because the work done inside of the body of the second for loop is not constant, but rather is O(n). checkRepitition(s, i, j) iterates from i to j, taking j-i time (hence the expression inside the second term of the sum). O(j-i) time is O(n) time in the worst case because i could be as low as 0, j as high as n, and of course O(n-0) = O(n) (it's not too hard to show that checkRepitions is O(n) in the average case as well).
As mentioned by a commenter, having a linear operation inside the body of your second for loop has the same practical effect in terms of complexity as having a third for loop, which would probably be easier to see as being O(n^3) (you could even imagine the function definition for checkRepitition, including its for loop, being pasted into lengthOfLongestSubstring in place to see the same result). But the basic idea is that doing O(n) work for each of the O(n^2) iterations of the 2 for loops means the total complexity is O(n)*O(n^2) = O(n^3).

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.

How can i speed up my following algorithm for majority element problemset?

So, I have to write an algorithm for my Coursera assignment in Data Structures. I have used Java for the following problem set.
Problem:- So, lets consider a sequence of numbers in an array of say 5 elements.
Number of elements - 5
Array elements - 2, 2 3, 9, 2
The majority element algorithm states that if an element appears more than n/2 times then it is the majority element in the array. Hence, my program should output 1(indicates that a majority element found), 0 (no majority element found).
As per the above question- 2 appears 3 times in the array which means n/2 times more (5/2 = 2(integer,ignoring the decimal) + 1 = 3)
So, I was asked to write an algorithm that could solve this problem. The options were divide and conquer (i.e. breaking the array into two halves and looking for majority element in both halves and then getting the answer)
Another option was to scan the elements in the array using two for loop and finally getting the majority element. This is what i tried. I ran through the grader but my program exceeds the time limit. Can anyone suggest any suggestions. Thank You.!
Java Code:-
import java.util.*;
import java.io.*;
public class MajorityElement {
private static int getMajorityElement(int[] a, int left, int right) {
int count = 1;
int num = a.length/2 + 1;
Arrays.sort(a);
if (left == right) {
return -1;
}
if (left + 1 == right) {
return a[left];
}
else
{
for(int i=0;i<a.length;i++)
{
for(int j=i+1;j<a.length;j++)
{
if(a[i]==a[j])
{
count++;
}
}
if(count>1)
{
if(count>=num)
{
return 1;
}
i = i + count-1;
count = 1;
}
}
return -1;
}
}
public static void main(String[] args) {
FastScanner scanner = new FastScanner(System.in);
int n = scanner.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
}
if (getMajorityElement(a, 0, a.length) != -1) {
System.out.println(1);
} else {
System.out.println(0);
}
}
static class FastScanner {
BufferedReader br;
StringTokenizer st;
FastScanner(InputStream stream) {
try {
br = new BufferedReader(new InputStreamReader(stream));
} catch (Exception e) {
e.printStackTrace();
}
}
String next() {
while (st == null || !st.hasMoreTokens()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
int nextInt() {
return Integer.parseInt(next());
}
}
}
The two-for-loop approach is pretty much just this:
for (int x: a) {
int count = 0;
for (int y: a) {
if (x == y) {
count++;
if (count > a.length/2) {
return true;
}
}
}
}
return false;
That will certainly take too long in cases where there is no majority element, since it will require n^2 comparisons, where n is the number of elements in the list. Don't do that. You can sort first, like a commenter on your question said, which will allow you to break out a little early, but you still have the overhead of sorting, followed by some scanning. That would look something like (NOT TESTED, since it's for you to write):
Arrays.sort(a); // actually I hate this because it mutates your array (BAD!)
for (int i = 0; i < a.length; i++) {
int count = 0;
for (int j = i; i < j.length; j++) {
if (a[j] == a[i]) {
count++;
if (count > a.length / 2) {
return true;
}
} else if (a[j] > a[i]) {
break; // no more to count
}
}
}
return false;
You might instead want to go with the divide and conquer approach (n log n). There are also O(n) algorithms, including one by J. Moore, which goes like this:
count = 0
for (int x: a) {
if (count == 0) {
candidate = x;
}
if (x == candidate) {
count += 1
} else {
count -= 1
}
}
count = 0;
for (int x: a) if (a==candidate) count++;
return count > a.length / 2;
Treat the above as pseudo code, as it is not tested.
More information on majority element here but it's all in Python so it might not help.
Both of your options do not sound good to me. The problem you described is a standard problem in streaming algorithms (where you have a huge (potentially infinite) stream of data) and you have to calculate some statistics from this stream, passing through this stream once.
It can be solved using Boyer–Moore majority vote algorithm. I do not know Java, but here is my explanation and few lines of python code, which you can surely convert to Java.
The majority element is the element that occurs more than half of the size of the array. This means that the majority element occurs more than all other elements combined or if you count the number of times, majority element appears, and subtract the number of all other elements, you will get a positive number.
So if you count the number of some element, and subtract the number of all other elements and get the number 0 - then your original element can't be a majority element. This if the basis for a correct algorithm:
Have two variables, counter and possible element. Iterate the stream, if the counter is 0 - your overwrite the possible element and initialize the counter, if the number is the same as possible element - increase the counter, otherwise decrease it. Python code:
def majority_element(arr):
counter, possible_element = 0, None
for i in arr:
if counter == 0:
possible_element, counter = i, 1
elif i == possible_element:
counter += 1
else:
counter -= 1
return possible_element
It is clear to see that the algorithm is O(n) with a very small constant before O(n) (like 3). Also it looks like the space complexity is O(1), because we have only three variable initialized. The problem is that one of these variables is a counter which potentially can grow up to n (when the array consists of the same numbers). And to store the number n you need O(log (n)) space. So from theoretical point of view it is O(n) time and O(log(n)) space. From practical, you can fit 2^128 number in a longint and this number of elements in the array is unimaginably huge.
Also note that the algorithm works only if there is a majority element. If such element does not exist it will still return some number, which will surely be wrong. (it is easy to modify the algorithm to tell whether the majority element exists)

Stack Overflow Error in java with small input set

I am trying to write quick sort in java. But getting a stack over flow error for very small set of inputs. In the createArray function I am taking input by the Scanner object.
Please somebody help me in this.
public class quickSort {
static int[] ar;
int number;
public static void main(String args[]) {
CreatingArray ca = new CreatingArray();
ar = ca.createArray();
ca.printArray(ar);
int len = ar.length;
sort(0,(len-1));
System.out.println("");
System.out.println("Array after QuickSort:");
ca.printArray(ar);
}
public static void sort(int l,int h) {
int i=l;
int j=h;
int temp =0;
int pivot = ar[(l + (l+h)/2)];
while(i <= j) {
while(ar[i] < pivot) {
i++;
}
while(ar[j] > pivot) {
j--;
}
if (i<=j) {
temp = ar[i];
ar[i] = ar[j];
ar[j] = temp;
i++;
j--;
}
}
if(l<j){
sort(l,j);
}
if(i<h) {
sort(i,h);
}
}
}
int pivot = ar[(l + (l+h)/2)];
This line is wrong. It only gets the center point when l == 0. If, say, l == 4 && h == 7 (e.g. the upper half of an 8-element array), you get 4 + (4+7)/2 which is 9 and thus outside the bounds. You really want l + (h-l+1)/2.
The first thing that can happen because of this is ArrayIndexOutOfBoundsException, but you never get that because you always recurse on the lower partition first and run into the second problem. Swap the two ifs at the end of the function to see this in action.
The second thing that can happen is that, because pivot is not actually an element in the range [i, j], the element search at the start of the loop can go crazy. In particular, pivot could be a very small value that is smaller than any in the range. The i search will terminate immediately (leaving i == l the way it started), while the j search will run way beyond i, which means the if won't be entered either, i still doesn't change, and the main loop terminates. Because i is unchanged, i<h is still true (assuming l<h was), and you enter the recursive call with exactly the same arguments you just had, which means the next call will do exactly the same thing as the current one, ending in infinite recursion.
I think your recursive calls don't end... debug your code with very small input - say 3 numbers... check when and how sort() is being called..

Where is the mistake? Finding majority

I want to find the majority in array (number that appears most of the time).
I have a sorted array and use these cycles:
for(int k = 1;k < length;k++)
{
if(arr[k-1] == arr[k])
{
count++;
if(count > max)
{
max = count;
maxnum = arr[k-1];
}
} else {
count = 0;
}
}
or
for(int h=0;h<length;h++)
{
for(int l=1;l<length;l++)
{
if(arr[h] == arr[l])
{
count++;
if(count > max)
{
max = count;
maxnum = arr[h];
}
} else count = 0;
}
}
they are similiar. When i try them on small arrays everything seems to be ok. But on a long run array with N elements 0<=N<=500000, each element K 0<=K<=10^9 they give wrong answers.
Here is solution with mistake http://ideone.com/y2gvnX. I know there are better algos to find majority but i just need to know where is my mistake.
I really can't find it :( Will really appreciate help!
First of all, you should use the first algorithm, as your array is sorted. 2nd algorithm runs through the array twice unnecessarily.
Now your first algorithm is almost correct, but it has two problems: -
The first problem is you are setting count = 0, in else part,
rather it should be set to 1. Because every element comes at least
once.
Secondly, you don't need to set max every time in your if. Just
increment count, till the if-condition is satisfied, and as soon
as condition fails, check for the current count with current
max, and reset the current max accordingly.
This way, your max will not be checked on every iteration, but only when a mismatch is found.
So, you can try out this code: -
// initialize `count = 1`, and `maxnum = Integer.MIN_VALUE`.
int count = 1;
int max = 0;
int maxnum = Integer.MIN_VALUE;
for(int k = 1;k < length;k++)
{
if(arr[k-1] == arr[k]) {
count++; // Keep on increasing count till elements are equal
} else {
// if Condition fails, check for the current count v/s current max
if (max < count) { // Move this from `if` to `else`
max = count;
maxnum = arr[k - 1];
}
count = 1; // Reset count to 1. As every value comes at least once.
}
}
Note : -
The problem with this approach is, if two numbers say - 1 and 3, comes equal number of times - which is max, then the max count will be counted for 3 (assuming that 3 comes after 1, and maxnum will contain 3 and ignore 1. But they both should be considered.
So, basically, you cannot use a for loop and maintain a count to take care of this problem.
A better way is to create a Map<Integer, Integer>, and store the count of each value in there. And then later on sort that Map on value.
Your first algorithm looks correct to me. The second one (which is what your linked code uses) needs some initialization each time through the loop. Also, the inner loop does not need to start at 1 each time; it can start at h + 1:
for(int h=0; h<length; h++)
{
count = 1; // for the element at arr[h]
for(int l=h + 1; l<length; l++)
{
if(arr[h] == arr[l])
{
count++;
}
}
if(count > max)
{
max = count;
maxnum = arr[h];
}
}
The first algorithm is much better for sorted arrays. Even for unsorted arrays, it would be cheaper to sort the array (or a copy of it) and then use the first algorithm rather than use the second.
Note that if there are ties (such as for the array [1, 1, 2, 2, 3] as per #Rohit's comment), this will find the first value (in the sort order) that has the maximum count.
The error I can readily see is that if all elements are distinct, then the max at end is 0.
However it has to be 1.
So when you update count in "else" case, update it to 1 instead of 0, as a new element has been discovered, and its count is 1.
Your first algorithm only makes sense if the array is sorted.
Your second algorithm just sets count to zero in the wrong place. You want to set count to zero before you enter the inner for loop.
for(int h=0;h<length;h++)
{
count = 0;
for(int l=0;l<length;l++)
{
if(arr[h] == arr[l])
{
count++;
if(count > max)
{
max = count;
maxnum = arr[h];
}
}
}
}
Also, you don't need to check count each time in the inner loop.
max = 0;
for(int h=0;h<length;h++)
{
count = 0;
for(int l=0;l<length;l++)
{
if(arr[h] == arr[l])
count++;
}
if(count > max)
{
max = count;
maxnum = arr[h];
}
}

Categories