I was trying one problem to count the number of triplets in an array whose sum is less than target value.
Input: [-1, 4, 2, 1, 3], target=5
Output: 4
Explanation: There are four triplets whose sum is less than the target:
[-1, 1, 4], [-1, 1, 3], [-1, 1, 2], [-1, 2, 3]
My Code
import java.util.*;
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for(int i = 0; i < arr.length - 2; i++)
{
int left = i + 1;
int right = arr.length - 1;
while(left < right)
{
int targetDiff = target - arr[i] - arr[left] - arr[right];
if (targetDiff > 0)
{
count++;
right--;
}
else
{
left++;
}
}
}
// TODO: Write your code here
return count;
}
}
It produces the output of 3 where as correct value should be 4 as per the above given input. My logic was , say , x + y + z < targetSum , it implies (targetSum - (x + y + z) ) > 0. If this is true I will increase the count and then decrement the right pointer , since array is sorted. If its not true then I will increment the left pointer . But my logic does not cover the triplet {-1, 2, 3}.
Below is the correct code given by author.
import java.util.*;
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for (int i = 0; i < arr.length - 2; i++) {
count += searchPair(arr, target - arr[i], i);
}
return count;
}
private static int searchPair(int[] arr, int targetSum, int first) {
int count = 0;
int left = first + 1, right = arr.length - 1;
while (left < right) {
if (arr[left] + arr[right] < targetSum) {
count += right - left;
left++;
} else {
right--; // we need a pair with a smaller sum
}
}
return count;
}
public static void main(String[] args) {
System.out.println(TripletWithSmallerSum.searchTriplets(new int[] { -1, 0, 2, 3 }, 3));
System.out.println(TripletWithSmallerSum.searchTriplets(new int[] { -1, 4, 2, 1, 3 }, 5));
}
}
The author has used the concept , say x + y + z < targetSum , it implies x + y < targetSum - z . But I don't get the logic of line count += right - left; . How author use this one line to capture the count. If some one can give me the intution on how to reach this inference. Also what is wrong with my code and what can I do to correct it.
A first issue with your code is that :
you only decrease the right index if the sum is inferior to the target.
However, since you have ordered your list, well you will only be entering that case until left=right.
Quick example : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], target=14
if 1+2+10 <13:
then you will only decrease 10 until you reach 2 in your array
and then you proceed to iterate to the next i-index, here going from 0 to 1.
Meaning that: you will never get the solutions in between such as [1,3,9] and all the one that follows.
I hope it helps you see where there was an error in the logic, which was not from the statement : (targetSum - (x + y + z) ) > 0 but from the action you take according to the result (True/False).
Now, I am not sure there would be an easy way to adapt your code corrctly, because the main issue here is that you have iterate over 2 indexes at once (right and left).
Now regarding your author's answer :
The trick behind :
count += right - left;
goes back to the issue you had, if i tame my example, for
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
it is basically saying that, since the array is ordered, if the sum of two integers with the right one is inferior to target, then it will also be true for all integers inferior to right :
1+2+10<14 => 1+2+9<13
And this statement is true for all integers between left and right, so instead of doing a loop for which we already have the answer, he adds to count the differences between right and left, in other words, the number of integers in your array that will be greater than left and lower than right.
Now that i have explained that, you could use the same "trick" to your code:
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for(int i = 0; i < arr.length - 2; i++)
{
int left = i + 1;
int right = arr.length - 1;
while(left < right)
{
int targetDiff = target -( arr[i] + arr[left] + arr[right]);
if (targetDiff > 0)
{
count += right - left;
left++;
}
else
{
right--;
}
}
}
// TODO: Write your code here
return count;
}
}
I tried to be as detailed as possible, hope it helps you understand better!
Related
I need to take a 2D array and move everything as far left as possible. It is a 4x4 array and I have tried to do it but either only move certain items or the index goes out of bounds.
The gameBoard array looks like this:
{0 2 4 2}
{0 0 2 0}
{2 2 0 0}
{0 4 0 2}
and after you call the swipeLeft() method it should look like this:
{2 4 2 0}
{2 0 0 0}
{2 2 0 0}
{4 2 0 0}
There is also the issue of placing a zero into the previous index that you moved it from.
I created a double for loop to just loop through the array and tried to code something that would move it over but it hasn't worked.
Here was the code I had so far
public void swipeLeft() {
for ( int r = 0; r < gameBoard.length; r++ ) {
for ( int c = 0; c < gameBoard[r].length; c++ ) {
gameBoard[r][c] = gameBoard[r][ (c+1) %
gameBoard.length];
}
}
}
Based on your desired OUTPUT, it looks like swipeLeft() is supposed to push all non-zero values to the very left of their row, displacing the zeroes to the right of all non-zero values.
If that's correct, this is similar to Old Dog Programmer's approach, except all shifting is done "in place" without creating any new arrays:
import java.util.*;
class Main {
private static int[][] gameBoard;
public static void main(String[] args) {
gameBoard = new int[][] {
{0, 2, 4, 2},
{0, 0, 2, 0},
{2, 2, 0, 0},
{0, 4, 0, 2}
};
System.out.println("Before:");
displayBoard();
swipeLeft();
System.out.println("\nAfter:");
displayBoard();
}
public static void displayBoard() {
for(int[] row : gameBoard) {
System.out.println(Arrays.toString(row));
}
}
public static void swipeLeft() {
for(int[] row : gameBoard) {
// find the first blank (zero) spot
int nextIndex = 0;
while(nextIndex < row.length && row[nextIndex] != 0) {
nextIndex++;
}
// start with the first blank, and shift any non-zero
// values afterwards to the left
for(int col=nextIndex; col < row.length; col++) {
if (row[col] != 0) {
row[nextIndex] = row[col];
row[col] = 0;
nextIndex++;
}
}
}
}
}
Output:
Before:
[0, 2, 4, 2]
[0, 0, 2, 0]
[2, 2, 0, 0]
[0, 4, 0, 2]
After:
[2, 4, 2, 0]
[2, 0, 0, 0]
[2, 2, 0, 0]
[4, 2, 0, 0]
From the example in the question, it appears to me that what is wanted is to shift all non-zero elements to the left, and zero elements are shifted to the right. The order of the non-zero elements is to be retained.
Note that each row is independent of other rows.
One way to approach this is to create a method that works on a 1D array. This method takes a 1D array as a parameter, and returns another 1D array with the elements shifted:
public static int [] zeroShift (int [] arr) {
int [] left = new int [arr.length];
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0) {
left [count++] = arr [i];
}
}
return left;
}
This copies each non-zero element to a new array of the same size, keeping track (count) of how many have been copied so far. Note this relies on left being initialized to all-zeros.
Once that method is working, it can be used for gameBoard on a row-by-row basis:
public void swipeLeft() {
for (int r = 0; r < gameBoard.length; r++) {
gameBoard [r] = zeroShift (gameBoard [r]);
}
// output for testing
for (int i = 0; i < gameBoard.length; ++i) {
System.out.println(Arrays.toString(gameBoard[i]));
}
}
To rotate the array in place, you should roteate the array 3 times:
123456 -> 654312
654321
3456..
....12
public static void shiftLeft(int[] arr, int offs) {
if (offs <= 0)
return;
offs = arr.length - offs % arr.length - 1;
for (int i = 0, j = arr.length - 1; i < j; i++, j--)
swap(arr, i, j);
for (int i = 0, j = offs; i < j; i++, j--)
swap(arr, i, j);
for (int i = offs + 1, j = arr.length - 1; i < j; i++, j--)
swap(arr, i, j);
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
So your code intends to rotate the board one column to the left. Rotate? Well, the numbers you push out on the left might come back on the end, right?
Probably the line
gameBoard[r][c] = gameBoard[r][ (c+1) % gameBoard.length];
should be
gameBoard[r][c] = gameBoard[r][ (c+1) % gameBoard[r].length];
But try to do this stuff with pen & paper, and you should notice that you are going to loose one column/copy the values from the second column into the first, then copy that into the last column again.
You will need to change two items:
store the value from the first column somewhere if you still need it so you can push it into the last one.
only rotate the column data if it needs to be rotated. Or in other words, rotate the remainder of the row if you find a zero. In this case you do not need to remember the first column, as you will overwrite a zero and push a zero into the last column. And then it would not be called rotate but shift.
Exercise this with pen & paper until you can write down instructions for someone else to perform the same operation. Then you are ready to also write it in Java.
For example, {1, 4, 45, 6, 0, 19} and the number 51 should return 3, because the number of elements in the smallest subarray which together are greater than 51 are 3: {4,45,6}`.
{7, 2, 5, 10, 1} and the number 9 should return 1, because the number of the elements in the smallest subarray possible that is greater than 9 is {10}.
If array is null or empty, or the array has no subarray that is greater than the given number, the method has to return -1.
I'm not allowed to use array package from java.util.
My goal is to execute the method in O(n) time.
This is my code so far, if the array has no subarray greater than the given number, it returns an OutofBounds error.
Anyone has a clue?
public static int smallestSubSum(int arr[], int x) {
int left = 0, right = 1, smallest = 0;
int sum = arr[right];
for (int i = 1; i < arr.length; i++) {
if (sum > x)
smallest = left - right;
else
right++;
sum += arr[right];
if (sum > x && left - right < smallest) {
smallest = left - right;
left++;
} else
sum -= arr[left];
left++;
if (sum > x && left - right < smallest)
smallest = left - right;
}
return smallest;
}
Edit: Perhaps I should explain what I tried to do with my code, basically I wanted the sum to hold the first two elements in the code, and then compare with each 'if' iteration if the sum of the current elements are greater or less than X, if not I raise the right element to go further, if yes I erase the first element, the 'left' one.
The array of {1, 4, 45, 6, 0, 19} and the number 51 returns 2, even though the result should be 3. I don't know why, because my right reaches the index 3 which is 6 and the left one reaches index 1 which is 4, so the result should indeed be {4,45,6} but it doesn't get to it.
This is the best I could do.
Here are my test results.
[1, 4, 45, 6, 0, 19] -> 51
3
[7, 2, 5, 10, 1] -> 9
1
[1, 4, 45, 6, 0, 19] -> 200
-1
I just cycled through the array with a for loop. Whenever the total exceeded the X amount, I subtracted values until the total dropped below the X amount.
Here's the complete runnable code I tested with.
import java.util.Arrays;
public class SmallestSubarray {
public static void main(String[] args) {
int[] arr1 = new int[] { 1, 4, 45, 6, 0, 19 };
int x1 = 51;
System.out.println(Arrays.toString(arr1) + " -> " + x1);
System.out.println(smallestSubSum(arr1, x1));
int[] arr2 = new int[] { 7, 2, 5, 10, 1 };
int x2 = 9;
System.out.println(Arrays.toString(arr2) + " -> " + x2);
System.out.println(smallestSubSum(arr2, x2));
int[] arr3 = new int[] { 1, 4, 45, 6, 0, 19 };
int x3 = 200;
System.out.println(Arrays.toString(arr3) + " -> " + x3);
System.out.println(smallestSubSum(arr3, x3));
}
public static int smallestSubSum(int arr[], int x) {
if (arr == null || arr.length < 1) {
return -1;
}
int sum = 0;
int minCount = Integer.MAX_VALUE;
int index = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
while (sum > x) {
minCount = Math.min(i - index + 1, minCount);
sum -= arr[index];
index++;
}
}
return (minCount == Integer.MAX_VALUE) ? -1 : minCount;
}
}
Just a quick one. I'm struggling to make this code work when the pivot is the middle value. I know it works when the pivot is the leftmost value. Any help please?
public class Quicksort {
public static void main(String[] args) {
int[] numbers = { 1, 3, 6, 2, 2, 2, 4, 8, 4 };
System.out.println(Arrays.toString(numbers));
sort(numbers);
System.out.println(Arrays.toString(numbers));
}
public static void sort(int[] number) {
quicksort(number, 0, number.length - 1);
}
public static void quicksort(int[] array, int left, int right) {
int lowest = left, highest = right;
if (right - left >= 1) {
int pivot = array[(left + right) / 2];
// works when pivot = array[left];
while (lowest < highest) {
while (array[lowest] <= pivot && lowest <= right && highest > lowest) {
lowest++;
}
while (array[highest] > pivot && highest >= left && highest >= lowest) {
highest--;
}
if (highest > lowest) {
swap(array, lowest, highest);
}
}
swap(array, left, highest); // problem area?
quicksort(array, left, highest - 1);
quicksort(array, highest + 1, right);
} else {
return;
}
}
public static void swap(int[] array, int index, int index1) {
int temp = array[index];
array[index] = array[index1];
array[index1] = temp;
}
}
the output for this is
[1, 3, 6, 2, 2, 2, 4, 8, 4] //unsorted
[2, 2, 2, 1, 3, 4, 4, 6, 8] //wrongly "sorted"
Following the suggestion by Peter Lawrey, I first observed that your program sorts two numbers correctly. Then tried with three:
int[] numbers = { 1, 3, 6 };
After your while loop you have
left = 0
lowest = 2
highest = 1
In this case your comment problem area? seems correct sonce you are swapping the numbers 1 and 3, thus bringing them out of order.
In general, after your loop you know that array[left..lowest - 1] are <= pivot and array[highest + 1..right] are > pivot, but if lowest == highest I don’t think you can be sure whether array[highest] belongs in one part or the other, so you don’t know whether to swap it into the left part.
By the way, you have the same problem with int pivot = array[left];. Try int[] numbers = { 3, 1, 6 };, the result is [3, 1, 6].
I was looking at websites such as https://projecteuler.net/, and I came across this question:
"Given an unordered array of integers of length N > 0, calculate the length of the longest ordered (ascending from left [lower index] to right [higher index]) sub-sequence within the array."
For the live of me I am struggling to find a solution in Java. Can anyone help?
EDIT:
Some examples:
Example 1
Input: [1, 4, 1, 4, 2, 1, 3, 5, 6, 2, 3, 7]
Expected Output: 4
Example 2
Input: [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
Expected Output: 3
Example 3
Input: [2, 7, 1, 8, 2, 8, 1]
Expected Output: 2
A term for what you want to find is a subarray, not a subsequence(according to your examples). You can solve it in a linear time using a simple loop:
int res = 0;
int cur = 0;
for (int i = 0; i < a.length; i++) {
if (i > 0 && a[i] <= a[i - 1])
cur = 0;
cur++;
res = Math.max(res, cur);
}
It is not clear if equal elements are considered ordered. Also you can return if there are not enough elements to create a bigger maxLength.
public static int maxLength(int[] array) {
if (array.length <= 1) return array.length; // check for null also
int maxLength = 1;
int curLength = 1;
for (int i = 1; i < array.length; i++) {
//extra check to finish earlier if possible (you may omit it)
if ((array.length - i + curLength) <= maxLength)
return maxLength;
if (array[i] > array[i-1]) //use >= if equal elements count too
curLength++;
else {
if (maxLength < curLength)
maxLength = curLength;
curLength = 1;
}
}
//final check (in case the last element is included in maxLength)
if (maxLength < curLength)
return curLength;
return maxLength;
}
I have a problem where I need to find the maximum distance between two different elements in an array.
For example: given an array 4,6,2,2,6,6,4 , the method should return 5 as the max distance.
I am able to solve the problem using two for loops but it is not an optimized solution. Am trying to optimize it by doing it in a single for loop.
here is my current solution:
int [] A = {4,6,2,2,6,6,4};
int N = A.length;
int result = 0;
for (int i = 0; i < N; i++){
for (int j = i; j < N; j++) {
if(A[i] != A[j]){
result = Math.max(result, j - i);
}
}
}
// tried below code but it is not efficient
// for (int i = 0; i < N; i++){
//
// if(A[N-1] != A[i]){
// result = Math.max(result, N-1-i);
// }
// }
System.out.println(result);
How to make this better in terms of time complexity?
Simple (not nested) loop is enough, but two cases should be taken into
account: either the best result is
4,6,2,2,6,6,4
^ ^ - moving first
or
4,6,2,2,6,6,4
^ ^ - moving last
for instance: [4, 2, 4, 4, 4] moving first brings the answer, when in case of [4, 4, 4, 2, 4] moving last should be used.
int first = 0;
int last = A.length - 1;
// 1st case: moving "first"
while (first < last) {
if (A[first] == A[last])
first++;
else
break;
}
int diff1 = last - first;
first = 0;
last = A.length - 1;
// 2nd case: moving "last"
while (first < last) {
if (A[first] == A[last])
last--;
else
break;
}
int diff2 = last - first;
// result is the max between two cases
int result = diff1 > diff2
? diff1
: diff2;
So we have O(N) time complexity.
Edit: Let's proof that at least one of the indexes is either 0 or length - 1. Let's do it by contradiction. Suppose we have a solution like
a, b, c, .... d, e, f, g
^ ..... ^ <- solution indexes (no borders)
Items to the left of c must be d, otherwise we can take a or b indexes and have an improved solution. Items to right of d must be c or we can once again push last index to the right and have a better solution. So we have
d, d, c .... d, c, c, c
^ .... ^ <- solution indexes
Now, since d <> c (c..d is a solution) we can improve the solution into
d, d, c .... d, c, c, c
^ .... ^ <- solution indexes
^ .... ^ <- better solution
We have a contradiction (the supposed solution is not one - we have a better choice) and that's why at least one index must be 0 or length - 1.
Now we have 2 scenarions to test:
a, b, ..... y, z
^ ...... ^ <- moving first
^ ...... ^ <- moving last
We can combine both conditions into if and have just one loop:
int result = 0;
for (int i = 0; i < A.length; ++i)
if (A[i] != A[A.length - 1] || A[0] != A[A.length - 1 - i]) {
result = A.length - i - 1;
break;
}
This can be done in a single loop
Consider this.
The maximum difference between from a index i can be either between start element and i or i and the last element
int main() {
vector<int> v {4, 6, 2, 2, 6, 6, 4};
int start = 0, end = v.size() -1;
int result = 0;
for(int i=0; i< v.size(); ++i)
{
if(v[i] != v[start])
{
result = max(result, i);
}
if(v[i] != v[end])
{
result = max(result, end - i);
}
}
return result;
}
The reason we are able to achieve a O(N) algorithm is because
Consider v = [4, 4, 2, 3, 4, 4]
At index i = 0 we check if we can find the maximum possible distance i.e with the last element but since they are same we can't consider it.
At i = 0 for this array the maximum possible answer would have been 5.
[4, 4, 2, 3, 4, 4]
^
At i = 1 we again check both ends of the array still the same so we move on.
The real savings come here that we do not have to check for every other entry
keeping the start at i = 0
So, at i = 2, we find that the maximum can be obtained with the end of the array
[4, 4, 2, 3, 4, 4]
^ ^ ^
start i end
which is the same as keeping the start constant and keeping a runner loop.