LeetCode Java - Frequency of the most frequent element - java

So I was trying to solve the LeetCode question n.1838 (Frequency of the most frequent element) and it works except for a test case with an input array of 500000 elements (1 <= nums.length <= 10^5): it gives me TimeLimitExceeded and I don't understand why.
I tried to explain my approach with comments in the code, but it's very easy: I sort the array first so I can start from the last element (the bigger one), I loop the array and for each element in the array i calculate the frequency for different cases:
if nums[i] == nums[i - 1] increment the frequency
if k < nums[i] - nums[i - 1] there's no need to do anything, so i just update j to get outside of the loop
else I add to nums[i - 1] the difference so it can be nums[i], I update q and num.
I then update the max frequency and once I'm outside the while loop I do the same for each element of the array.
Here's my method:
public int maxFrequency(int[] nums, int k) {
//corner case
if (nums.length == 1)
return 1;
//order array
Arrays.sort(nums);
int max = 0;
int freq = 0;
int num = 0; //will always be the int at the index - j (j will be incremented each time)
int j = 0;
//start from the last element of the array
for (int i = nums.length - 1; i >= 0; i--) {
num = nums[i - j];
int q = k;
while (q >= 0 && i - j >= 0) {
//if element equals itself, increment frequency
if (nums[i] - num == 0) {
freq++;
j++;
} // if k < nums[i] - num (aka even if we add k to num we won't have a new nums[i])
else if (nums[i] - num > q) {
j = i + 1; //to get out of the while loop
} else { //case where i can add k because k >= nums[i] - num
q -= nums[i] - num;
freq++;
j++;
} // update num only if i - j > 0 so we dont have ArrayOutOfBoundException
if (i - j >= 0) {
num = nums[i - j];
} //update max frequency
max = Math.max(freq, max);
}
freq = 0;
j = 0;
}
return max;
}
Any advice or hint would be helpful, thanks in advance.

Related

Difference between writing ++i in the condition of a while loop and incrementing i inside the while loop

I am writing the code as shown at the bottom of this question.
For the while loop part, I thought the following two codes are just the same
first one
while (lIndex < rIndex && height[++lIndex] <= left) {
ans += (left - height[lIndex]);
}
second one
while (lIndex < rIndex && height[lIndex + 1] <= left) {
ans += (left - height[lIndex + 1]);
lIndex++;
}
However, when I run the second one on system,
There is an error of Time Limit Exceeding.
Could someone explain the reason of this problem?
Thanks for reading my question.
Original code:
public int trap(int[] height) {
if (height.length < 3) return 0;
int ans = 0;
int lIndex = 0;
int rIndex = height.length - 1;
// Find the first wall on each side
while (lIndex < rIndex && height[lIndex] <= height[lIndex + 1]) lIndex++;
while (lIndex < rIndex && height[rIndex] <= height[rIndex - 1]) rIndex--;
while (lIndex < rIndex) {
int left = height[lIndex];
int right = height[rIndex];
if (left <= right) {
while (lIndex < rIndex && height[++lIndex] <= left) {
ans += (left - height[lIndex]);
}
}
else {
while (lIndex < rIndex && height[--rIndex] <= right) {
ans += right - height[rIndex];
}
}
}
return ans;
}
In the first example, lIndex is incremented even when evaluation of the condition ends up with it being false. In other words, if the while loop body is executed n times, lIndex will be incremented n + 1 times.
In the second example, lIndex is only incremented with the rest of the body. So if the while loop body is executed n times, lIndex will be incremented n times.
Here's a very simple example of both, showing the difference:
public class Test {
public static void main(String[] args) {
int i = 0;
while (++i < 3) {
System.out.println("First loop iteration");
}
System.out.println("Value of i afterwards: " + i);
int j = 0;
while (j + 1 < 3) {
System.out.println("Second loop iteration");
j++;
}
System.out.println("Value of j afterwards: " + j);
}
}
Output:
First loop iteration
First loop iteration
Value of i afterwards: 3
Second loop iteration
Second loop iteration
Value of j afterwards: 2
So both loops execute the body twice, but they end up with different counter values.

Find time complexity of the following algorithm - isBunkerArray()?

