3Sum on Leetcode - java

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;
}

Related

Is there a better way of solving this The Leetcode ThreeSum problem?

Please I am just curious if there is a better way I can achieve the Leetcode ThreeSum problem.
I came up with this piece of code using Binary Search
// Time Complexity: O(nlogn) + O(nlogn) == O(nlogn)
// Extra Space Complexity: O(1)
public static List<List<Integer>> threeSumOptimalSolution(int[] nums) {
int n = nums.length;
if (n < 3)
return Collections.emptyList();
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
int left;
int right;
int mid;
int threeSum;
for (int firstValue = 0; firstValue < n - 2; firstValue++) {
if (firstValue == 0 || nums[firstValue] != nums[firstValue - 1]) {
left = firstValue + 1;
right = n - 1;
while (left < right) {
mid = left + (right - left) / 2;
threeSum = nums[firstValue] + nums[left] + nums[right];
if (threeSum < 0) {
if (nums[mid] + nums[right] + nums[firstValue] < 0)
left = mid + 1;
else
left++;
} else if (threeSum > 0) {
if (nums[mid] + nums[left] + nums[firstValue] > 0)
right = mid - 1;
else
right--;
} else {
result.add(List.of(nums[firstValue], nums[left], nums[right]));
left++;
while (nums[left] == nums[left - 1] && left < right)
left++;
while (nums[right] == nums[right - 1] && left < right)
right--;
}
}
}
}
return result;
}
I would appreciate an extensive review from individual/individuals.
The best time complexity in the ThreeNums problem I know is O(n^2)
And I think you want to use Two Pointers to finish the problem.
In your Answer, the part Of Binary Search is wrong, I don't know what's are you doing.
The correct method is to traverse the list outside and use Two Pointers inside.
E.g
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++) {
if(nums[i] > 0) break;
if(i > 0 && nums[i] == nums[i-1]) continue;
int left = i+1;
int right = nums.length-1;
while(left < right) {
int sum = nums[i] + nums[left] + nums[right];
if(sum == 0) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[left]);
temp.add(nums[right]);
res.add(temp);
while(right > left && nums[right] == nums[right-1]) right--;
while(right > left && nums[left] == nums[left+1]) left++;
right--;
left++;
} else if(sum > 0) {
right--;
} else {
left++;
}
}
}
return res;
}
}

LeetCode Java - Frequency of the most frequent element

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.

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 the max sum path from bottom left side to top right cell

