Write a function that takes an array as input and returns an array of 2 numbers. The returned array contains the sum of even numbers and sum of odd numbers from the input.
If any of the input is null it should be treated as an empty array
Example:
Input:
[30, 18, 2, 83, 20, 71]
Output:
[70, 154]
Input:
[14, 11, 10, 67, 41]
Output:
[24, 119]
Input:
[36, 24, -82, 29, 44, -3, -100, -5, 49]
Output:
[-78, 70]
The function that I have written is
public int[] getSumOfEvensAndOdds(int[] input) {
int x[] = input;
int even = 0, odd = 0;
for (int i = 0; i < x.length; i++) {
if (x[i] % 2 == 0)
even += x[i];
else
odd += x[i];
}
int[] ans={even, odd};
return ans;
}
But how should I incorporate the part of the empty array?
Check if input is null first. If it is, work on an empty array instead:
int x[] = input == null ? new int[0] : input;
if any of the input is null it should be treated as an empty array
Why not just check for null value?
public int[] getSumOfEvensAndOdds(int[] input) {
int even = 0, odd = 0;
if(null != input){
for (int i: input) {
if (0 == i % 2){
even += i;
} else{
odd += i;
}
}
}
return new int[]{even, odd};
}
You need something like this:
public class Test {
public static int[] getSumOfEvensAndOdds(int[] input) {
int[] def = {0,0};
if (input != null && input.length!=0) {
int x[] = input;
int even = 0, odd = 0;
for (int i = 0; i < x.length; i++) {
if (x[i] % 2 == 0)
even += x[i];
else
odd += x[i];
}
int[] ans = {even, odd};
return ans;
}
return def;
}
public static void main(String [ ] args){
int[] ar = {10,20,30,40,50,60,71,80,90,91};
int[] res;
res = getSumOfEvensAndOdds(ar);
System.out.println("Result: " + res[0] + " " + res[1]);
int[] ar2 = {};
res = getSumOfEvensAndOdds(ar2);
System.out.println("Result: " + res[0] + " " + res[1]);
int[] ar3 = null;
res = getSumOfEvensAndOdds(ar3);
System.out.println("Result: " + res[0] + " " + res[1]);
}
}
I use input!=null to check whether the array is null and input.length!=0 to check if its size is 0. Also, in the main method I give three examples.
public int[] getSumOfEvensAndOdds(int[] input) {
int x[] = input;
int even = 0, odd = 0;
for (int i = 0; i < x.length; i++) {
if(x[i] != null) //in case array contains elements which aren't null
{
if (x[i] % 2 == 0)
even += x[i];
else
odd += x[i];
}
else //in case the array has null array elements
{
even = 0;
odd = 0;
}
int[] ans={even, odd};
return ans;
}
Empty array may be return like this return new int[]{}
Your question is just about arrays being empty. A quick search got me this: How can I check whether an array is null / empty?
Related
Find the smallest number M, which is divided by exactly n-1 numbers from the input array. If there is no such M then return -1.
Example:
array = [2,3,5]
Answer :
6
Explanation :
6 can be divided by 2 and 3
Example:
array = [2,3,6]
Answer:
-1
Explanation :
It's not possible in this case so return -1.
My code:
As we need to find the smallest M, I am selecting only the elements from 0 to n-2
public int process(int[] arr) {
int answer = 1;
for(int i=0; i<arr.length-1; i++) {
answer *= arr[i];
}
return answer;
}
This program works for these 2 sample test cases but it was failing for multiple hidden test cases. I trying to understand what I am missing here.
Calculation of the Least Common Multiple (LCM)
A problem inside the task is the calculation of the Least Common Multiple of 2 numbers. The method public int lowerCommonMultiple(int x1, int x2) solves this problem and I think it can be used in other context.
List of the class methods
All the code is included in the methods of the BestMultiple class. These methods (excluding the main) are:
public int[] removeElem(int[] tensArray, int rm_index): used to remove an element from an array
public int leastCommonMultiple(int x1, int x2): calculates the Least Common Multiple of 2 numbers
private int getLeastCommonMultipleNnumber(int[] arr): Calculates the least common multiple of N-1 integer contain in an array
public int process(int[] arr): calculates the least multiple of exactly N-1 number of an array of N integer; it manages many test strange cases (array empty, elem<=0, etc.)
May be the code is not optimized, but I hope it is correct (the output added, shows that it works correctly, at least with the test cases chosen).
public class BestMultiple {
/*++++++++++++++++++++++++++++++++++++++++++++
Method: removeElem() remove an element from
an array.
+++++++++++++++++++++++++++++++++++++++++++*/
public int[] removeElem(int[] tensArray, int rm_index) {
// Create a proxy array of size one less than original array
int[] proxyArray = new int[tensArray.length - 1];
// copy all the elements in the original to proxy array
// except the one at index
for (int i = 0, k = 0; i < tensArray.length; i++) {
// check if index is crossed, continue without copying
if (i == rm_index) {
continue;
}
// else copy the element
proxyArray[k++] = tensArray[i];
}
return proxyArray;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Method: leastCommonMultiple() Calculates the Least Common
multiple for 2 numbers
++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
public int leastCommonMultiple(int x1, int x2) {
int lcm = 1;
int max = x1;
if ((x1 == 0) || (x2 == 0)) {
lcm = 0;
} else {
if (x2 > x1) {
max = x2;
}
for (int i = 2; i <= max; i++) {
int exp_x1 = 0;
int exp_x2 = 0;
int exp = 0;
if (x1 > 1) {
while ((x1 % i) == 0) {
exp_x1++;
x1 /= i;
}
}
if (x2 > 1) {
while ((x2 % i) == 0) {
exp_x2++;
x2 /= i;
}
}
if ((exp_x1 > 0) || (exp_x2 > 0)) {
exp = exp_x1;
if (exp_x2 > exp) {
exp = exp_x2;
}
while (exp > 0) {
lcm *= i;
exp--;
}
}
}
}
return lcm;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Method: getLeastCommonMultipleNnumber()
Calculates the least common multiple of N-1
integer contain in an array
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
public int getLeastCommonMultipleNnumber(int[] arr) {
int multiple = 1;
if (arr.length >= 2) {
multiple = leastCommonMultiple(arr[0], arr[1]);
for (int j = 2; j < arr.length; j++) {
multiple = leastCommonMultiple(multiple, arr[j]);
}
} else {
// array with only 2 elements
multiple = arr[0];
}
return multiple;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Method: process()
Calculates the least multiple of EXACTLY N-1
number of an array of N integer
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
public int process(int[] arr) {
int answer;
if (arr.length <= 1) {
// array contains only one element or is empty => return -1
answer = -1;
} else {
int pos_elem_zero = -1;
int prod = 1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > 0) {
prod *= arr[i];
} else {
if (arr[i] < 0) {
// integer < 0 are not allowed
return -1;
}
if (pos_elem_zero == -1) {
pos_elem_zero = i;
} else {
// there are more element == 0
return -1;
}
}
}
if (pos_elem_zero >= 0) {
// there is one element == 0
arr = this.removeElem(arr, pos_elem_zero);
return getLeastCommonMultipleNnumber(arr);
}
// managing of normal test case
answer = prod;
for (int i = 0; i < arr.length; i++) {
int elem = arr[i];
int[] arr2 = this.removeElem(arr, i);
int multiple = getLeastCommonMultipleNnumber(arr2);
if (multiple > elem) {
if ((multiple % elem) != 0) {
if (multiple < answer) {
answer = multiple;
}
}
} else {
if (multiple < elem) {
answer = multiple;
}
}
}
if (answer == prod) {
answer = -1;
}
}
return answer;
}
/*++++++++++++++++++++++++++++++++++++++++++
Method: main() Executes test of process()
method
+++++++++++++++++++++++++++++++++++++++++*/
public static void main(String[] args) {
BestMultiple bm = new BestMultiple();
int[] arr1 = {6,30,5,3};
int[] arr2 = {1,2,3};
int[] arr3 = {1,2,3,3};
int[] arr4 = {6,7,5,3};
int[] arr5 = {9,14, 21};
int[] arr6 = {2,4};
int[] arr7 = {2,3,5};
int[] arr8 = {2,3,6};
int[] arr9 = {2};
int[] arr10 = {};
int[] arr11 = {2,3,0};
int[] arr12 = {0,2,3,0};
int[] arr13 = {20,3};
int[] arr14 = {0,6,15};
int[] arr15 = {1,6,15,-1};
int[] arr16 = {1,6,15};
int[] arr17 = {2,3,0,6,15};
System.out.println("{6,30,5,3} --> " + bm.process(arr1));
System.out.println("{1,2,3} --> " + bm.process(arr2));
System.out.println("{1,2,3,3} --> " + bm.process(arr3));
System.out.println("{6,7,5,3} --> " + bm.process(arr4));
System.out.println("{9,14,21} --> " + bm.process(arr5));
System.out.println("{2,4} --> " + bm.process(arr6));
System.out.println("{2,3,5} --> " + bm.process(arr7));
System.out.println("{2,3,6} --> " + bm.process(arr8));
System.out.println("{2} --> " + bm.process(arr9));
System.out.println("{} --> " + bm.process(arr10));
System.out.println("{2,3,0} --> " + bm.process(arr11));
System.out.println("{0,2,3,0} --> " + bm.process(arr12));
System.out.println("{20,3} --> " + bm.process(arr13));
System.out.println("{0,6,15} --> " + bm.process(arr14));
System.out.println("{1,6,15,-1} --> " + bm.process(arr15));
System.out.println("{1,6,15} --> " + bm.process(arr16));
System.out.println("{2,3,0,6,15} --> " + bm.process(arr17));
}
}
Output of the program
The output of the program with the test cases chosen is:
{6,30,5,3} --> -1
{1,2,3} --> 2
{1,2,3,3} --> 3
{6,7,5,3} --> 30
{9,14,21} --> 42
{2,4} --> 2
{2,3,5} --> 6
{2,3,6} --> -1
{2} --> -1
{} --> -1
{2,3,0} --> 6
{0,2,3,0} --> -1
{20,3} --> 3
{0,6,15} --> 30
{1,6,15,-1} --> -1
{1,6,15} --> 6
{2,3,0,6,15} --> 30
You start by writing a method process that computes the minimum number for each subarray with one element excluded:
public static int process(int... arr) {
int min = -1;
for (int i = 0; i < arr.length; ++i) {
int r = process(arr, i);
if (r != -1) {
if (min == -1) {
min = r;
} else {
min = Math.min(min, r);
}
}
}
return min;
}
Where the second process method looks like this:
private static int process(int[] arr, int exclude) {
int result = 0;
for (int i = 0; i < arr.length; ++i) {
if (i != exclude) {
if (result == 0) {
result = arr[i];
} else {
result = lcm(result, arr[i]);
}
}
}
if (result%arr[exclude] == 0) {
return -1;
}
return result;
}
You need a method that computes the LCM of two numbers. Here, I'll use a second method that computes the GCD:
private static int lcm(int a, int b) {
return a*b/gcd(a,b);
}
private static int gcd(int a, int b) {
if (a == 0) {
return b;
} else if (b == 0) {
return a;
} else {
while (a != b) {
if (a > b) {
a -= b;
} else {
b -= a;
}
}
return a;
}
}
Examples:
System.out.println(process(2, 3, 5)); // prints 6
System.out.println(process(2, 3, 6)); // prints -1
System.out.println(process(9, 14, 21)); // prints 42 (divisible by 14 and 21, but not by 9
System.out.println(process(6, 7, 5, 3)); // prints 30 (divisible by 6, 5, 3, but not by 7
The way you implemented process() assumes the input array is sorted. But anyway, I don't think sorting will help here. Note that the number satisfying the given conditions can be divided by the biggest number. For [2, 3, 5, 6] it is 6. Dividing the product of all the elements by consecutive elements from the biggest to the lowest and stopping at the first that is not a divisor is also not correct. In the example [2, 4, 5, 6] this would give 2 * 4 * 5 = 40 when the correct answer is 20.
My idea is to use an algorithm inspired by Sieve of Eratosthenes. Note that the number that satisfies the conditions can't be bigger than the product of all the elements. So create a table divisors[] with indices from 0 through the product of the elements of array where divisors[i] indicates how many elements from array divide i. Iterate over elements of array and increment all elements in divisors[i] where i is divided by the element. Then find the first i for which divisors[i] == n - 1.
The limitation is that divisors can be quite big depending on what is the product of array, so applicability will be limited to relatively small values in array.
This question already has answers here:
How do I convert from int to String?
(20 answers)
Closed 1 year ago.
Task
Given int[] arrays such as I have to identify consecutive ranges and build a corresponding String
Another example would be
Notes
I can assume that the array is already ordered and there are no overlaps in the ranges.
Also, the array might be empty.
Ideas
I know how to build a string that just lists all numbers, like 4, 5, 6, 8, 14, 15, 16 but I have no idea how to identify the ranges and especially how to add a ~ between them.
I thought maybe regex might be usefull here, but I am unsure.
Explanation
You can simply iterate from left to right and maintain ranges while doing so.
For that, just remember the start of a range and the last value in each iteration. Then you can easily figure out if a range just stopped or is continuing. If it stopped, conclude the range and start the next.
For the special case of an empty array, just add a simple if to the start of the method, to handle it separately.
Build ranges
public static String buildRanges(int[] values) {
if (values.length == 0) {
return "";
}
StringJoiner result = new StringJoiner(", ");
int rangeStart = values[0];
int lastValue = values[0];
// Skip first value to simplify 'lastValue' logic
for (int i = 1; i < values.length; i++) {
int value = values[i];
if (value != lastValue + 1) {
// Range ended, wrap it up
int rangeEnd = lastValue;
result.add(rangeStart == rangeEnd
? Integer.toString(rangeStart)
: rangeStart + "~" + rangeEnd);
rangeStart = value;
}
lastValue = value;
}
// Conclude last range
int rangeEnd = lastValue;
result.add(rangeStart == rangeEnd
? Integer.toString(rangeStart)
: rangeStart + "~" + rangeEnd);
return result.toString();
}
Simplify
To tackle the code duplication and to improve readability, I would suggest to also introduce a helper method rangeToString:
public static String rangeToString(int rangeStart, int rangeEnd) {
return rangeStart == rangeEnd
? Integer.toString(rangeStart)
: rangeStart + "~" + rangeEnd);
}
The code then simplifies to:
public static String buildRanges(int[] values) {
if (values.length == 0) {
return "";
}
StringJoiner result = new StringJoiner(", ");
int rangeStart = values[0];
int lastValue = values[0];
// Skip first value to simplify 'lastValue' logic
for (int i = 1; i < values.length; i++) {
int value = values[i];
if (value != lastValue + 1) {
// Range ended, wrap it up
result.add(rangeToString(rangeStart, lastValue);
rangeStart = value;
}
lastValue = value;
}
// Conclude last range
result.add(rangeToString(rangeStart, lastValue);
return result.toString();
}
OOP solution
If you feel like, you can also introduce dedicated Range classes to solve this. Might be a bit overkill in this particular situation but still.
Let us first create a Range class that knows its start and end. Furthermore, it can convert itself to a String properly and you can attempt to increase the range.
public final class Range {
private final int start;
private int end;
public Range(int value) {
start = value;
end = value;
}
public boolean add(int value) {
if (value != end + 1) {
return false;
}
end = value;
return true;
}
#Override
public String toString() {
return start == end
? Integer.toString(start)
: start + "~" + end;
}
}
And now you can easily use that in a simple loop:
public static String buildRanges(int[] values) {
if (values.length == 0) {
return "";
}
StringJoiner result = new StringJoiner(", ");
Range range = new Range(values[0]);
// Skip first value to simplify logic
for (int i = 1; i < values.length; i++) {
int value = values[i];
if (!range.add(value)) {
// Range ended, wrap it up
result.add(range.toString());
range = new Range(value);
}
}
// Conclude last range
result.add(range.toString());
return result.toString();
}
Collecting to List<Range>
This approach has the advantage that you can also collect to something like a List<Range> ranges and then continue working with the data, instead of only going to a String. Example:
public static List<Range> buildRanges(int[] values) {
if (values.length == 0) {
return List.of();
}
List<Range> ranges = new ArrayList<>();
Range range = new Range(values[0]);
// Skip first value to simplify logic
for (int i = 1; i < values.length; i++) {
int value = values[i];
if (!range.add(value)) {
// Range ended, wrap it up
ranges.add(range);
range = new Range(value);
}
}
// Conclude last range
ranges.add(range);
return ranges;
}
In particular useful if you also add some getStart() and getEnd() method to the Range class.
Notes
Note that the method will likely behave funky if the array contains duplicates. You did not specify what to do in that case, so I simply assumed duplicates will not exist for your use case.
Say you have the following Interval class:
public class Interval {
public final int min;
public final int max;
public Interval(int min, int max) {
this.min = min;
this.max = max;
}
public Interval(int minmax) {
this(minmax, minmax);
}
public static List<Interval> toIntervals(int... values) {
List<Interval> list = new ArrayList<>();
for (int val: values) {
addInterval(list, val, val);
}
return list;
}
private static void addInterval(List<Interval> list, int min, int max) {
int i = 0;
while (i < list.size()) {
Interval cur = list.get(i);
if (max < cur.min-1) {
break;
} else if (cur.max >= min-1) {
min = Math.min(min, cur.min);
max = Math.max(max, cur.max);
list.remove(i);
} else {
++i;
}
}
list.add(i, new Interval(min, max));
}
#Override
public String toString() {
if (min == max) {
return Integer.toString(min);
}
return min + "~" + max;
}
}
You can convert the list of numbers in your examples to lists of intervals:
List<Interval> list = Interval.toIntervals(4, 5, 6, 8, 14, 15, 16);
Or:
List<Interval> list = Interval.toIntervals(13, 14, 15, 16, 21, 23, 24, 25, 100);
And print lists like that:
System.out.println(list.stream()
.map(Interval::toString)
.collect(Collectors.joining(", ")));
Try this.
static String summary(int[] input) {
int length = input.length;
if (length == 0) return "";
StringBuilder sb = new StringBuilder();
new Object() {
int start = input[0];
int end = input[0];
String separator = "";
void append() {
sb.append(separator).append(start);
if (start != end)
sb.append("~").append(end);
separator = ", ";
}
void make() {
for (int i = 1; i < length; ++i) {
int current = input[i];
if (current != end + 1) {
append();
start = current;
}
end = current;
}
append();
}
}.make();
return sb.toString();
}
public static void main(String[] args) {
System.out.println(summary(new int[] {4, 5, 6, 8, 14, 15, 16}));
System.out.println(summary(new int[] {13, 14, 15, 16, 21, 23, 24, 25, 100}));
}
output:
4~6, 8, 14~16
13~16, 21, 23~25, 100
It is a int-to-string conversion, not a cast in java sence which means (TYPE2)OBJECTOFTYPE1 for instance int-to-double (widening) or Object-to-String.
The ranges one has to do oneself.
int[] a = {13, 14, 15, 16, 21, 23, 24, 25, 100};
StringBuilder sb = new StringBuilder();
if (a.length > 0) {
for (int i = 0; i < a.length; ++i) {
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(Integer.toString(a[i]));
int rangeI = i;
while (a[i] != Integer.MAX_VALUE && a[i] + 1 == a[i + 1]) {
++i;
}
if (i != rangeI) {
sb.append('~').append(Integer.toString(a[i]));
}
}
}
return sb.toString();
You can do append(a[i]) as there is an overloaded method that does internally a conversion. For clearity not done.
The separation by comma is quite standard.
And for every element check its range of increasing followers.
As java ignores integer overflow, the check on MAX_VALUE.
Convert the integers to strings and append them using the StringBuilder class of Java: https://www.geeksforgeeks.org/stringbuilder-class-in-java-with-examples/
StringBuilder str = new StringBuilder();
and append the numbers and '~' (tilda) sign by using:
str.append(start_number);
str.append('~');
str.append(end_number);
the code is suposed to print the largest number to the right of the current number, and for the last number it shoukd print -1
import java.util.Scanner;
public class Maxtoright {
public static void main(String[] args) {
Scanner sc = newScanner(System.in);
int t= sc.nextInt();
while(t-->0) {
int n=sc.nextInt();
int a[]= new int[n];
for(int i=0;i<n;i++) {
a[i]=sc.nextInt();
}
if(n==1) {
System.out.print("-1");
}
if(n==2) {
System.out.print(a[n-1]+"-1");
}
if(n>2) {
for(int i=0;i<n-2;i++) {
int max= a[i+1];
for(int j=i+1;j<n-2;j++) {
if(a[j]>max) {
max=a[j];
}
// System.out.print(max+" ");
}
System.out.print(max+" ");
}
System.out.print(a[n-1]+" "+"-1");
}
}
}
Blockquote
Well, if the requirement is to for every element in an array find the largest element to its right. (For last element it would be -1)
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while (t-- > 0) {
int n = sc.nextInt();
int[] input = new int[n];
for (int i = 0; i < n; i++) {
input[i] = sc.nextInt();
}
if (n == 1) {
System.out.print("-1");
}
if (n == 2) {
System.out.print(input[n - 1] + "-1");
} else {
findLargeNumber(input);
}
}
}
public static void findLargeNumber(int[] input) {
int n = input.length;
int max = input[n - 1];
System.out.print(input[n - 1] + " " + -1 + "\n");
for (int i = n - 2; i >= 0; i--) {
int temp = input[i];
if (temp > max) {
max = temp;
}
System.out.print(input[i] + " " + max + "\n");
}
}
Output: for input {2, 4, 3, 7, 19, 7, 23, 1, 10, 17};
17 -1
10 17
1 17
23 23
7 23
19 23
7 23
3 23
4 23
2 23
As far as I understand, you have an unsorted array like:
[1, 10, 20, 18, 30]
Then you want the user to get the user input, which is a number in an array and find the biggest number to its right. So for example, if the input is 10 it will output 30, but if it's the last number it will just output -1.
import java.util.Scanner;
class Main {
public static void main(String[] args) {
//An unsorted array
final int[] array = {1, 10, 20, 18, 30};
//Here we get the user input
final var scanner = new Scanner(System.in);
final var input = scanner.nextInt();
System.out.println(findBiggestValue(array, input));
}
private static int findBiggestValue(int[] array, int input){
//Find index of the input in the array
var index = -1;
for(int x = 0; x < array.length; x++){
var entry = array[x];
if(entry == input){
index = x;
break;
}
}
//If no index is found, throw an exception
if(index == -1){
throw new RuntimeException("The number inserted isn't in the array!");
}
//If the index is the last entry return -1
if(index == array.length - 1){
return -1;
}
//Find biggest value after the index that we found previously
var maxValue = input;
for(var entry : array){
if(entry > maxValue){
maxValue = entry;
}
}
return maxValue;
}
}
I am writing a program and I can't seem to make the IF action loop and check all of the arrays in the main. My job is to figure out whether there exists any pair of numbers (i.e., any two elements) in this ascending sorted array that will add up to 20. All you need to do is to sum the values these two pointers point to and see if they are equal to 20, if so, output. otherwise, inspect the sum, if the sum is greater than 20,decrement the second pointer and if the sum is less than 20, increment the first pointer. cannot use the nested for loop approach!! Not sure how to fix this... i've been at it for hours and have handwritten it with no luck. thank you!!
// if val of arr at index i is smaller than at arr j, then return
// smaller value
// This function will inspect the input to find any pair of values that
// add up to 20
// if it find such a pair, it will return the *index* of the smallest
// value
// if it does not find such as pair, it will return -1;
public class SumExperiment {
public static int check_sum(int[] array) {
int i = array[0];
int y = array.indexOf(array.length); // need value # index of array.length to begin
//loop to repeat action
for (int arraysChecked = 0; arraysChecked < 5; arraysChecked++ )
{
if ( i + y == 20)
{
return i;
// System.out.print(array[i]);
}
else if ( i + y > 20)
{
y--; //index #y
}
else if (i + y < 20)
{
i++; //index #x
}
if ( i + y != 20)
{
return -1;
}
arraysChecked++;
}
return -1; //because must return int, but this isn't correct
}
public static void main(String[] args) {
int[] array1 = new int[] { 5, 7, 8, 9, 10, 15, 16 };
if (check_sum(array1) != 0)
System.err.println("TEST1 FAILED");
int[] array2 = new int[] { 3, 5, 8, 9, 10, 15, 16 };
if (check_sum(array2) != 1)
System.err.println("TEST2 FAILED");
int[] array3 = new int[] { 3, 4, 6, 9, 10, 14, 15 };
if (check_sum(array3) != 2)
System.err.println("TEST3 FAILED");
int[] array4 = new int[] { 6, 7, 8, 9, 10, 15, 16 };
if (check_sum(array4) != -1)
System.err.println("TEST4 FAILED");
System.out.println("Done!!!");
}
}
I think you are getting confused between the values in the array and the indices of the values. Here is a working version with variable names that make it easier to understand what's going on:
public static int check_sum(int[] array) {
int leftIndex = 0;
int rightIndex = array.length - 1;
for (int arraysChecked = 0 ; arraysChecked < 5 ; arraysChecked++) {
if (leftIndex == rightIndex) {
return -1;
}
int smallerValue = array[leftIndex];
int largerValue = array[rightIndex];
int sum = smallerValue + largerValue;
if (sum == 20) {
// Returns INDEX of smaller value
return leftIndex;
} else if (sum > 20) {
rightIndex--;
} else if (sum < 20) {
leftIndex++;
}
// NO NEED FOR THIS: arraysChecked++; (for loop does it for you)
}
}
I'm studying the 3 Sum to implement it on my own, and came across the following implementation with the rules:
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
And implementation (sorts the array, iterates through the list, and uses another two pointers to approach the target):
import java.util.*;
public class ThreeSum {
List<List<Integer>> threeSum(int[] num) {
Arrays.sort(num);
List<List<Integer>> res = new LinkedList<>();
for (int i=0; i<num.length-2; i++) {
if (i==0 || (i>0 && num[i] != num[i-1])) { //HERE
int lo = i+1;
int hi = num.length-1;
int sum = 0 - num[i];
while (lo < hi) {
if (num[lo] + num[hi] == sum) {
res.add(Arrays.asList(num[i], num[lo], num[hi]));
while (lo < hi && num[lo] == num[lo+1]) lo++; //HERE
while (lo < hi && num[hi] == num[hi-1]) hi--; //HERE
lo++; hi--;
} else if (num[lo] + num[hi] < sum) lo++;
else hi--;
}
}
}
return res;
}
//Driver
public static void main(String args[]) {
ThreeSum ts = new ThreeSum();
int[] sum = {-1, 0, 1, 2, -1, -4};
System.out.println(ts.threeSum(sum));
}
}
And my question is (located where commented: //HERE), what's the reason for checking num[i] != num[i-1], num[lo] == num[lo+1], and num[hi] == num[hi-1]? Supposedly they are supposed to skip the same result, but what does that mean? Examples would really help.
Thank you in advance and will accept answer/up vote.
Imagine you have {-1,-1,0,1,2,4} and considering triplet num[0], num[2], num[3] (-1,0,1).
lo=0 here. To exclude triplet num[1], num[2], num[3] with the same values, we should increment lo and pass over duplicate
This will prevent the list to have duplicate triplet.
For example, with you test :
int[] sum = {-1, 0, 1, 2, -1, -4};
will be sorted like :
sum = {-4, -1, -1, 0, 1, 2};
You see that you have -1 twice. Without these test, you would test twice if -1 = 0 + 1. This is not usefull so the algo simply search the next different value.
You could remove duplicate in the sorted List to prevent these test.
Thanks to MBo, we can't remove duplicate since we can have triplet with same value (but with different index)
All the three sentences is used to avoid the duplicate output.
Consider a sorted list {-2, -2 , 1, 1}
If there is no checking for num[i] != num[i-1], the output of the program would be(-2, 1, 1)and(-2, 1, 1), which are two duplicate triplets.
The checking for num[lo] != num[lo + 1]and num[hi] != num[hi - 1] are for the same reason.
Consider a sorted list
{-2,-1,-1,0,3}
If there is no checking for num[lo], you will get (-2,-1,3) and (-2,-1,3) as the output.
Still, I want to recommend a better solution for this problem. You can numerate the sum of two numbers in the list and find the 3rd number by hash or binary search. It will helps you to gain a O(n^2logn) time complexity rather than O(n^3). (I was wrong, the time complexity of this algorithm is O(n^2), sorry for that.)
Following program finds pairs of three integer with O(N*2)
Sort the input Array
and iterate each element in for loop and check for sum in program which is developed for Two sum.
Two sum in linear time after sorting ->
https://stackoverflow.com/a/49650614/4723446
public class ThreeSum {
private static int countThreeSum(int[] numbers) {
int count = 0;
for (int i = 0; i < numbers.length; i++) {
int front = 0, rear = numbers.length - 1;
while (front < rear) {
if (numbers[front] + numbers[rear] + numbers[i] == 0) {
System.out.printf(String.format("Front : {%d} Rear : {%d} I : {%d} \n", numbers[front],
numbers[rear], numbers[i]));
front++;
rear--;
count++;
} else {
if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
front++;
} else {
rear--;
}
}
}
}
return count;
}
public static void main(String[] args) {
int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
Arrays.sort(numbers);
System.out.println(countThreeSum(numbers));
}
}
It's worked with any NSum (3Sum, 4Sum, 5Sum, ...) and quite fast.
public class ThreeSum {
private static final int RANDOM_RANGE = 20;
private Integer array[];
private Integer arrayIndex[];
private int result[];
private int bagLength;
private int resultIndex = 0;
private void generateData(int size) {
array = new Integer[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
array[i] = random.nextInt(RANDOM_RANGE) - (RANDOM_RANGE/2);
}
}
private void markArrayIndex(int size) {
arrayIndex = new Integer[size];
for (int i = 0; i < size; i++) {
arrayIndex[i] = i;
}
}
private void prepareBeforeCalculate(int size, int sumExpected, int bagLength) {
this.bagLength = bagLength;
result = new int[bagLength];
generateData(size);
markArrayIndex(size);
}
void calculate(int size, int sumExpected, int bagLength) {
prepareBeforeCalculate(size, sumExpected, bagLength);
Arrays.sort(arrayIndex, (l, r) -> array[l].compareTo(array[r]));
System.out.println(Arrays.toString(array));
long startAt = System.currentTimeMillis();
if (sumExpected > 0) findLeft(sumExpected, 0, 0, array.length);
else findRight(sumExpected, 0, 0 - 1, array.length - 1);
System.out.println("Calculating in " + ((System.currentTimeMillis() - startAt) / 1000));
}
private void findLeft(int total, int indexBag, int left, int right) {
while (left < array.length && array[arrayIndex[left]] < 0 && indexBag < bagLength) {
navigating(total, arrayIndex[left], indexBag, left, right);
left++;
}
}
private void findRight(int total, int indexBag, int left, int right) {
while (right >= 0 && array[arrayIndex[right]] >= 0 && indexBag < bagLength) {
navigating(total, arrayIndex[right], indexBag, left, right);
right--;
}
}
private void navigating(int total, int index, int indexBag, int left, int right) {
result[indexBag] = index;
total += array[index];
if (total == 0 && indexBag == bagLength - 1) {
System.out.println(String.format("R[%d] %s", resultIndex++, toResultString()));
return;
}
if (total > 0) findLeft(total, indexBag + 1, left + 1, right);
else findRight(total, indexBag + 1, left, right - 1);
}
private String toResultString() {
int [] copyResult = Arrays.copyOf(result, result.length);
Arrays.sort(copyResult);
int iMax = copyResult.length - 1;
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(array[copyResult[i]]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
}
public class ThreeSumTest {
#Test
public void test() {
ThreeSum test = new ThreeSum();
test.calculate(100, 0, 3);
Assert.assertTrue(true);
}
}