What is the time complexity of isBunkerArray() function below. Note that it is calling isPrime() function for every odd number in array till any odd number in array is followed by prime
static int isBunkerArray(int[] arr) {
int bunker = 0;
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] % 2 != 0) {
if (isPrime(arr[i + 1]) == 1) {
bunker = 1;
break;
}
}
}
return bunker;
}
private static int isPrime(int n) {
if (n < 2)
return 0;
for (int i = 2; i <= n / 2; i++) {
if (n % i == 0)
return 0;
}
return 1;
}
Suppose the value of each member of the array is W. The time complexity of isPrime is O(W). And in the worst case, it will be called for each member of the array with size n. Hence, isBunnkerArray is in O(W*n). Notice that, you can implement isPrime more efficiently in O(\sqrt{W}).

3Sum on Leetcode

I'm working on the 3sum problem. I did it by sorting the array in the beginning and then using two pointer method to find all the unique triplets in the array which gives the sum of zero.
My question is that if I comment out the two while loops at the end, the runtime can be improved from 32ms to 26ms. I get a 6 ms speed boost. I think the time complexity is still O(n^2). Does anyone know why commenting out the while loops is faster?
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new LinkedList<List<Integer>>();
if(nums.length < 3){
return result;
}
int left = 0;
int right = nums.length - 1;
int threeSum = 0;
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2 && nums[i] <= 0; i ++) {
right = nums.length - 1;
if (i > 0) {
while (i < right && nums[i - 1] == nums[i])
i ++;
}
left = i + 1;
while (left < right) {
threeSum = nums[i] + nums[left] + nums[right];
if (threeSum == 0) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
left ++;
right --;
while (left < right && nums[left - 1] == nums[left])
left ++;
while (left < right && nums[right + 1] == nums[right])
right --;
} else if (threeSum < 0) {
// move to the right
left ++;
// Skip duplicate numbers
// Comment this out get 6 ms speed increase
while (left < right && nums[left - 1] == nums[left])
left ++;
} else {
right --;
// Skip duplicate numbers
// Comment this out get 6 ms speed increase
while (left < right && nums[right + 1] == nums[right])
right --;
}
}
}
return result;
}

Binary search an element (with duplicate items) in a 2D array column-wise sorted?