I have to find the maximum sum path in a 2d Array(n,m) given which has a value from 0 to 99999. 0 means wall. We have t start from the left bottom side of the array and must reach the right top cell(0,m-1). You can go up/down/right and can visit each cell once. Below is the code without any blocks .My problem is that i cant move from left bottom to right top cell . I also created left array(lest side of the main array) so that i can start from the best value possible .Sorry am not good programmer :).
Code
public static int maxvalue(int [][]field,int[] left)
{
for(int i=field.length-1;i>0 && left[i]!=-1;i--)
{
System.out.println( "Startpos "+i+" 0");
int distance =max(i,0,field,0,field.length-1);
if(distance>maxvalue)
maxvalue=distance;
}
return maxvalue;
}
public static int max(int r, int c,int [][]field ,int destR, int destC)
{
if(r>destR|| c>destC)
return 0;
if(r==0 && c==field[0].length)
return field[r][c];
int sum1=max(r-1,c,field,destR,destC); // up
System.out.println(sum1);
int sum2= max(r+1,c,field,destR,destC); //down
int sum3= max(r,c+1,field,destR,destC); //right
return field[r][c]+Math.max(sum1, Math.max(sum2, sum3));
}
Sample
Input
0 1 2 3
2 0 2 4
3 3 0 3
4 2 1 2
Output
25
How to do solve this question? if all the path is blocked then print No Solution.
Have you first tried to solve it by yourself?
It looks like a bit of work but it is not impossible.
What I would use is 3 int variables : xPosition, yPosition and Sum;
Go on and test the values of xPosition+1, yPosition-1 in priority and then the rest (because you want to reach xPosition == array.length - 1 && yPosition == 0.) and if you find a 0, test the other possibilities and exclude the ones you already passed by.
Each time you find a good path, add the value of the cell to your sum.
Reset it to 0 once you're blocked.
For every element in the array, you have to find the maximum of the adjacent elements and also check the boundary conditions. I hope this code will help you.
public class StackOverFlow {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
Integer [][] array = new Integer[n][m];
boolean [][] visited = new boolean[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
array[i][j] = in.nextInt();
}
}
int i = n-1, j =0;
visited[i][j] = true;
int sum = array[i][j];
while(true)
{
int max = -1;
int maxi = 0, maxj = 0;
if(i-1 >= 0 && i-1<= n-1 && j>=0 && j<= m-1 && array[i-1][j] != null && array[i-1][j]>max && !visited[i-1][j])
{
max = array[i-1][j];
maxi = i-1;
maxj=j;
}
if(i+1 >= 0 && i+1<= n-1 && j>=0 && j<= m-1 &&array[i+1][j] != null && array[i+1][j]>max && !visited[i+1][j])
{
max = array[i+1][j];
maxi = i+1;
maxj=j;
}
if(i >= 0 && i<= n-1 && j-1>=0 && j-1<= m-1 && array[i][j-1] != null && array[i][j-1]>max && !visited[i][j-1])
{
max = array[i][j-1];
maxi = i;
maxj=j-1;
}
if(i >= 0 && i<= n-1 && j+1>=0 && j+1<= m-1 && array[i][j+1] != null && array[i][j+1]>max && !visited[i][j+1])
{
max = array[i][j+1];
maxi = i;
maxj=j+1;
}
i = maxi;
j = maxj;
visited[i][j] = true;
sum += max;
if(i == 0 && j == m-1)
break;
}
System.out.println(sum);
}
}

Messed up recursion code for a board game

What I have to do here is to count the number of adjacent white blocks (in 2's) on a square board which is made up of random black(0's) and white(1's) blocks. The white blocks have to be at i+1,j || i-1,j || i,j+1 || i,j-1. Technically diagonals are not counted. I have provided an example below:
[1 0 1]
[1 1 0]
[0 1 0]
Here count == 3 (0,0)(1,0) and (1,0)(1,1) and (1,1)(2,1)
Here is my code:
public int count = 0;
boolean count(int x, int y, int[][] mat)
{
if(x<0 || y<0)
return false;
if(mat[x][y] == 0)
return false;
for(int i = x; i<mat.length; i++)
{
for(int j = y; j<mat[0].length; j++)
{
if(mat[i][j] == 1)
{
mat[i][j] = 0;
if(count(i-1,j,mat))
count++;
if(count(i,j-1,mat))
count++;
if(count(i+1,j,mat))
count++;
if(count(i,j+1,mat))
count++;
}
}
}
return true;
}
Short explanation of what I am trying to do here: I am going about finding 1's on the board and when I find one I change it to a 0 and check its up,down,left,right for a 1. This goes on till I find no adjacent 1's. What is the thing I am missing here? I kind of have a feeling I am looping unnecessarily.
here's a solution without recursion
for(int i = 0; i < mat.length; i++) {
for(int j = 0; j < mat[i].length; j++) {
if(mat[i][j] == 1) {
if(i < mat.length - 1 && mat[i+1][j] == 1) {
count++;
}
if(j < mat[i].length - 1 && mat[i][j+1] == 1) {
count++;
}
}
}
I don't think recursion is the right answer as you should only being going one step deep (to find the adjacent value). Instead just loop through the elements looking to the right and down. Don't look up or left as twain mentioned so that you don't double count matches. Then is it simply:
for (i=0; i<max; i++)
for (j=0; j<max; j++)
if (array[i][j] == 1){
if (i<max-1 && array[i+1][j] == 1) count++;
if (j<max-1 && array[i][j+1] == 1) count++;
}

Categories