EDIT:
For the 2D array, only column were sorted but not sorted for rows. Below is the method I wrote, first binary search each column, then go through every column and also count the duplicate element.But the code does not work for me. Does anyone can help me? Many thanks!
public static int count(int[][] array, int query) {
int count = 0;
for (int j = 0; j < array[0].length; j++) {
count += biSearch(array, query, j);
}
return count;
}
private static int biSearch(int[][] array, int searchItem, int row) {
// create a 1D array to hold the entries of 2D array's column
int[] column = new int[array.length];
int count = 0;
int low = 0;
int high = column.length - 1;
// put 2D array's column into 1D array
for (int i = 0; i < array.length; i++)
column[i] = array[i][row];
// binary search on column array
while (low < high) {
int mid = (low + high) / 2;
if ( column[mid]== searchItem ) {
while ((mid - 1) >= 0) {
if (column[mid - 1] == searchItem){
mid--;
count++;
}
}
while ((mid + 1) < (column.length - 1)) {
if (column[mid + 1] == searchItem){
mid++;
count++;
}
}
}
else if ( column[mid] > searchItem)
high = mid - 1;
else if (column[mid] <searchItem )
low = mid + 1;
}
return count;
}
Not sure if that's the only problem, but this code is wrong :
if ( column[mid]== searchItem ) {
while ((mid - 1) >= 0) {
if (column[mid - 1] == searchItem){
mid--;
count++;
}
}
while ((mid + 1) < (column.length - 1)) {
if (column[mid + 1] == searchItem){
mid++;
count++;
}
}
}
It basically tests all the elements of the column array from mid to 0, and than from 0 to column.length-1. Not only you pass over the entire array, which defeats the purpose of your binary search, you visit all the elements between mid and 0 twice, which means your count is wrong.
At better approach :
if ( column[mid]== searchItem ) {
count = 1;
int temp = mid;
while (temp > 0 && column[temp - 1] == searchItem) {
temp--;
count++;
}
temp = mid;
while (temp < column.length - 1 && column[temp + 1] == searchItem) {
temp++;
count++;
}
}
The two things to note :
I don't modify mid, since we need the original value of mid for the second while loop.
I quit each while loop when I encounter an element that is not equal to searchItem. Since the array is supposed to be sorted (otherwise binary serarch won't work), there's no need to iterate over the entire array.
P.S. the argument called row in your biSearch is actualy an index of a column of your 2D array, so its name is confusing.

Alternate positive and negative numbers in an array

Given an array of positive and negative numbers(no zeroes), I have to arrange them in such a way that the positive and negative numbers should be arranged consecutively.
The number of positive and negative numbers may not be equal i.e. if there is no positive number(or negative) left, then all the remaining negative numbers(or positive) are appended to the end of the array.
The order is important i.e.if input array is {2,-1,-3,-7,-8, 9, 5,-5,-7},then the output array should be {2,-1, 9,-3, 5,-7,-8,-5,-7}. The code is done in O(n) without using another array.
Here is my solution in java, I test it again several case and it works. However, I'm not sure if this run in O(n) time. Basically, I count the number of positive and negative number first. then I have two index i = 0 and j =1. j is always 1 step ahead i. From there I keep checking if the number in i is positive and j is negative, if it isn't, i will iterate through the array until I find the next positive/negative for the correct position and move it to the correct position.
Any suggestion is much appreciated. Thanks!
//array of negative and positive, arrange them so that positive number follow by negative
// if no pos or neg left, the rest append to the array.
//should be O(n) no additional array
public static void alter(int[] a) {
int pos = 0;
int neg = 0;
int index = 0;
while (c < a.length) {
if (a[index] > 0) {
pos++;
} else neg++;
index++;
}
int i = 0;
int j = 1;
int temp = 0;
//run until no more positive number or negative number
while (pos > 0 && neg > 0) {
//
if (a[i] > 0) {
pos--;
if (a[j] < 0) {
i += 2;
j += 2;
neg--;
} else // a[j] > 0
{
while (a[j] > 0) {
j++;
}
//a[j] < 0
neg--;
//move that number to the appropriate place
while (j > i) {
temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
j--;
} // end while
i += 2;
j += 2;
}
} else // a[i] < 0
{
while (a[i] < 0) {
i++;
}
//a[i] > 0
//move that number to the appropriate place
while (i > (j - 1)) {
temp = a[i];
a[i] = a[i - 1];
a[i - 1] = temp;
i--;
}
} //end else
}
}
However, I'm not sure if this run in O(n) time
I do not think it runs in O(n). The moment you have to "find the next correct element and move it to the correct location", you need to
Loop over the remainder of the array. Which means that for the worst case scenario (array with all positive elements at the start, and all negative elements at the end) you will loop for each of the positive elements one more time over half of the "unsorted" part of the array
When you move the element back to the correct location, you move it position by position. This means you loop once more over the "unsorted" part of the array
Not really sure yet how you would get it running in O(n) if you need to respect the requirement that the ordering must be respected without using a third array.
Yes, it can be done in O(n).
Lets assume that c is current position. And a[c] is positive number.
1) Increment i from c towards end of array until i points to first wrong number (number that have the same sign as previous, in this case positive).
2) Set j := i;
3) Increment j towards end of array until j points to number with wanted sign (in this case - negative).
4) Swap a[i] with a[j].
5) Set c := j;
6) Set j := c + 1;
In each step you always increment i or j or c. Never decrement.
Variable i can be incremented no more than n times. Same to j and c.
So maximum number of increments is 3n ~ O(n).
PFB my code. If we will use Stack then it will make our problem more easier.
public class AlternatePosNeg {
public static void main(String[] args) {
int arr[] = { 2, -1, -3, -7, -8, 9, 5, -5, -7 };
Stack<Integer> pos = new Stack<>();
Stack<Integer> neg = new Stack<>();
int i;
for (i = 0; i < arr.length; i++) {
if (arr[i] > 0) {
pos.push(arr[i]);
} else {
neg.push(arr[i]);
}
}
int tempArr[] = new int[arr.length];
i = 0;
int sizePos = pos.size();
int sizeNeg = neg.size();
while (i < tempArr.length) {
if (sizePos > sizeNeg) {
if (pos.size() > 0) {
tempArr[i] = pos.pop();
}
if (neg.size() > 0) {
tempArr[i + 1] = neg.pop();
i++;
}
} else {
if (neg.size() > 0) {
tempArr[i] = neg.pop();
}
if (pos.size() > 0) {
tempArr[i + 1] = pos.pop();
i++;
}
}
i++;
}
for (int no : tempArr) {
System.out.print(no + " ");
}
}
}

Categories