Skip to content

Instantly share code, notes, and snippets.

@steghio
Last active September 30, 2021 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steghio/ea6d32381f2e589fd3f09cfed07d65a7 to your computer and use it in GitHub Desktop.
Save steghio/ea6d32381f2e589fd3f09cfed07d65a7 to your computer and use it in GitHub Desktop.
Array utilities
package com.blogspot.groglogs.structures;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
//for apartmentWithMinimumFarthestDistanceFromFacilities
/*
If a facility is present at this apartment, its value in the map is true
initially an apartment tracks all known facilities and sets presence to false
two apartments are same if they have same id
*/
public class Apartment {
public int id;
public Map<Facility, Boolean> facilities;
public Apartment(int id){
this.id = id;
this.facilities = new HashMap<>();
for(Facility f : Facility.values()){
this.facilities.put(f, false);
}
}
public void addFacility(Facility f){
this.facilities.put(f, true);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Apartment)) return false;
Apartment a = (Apartment) o;
return this.id == a.id;
}
@Override
public int hashCode() {
return Objects.hash(this.id);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import com.blogspot.groglogs.structures.Apartment;
import com.blogspot.groglogs.structures.Facility;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ApartmentWithMinimumFarthestDistanceFromFacilitiesJTests {
Apartment[] apartments;
Facility[] facilities;
int out;
@Test
public void badInput() {
apartments = null;
facilities = null;
try{
out = ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities);
}catch(IllegalArgumentException e){
System.out.println("null input got IllegalArgumentException: " + e.getMessage());
}
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
facilities = null;
try{
out = ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities);
}catch(IllegalArgumentException e){
System.out.println("null facilities array got IllegalArgumentException: " + e.getMessage());
}
apartments = null;
facilities = new Facility[1];
facilities[0] = Facility.GYM;
try{
out = ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities);
}catch(IllegalArgumentException e){
System.out.println("null apartments array got IllegalArgumentException: " + e.getMessage());
}
apartments = new Apartment[1];
apartments[0] = null;
facilities = new Facility[1];
facilities[0] = Facility.GYM;
try{
out = ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities);
}catch(IllegalArgumentException e){
System.out.println("null values in apartments got IllegalArgumentException: " + e.getMessage());
}
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
facilities = new Facility[1];
facilities[0] = null;
try{
out = ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities);
}catch(IllegalArgumentException e){
System.out.println("null values in facilities got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void noSingleApartmentMatchesSingleFacility() {
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
facilities = new Facility[1];
facilities[0] = Facility.GYM;
out = -1;
assertEquals("no single apartment matches single Facility expected -1", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void noMultipleApartmentsMatchSingleFacility() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[1] = new Apartment(1);
facilities = new Facility[1];
facilities[0] = Facility.GYM;
out = -1;
assertEquals("no multiple apartments match single Facility expected -1", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void noSingleApartmentMatchesMultipleFacilities() {
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = -1;
assertEquals("no single apartment matches multiple facilities expected -1", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void noMultipleApartmentsMatchMultipleFacilities() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[1] = new Apartment(1);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = -1;
assertEquals("no multiple apartments match multiple facilities expected -1", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void noSingleApartmentMatchesAllFacilities() {
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = -1;
assertEquals("no single apartment matches all Facilities expected -1", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void noMultipleApartmentsMatchAllFacilities() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
apartments[1] = new Apartment(1);
apartments[1].addFacility(Facility.GYM);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = -1;
assertEquals("no multiple apartment match all Facilities expected -1", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void singleApartmentMatchesSingleFacility() {
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
facilities = new Facility[1];
facilities[0] = Facility.GYM;
out = 0;
assertEquals("single apartment matches single facility expected 0", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void singleApartmentMatchesMultipleFacilities() {
apartments = new Apartment[1];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
apartments[0].addFacility(Facility.SCHOOL);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = 0;
assertEquals("single apartment matches multiple facilities expected 0", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void multipleApartmentsMatchSingleFacility() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
apartments[1] = new Apartment(1);
apartments[1].addFacility(Facility.GYM);
facilities = new Facility[1];
facilities[0] = Facility.GYM;
out = 1;
assertEquals("multiple apartments match single facility expected last", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void multipleApartmentsMatchMultipleFacilities() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
apartments[0].addFacility(Facility.SCHOOL);
apartments[1] = new Apartment(1);
apartments[1].addFacility(Facility.GYM);
apartments[1].addFacility(Facility.SCHOOL);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = 1;
assertEquals("multiple apartments match multiple facilities expected last", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void multipleApartmentsOnlyOneMatchesSingleFacility() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.SCHOOL);
apartments[1] = new Apartment(1);
apartments[1].addFacility(Facility.GYM);
facilities = new Facility[1];
facilities[0] = Facility.SCHOOL;
out = 0;
assertEquals("multiple apartments only one matches single facility expected 0", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void multipleApartmentsOnlyOneMatchesMultipleFacilities() {
apartments = new Apartment[2];
apartments[0] = new Apartment(0);
apartments[0].addFacility(Facility.GYM);
apartments[0].addFacility(Facility.SCHOOL);
apartments[1] = new Apartment(1);
apartments[1].addFacility(Facility.GYM);
facilities = new Facility[2];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
out = 0;
assertEquals("multiple apartments only one matches multiple facilities expected 0", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
@Test
public void multipleApartmentsMultipleFacilities() {
facilities = new Facility[3];
facilities[0] = Facility.GYM;
facilities[1] = Facility.SCHOOL;
facilities[2] = Facility.STORE;
apartments = new Apartment[5];
/*
gym = 1
school = 0
store = 4
max = 4
*/
Apartment a0 = new Apartment(0);
a0.addFacility(Facility.SCHOOL);
apartments[0] = a0;
/*
gym = 0
school = 1
store = 3
max = 3
*/
Apartment a1 = new Apartment(1);
a1.addFacility(Facility.GYM);
apartments[1] = a1;
/*
gym = 0
school = 0
store = 2
max = 2
*/
Apartment a2 = new Apartment(2);
a2.addFacility(Facility.GYM);
a2.addFacility(Facility.SCHOOL);
apartments[2] = a2;
/*
gym = 1
school = 0
store = 1
max = 1
*/
Apartment a3 = new Apartment(3);
a3.addFacility(Facility.SCHOOL);
apartments[3] = a3;
/*
gym = 2
school = 0
store = 0
max = 2
*/
Apartment a4 = new Apartment(4);
a4.addFacility(Facility.SCHOOL);
a4.addFacility(Facility.STORE);
apartments[4] = a4;
out = 3;//since its max travel distance is 1
assertEquals("multiple apartments multiple facilities expected 3", out, ArrayUtils.apartmentWithMinimumFarthestDistanceFromFacilities(apartments, facilities));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class ArrayOfDoubledPairsJTests {
int[] numbers;
@Test
public void nullEmpty() {
numbers = null;
assertFalse("null", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{};
assertFalse("empty", ArrayUtils.arrayOfDoubledPairs(numbers));
}
@Test
public void oddLength() {
numbers = new int[]{1};
assertFalse("1", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{1,2,3};
assertFalse("1,2,3", ArrayUtils.arrayOfDoubledPairs(numbers));
}
@Test
public void negative() {
numbers = new int[]{-5,1,2,4};
assertFalse("-5,1,2,4", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{-5,1,2,-10};
assertTrue("-5,1,2,-10", ArrayUtils.arrayOfDoubledPairs(numbers));
}
@Test
public void duplicates() {
numbers = new int[]{2,1,1,2};
assertTrue("2,1,1,2", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{2,1,2,2};
assertFalse("2,1,2,2", ArrayUtils.arrayOfDoubledPairs(numbers));
}
@Test
public void ascending() {
numbers = new int[]{1,2,3,6};
assertTrue("1,2,3,6", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{1,2,3,7};
assertFalse("1,2,3,7", ArrayUtils.arrayOfDoubledPairs(numbers));
}
@Test
public void descending() {
numbers = new int[]{6,3,2,1};
assertTrue("6,3,2,1", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{7,3,2,1};
assertFalse("7,3,2,1", ArrayUtils.arrayOfDoubledPairs(numbers));
}
@Test
public void sample() {
numbers = new int[]{4,-2,2,-4};
assertTrue("4,-2,2,-4", ArrayUtils.arrayOfDoubledPairs(numbers));
numbers = new int[]{3,1,3,6};
assertFalse("3,1,3,6", ArrayUtils.arrayOfDoubledPairs(numbers));
}
}
package com.blogspot.groglogs.arrayutils;
import com.blogspot.groglogs.comparator.BooleanInvertedComparator;
import com.blogspot.groglogs.structures.Apartment;
import com.blogspot.groglogs.structures.Facility;
import com.blogspot.groglogs.structures.IntWrapper;
import com.blogspot.groglogs.structures.Pair;
import com.blogspot.groglogs.structures.Quadruple;
import com.blogspot.groglogs.structures.Range;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
public class ArrayUtils {
private static void swap(int[] a, int i, int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
private static void swap(char[] a, int i, int j){
char tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
/*
Moves all the elements that are smaller than the one at the index on the left
followed by the ones that are equal to it and lastly the ones that are greater
*/
public static void partitionArray(int[] a, int index){
if(a == null || index > a.length - 1) throw new IllegalArgumentException("Array must be not null and index must exist in array");
/*
We divide the array in 4 regions: smaller, equal, unclassified, larger
We then proceed to partition the elements in a single pass
equal is the pointer to the beginning of the unclassified section and larger the pointer to the end
At the start smaller = equal and larger = end of array
It is not guaranteed that at each pass the array will be in a consistent state
but it will be by the end of the algorithm
*/
int smaller = 0, equal = 0, larger = a.length - 1, pivot = a[index];
//Use <= because in the else we do not update equals, so we might be left with a last swap to make
while(equal <= larger){
/*
if current element is smaller, swap with last of the smaller elements and move both pointers up
we might swap the element with itself, but we are also reducing the unclassified section by 1
*/
if(a[equal] < pivot){
swap(a, equal, smaller);
smaller++;
equal++;
}
/*
if current element is same, reduce unclassified by 1 but do not move smaller because we might have
another element to add there later and therefore we might have to swap again the first equal element
we are borrowing space from
*/
else if(a[equal] == pivot) equal++;
/*
if current element is larger, swap with first of larger elements (last of unclassified)
and only move larger pointer back by 1 but do not move equals, because we borrowed space from
an unclassified element we still need to check
*/
else{
swap(a, equal, larger);
larger--;
}
}
}
/*
Evaluate a reverse polish notation expression. Operators immediately follow the operands or partial result they should be applied to
eg:
3 + 4 -> 3 4 +
(3 x 2) - 6 -> 3 2 x 6 -
3 - (4 x 5) -> 3 4 5 x -
We handle only doubles at most
*/
public static double evaluateRPN(String[] expression){
if(expression == null || expression.length < 3) throw new IllegalArgumentException("expression must have at least an operand and two operators: " + expression);
Stack<Double> s = new Stack<>();
/*
Start processing the string.
To avoid creating the stack beforehand, everytime we encounter an operand, we push it on the stack
then later when we encounter an operator, we retrieve the two operands from the stack,
process them in REVERSE order, then push the result back on the stack.
The reverse order is important for asymmetrical operations such as - and / since the stack is LIFO!
*/
String curr;
Double op1, op2, tmp;
for(int i = 0; i < expression.length; i++){
curr = expression[i];
switch(curr){
case "+":
op1 = s.pop();
op2 = s.pop();
s.push(op2 + op1);
break;
case "-":
op1 = s.pop();
op2 = s.pop();
s.push(op2 - op1);
break;
case "*":
case "x":
case "X":
op1 = s.pop();
op2 = s.pop();
s.push(op2 * op1);
break;
case "/":
op1 = s.pop();
op2 = s.pop();
//double objects do not fail on division by 0 :)
tmp = op2 / op1;
if(tmp.isInfinite()) throw new ArithmeticException("Divide by 0: " + op2 + "/" + op1);
s.push(tmp);
break;
default:
//push the operand onto the stack
s.push(Double.parseDouble(curr));
break;
}
}
//get the result
tmp = s.pop();
//if the stack is not empty by now, the expression was malformed!
if(!s.isEmpty()) throw new IllegalArgumentException("Malformed expression, got at least two operands with no operator");
return tmp;
}
//helper for evaluateRPNinPlace
private static double findOperand(String[] expression, int i){
double result;
//get the value to return. If we are not a pointer (no null precedes us), return us
if(i - 1 < 0 || expression[i - 1] != null){
result = Double.parseDouble(expression[i]);
//mark us and set the pointer in the next element to whatever precedes us
expression[i] = null;
expression[i + 1] = String.valueOf(i - 1);
}
//otherwise find the value at the pointer and return that
else{
int idx = Integer.parseInt(expression[i]);
result = Double.parseDouble(expression[idx]);
//mark us and set the pointer in the next element to whatever precedes us, unless it's another pointer
//in which case, set it to that specific pointer
expression[i] = null;
//careful with out of bounds, check if the pointer is not to the first element
if(idx - 1 >= 0){
//check if the pointer has a previous element
if(idx - 2 >= 0){
//verify whether it's a marker, if not, point to the item preceding the pointer
if(expression[idx - 2] != null) expression[i + 1] = String.valueOf(idx - 1);
//if yes, point to the same item as it, so we create a jump and avoid the need for scanning the next time we come here
else expression[i + 1] = expression[idx - 1];
}
//point to the first element, otherwise we would miss this
else expression[i + 1] = String.valueOf(idx - 1);
}
}
return result;
}
/*
Evaluate a reverse polish notation expression. Operators immediately follow the operands or partial result they should be applied to
eg:
3 + 4 -> 3 4 +
(3 x 2) - 6 -> 3 2 x 6 -
3 - (4 x 5) -> 3 4 5 x -
We handle only doubles at most
Do NOT use a stack and do this like real men in constant space. Idea is to recycle the input array, who needs it anymore anyway.
Everytime we find an operator, the two operands must appear before it, in a normal case they are at index -1 (second operand) and -2 (first operand)
We calculate the result and store it in place of the operator, then we mark where the next operand would be and reuse the space
from the two operands we just had. From left to right, the second operand will be the marker (null value)
to indicate that a pointer is following this item and the first operand will be the pointer.
Whenever we encounter a marker, we know we have to return the element at the index specified in marker + 1
To keep this in constant space, we have to avoid scanning the array backwards if more pointers appear together
so whenever we set a pointer value, we also verify that it is not already a pointer itself, in which case we reuse the
previous pointer value to jump at the desired position, thus avoiding the scanning.
The result will always be in the last element of the array
eg:
3 4 5 x -
3 n 0 20 -
3 n n 20 -17
*/
public static double evaluateRPNinPlace(String[] expression){
if(expression == null || expression.length < 3) throw new IllegalArgumentException("expression must have at least an operand and two operators: " + expression);
try{
Double.parseDouble(expression[expression.length - 1]);
//if we can parse the last symbol, the expression is malformed
throw new IllegalArgumentException("Malformed expression, got at least two operands with no operator");
}catch(NumberFormatException e){
//all good, we expect an operator as last symbol
}
String curr;
double op1, op2, tmp;
for(int i = 0; i < expression.length; i++){
curr = expression[i];
//for correct pointer handling, we ALWAYS retrieve the second operand first (it's always before the operator)
//then we retrieve the first operand (we might need to jump in this case)
switch(curr){
case "+":op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
expression[i] = String.valueOf(op1 + op2);
break;
case "-":
op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
expression[i] = String.valueOf(op1 - op2);
break;
case "*":
case "x":
case "X":
op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
expression[i] = String.valueOf(op1 * op2);
break;
case "/":
op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
//double objects do not fail on division by 0 :)
tmp = op1 / op2;
if(Double.isInfinite(tmp)) throw new ArithmeticException("Divide by 0: " + op1 + "/" + op2);
expression[i] = String.valueOf(op1 / op2);
break;
default:
//nothing to do here
break;
}
}
return Double.parseDouble(expression[expression.length - 1]);
}
/*
Helper for convertToRPN
if op1 has same or higher precedence than op2, return true
Ranking:
- multiplication and division
- sum and subtraction
*/
public static boolean hasPrecedence(String op1, String op2){
switch(op2) {
case "+":
case "-":
case "*":
case "x":
case "X":
case "/":
//all good, we accept these operators
break;
default:
throw new IllegalArgumentException("Unknown operator: " + op2);
}
switch(op1) {
case "+":
case "-":
return op2.equals("+") || op2.equals("-");
case "*":
case "x":
case "X":
case "/":
//these are always top priority and we already tested for the lower priority ones
return true;
default:
throw new IllegalArgumentException("Unknown operator: " + op1);
}
}
/*
Convert infix expression to reverse polish notation (postfix)
Based on Dijkstra shunting yard algorithm
*/
public static String[] convertToRPN(String[] expression){
if(expression == null || expression.length < 3) throw new IllegalArgumentException("expression must have at least an operand and two operators: " + expression);
Stack<String> operators = new Stack<>();
Queue<String> out = new LinkedList<>();
for(String curr : expression){
switch(curr) {
case "+":
case "-":
case "*":
case "x":
case "X":
case "/":
//check operator precedence: multiplication and division are same and go before sum and subtraction
//as long as we find same or higher precedence, we need to add those to the result
//skip parenthesis! they might be the very first element, respect their meaning!
while (!operators.isEmpty() && !operators.peek().equals("(") && !operators.peek().equals(")") && hasPrecedence(operators.peek(), curr)) {
out.add(operators.pop());
}
operators.push(curr);
break;
case "(":
operators.push(curr);
break;
case ")":
while(!operators.isEmpty() && !operators.peek().equals("(")) out.add(operators.pop());
//if we do not have a matching parentheses, we got a malformed expression, otherwise we remove the matching opening bracket
try{
operators.pop();
}catch(EmptyStackException e){
throw new IllegalArgumentException("Mismatched parentheses found, missing '('");
}
break;
default:
//check if it's a number or garbage, if ok, move to output
try {
//this will throw an exception for us if it is not a number, therefore the expression is malformed
Double.parseDouble(curr);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Unknown token found while parsing, expected number, got: " + curr);
}
out.add(curr);
break;
}
}
//finish adding the last operators to the output, if we have parentheses, the expression was malformed
while(!operators.isEmpty()){
String curr = operators.pop();
if(curr.equals("(") || curr.equals(")")) throw new IllegalArgumentException("Mismatched parentheses found: " + curr);
out.add(curr);
}
//convert the output to a properly sized array
String[] res = new String[out.size()];
int i = -1; //convenient for ++i later
while(!out.isEmpty()){
res[++i] = out.poll();
}
return res;
}
/*
Given an array of integers in the range 1..N and length N+1 find one of the possible duplicate elements.
To run in constant space without destroying the input, we treat the array as a graph
where each value is a pointer to another element of the same array.
Start from the LAST element, since range is 1..N and length is N+1 but arrays are 0-based, so nothing can point to it
and keep iterating until a cycle is identified, therefore a duplicate element is found.
Always handle pointers - 1 since array is 0 based!
Cycle detection is based on Floyd's algorithm
*/
public static int findRepeat(int[] input) {
if(input == null || input.length < 2) throw new IllegalArgumentException("Array length must be at least 2");
//use two pointers, one ahead of the other starting from the end of the array
int slow = input[input.length - 1], fast = input[slow - 1];
//identify the loop
while(slow != fast){
slow = input[slow - 1];
fast = input[input[fast - 1] - 1];
}
//search for the duplicate element
slow = input.length;
while(slow != fast){
slow = input[slow - 1];
fast = input[fast - 1];
}
return slow;
}
/*
Given an array of integers in the range 1..N and length N+1 find one of the possible duplicate elements.
To run in constant space without destroying the input, we treat the array as a graph
where each value is a pointer to another element of the same array.
Start from the LAST element, since range is 1..N and length is N+1 but arrays are 0-based, so nothing can point to it
and keep iterating until a cycle is identified, therefore a duplicate element is found.
Always handle pointers - 1 since array is 0 based!
Cycle detection is based on Brent's algorithm
*/
public static int findRepeatBrent(int[] input) {
if(input == null || input.length < 2) throw new IllegalArgumentException("Array length must be at least 2");
int slow = input[input.length - 1], fast = input[slow - 1], limit = 1, step = 1; //optional: idx = 0
while(slow != fast){
if(step == limit){
slow = fast;
limit *= 2;
step = 0;
}
fast = input[fast - 1];
step++;
}
slow = fast = input[input.length - 1];
for(int i = 0; i < step; i++) fast = input[fast - 1];
while(slow != fast){
slow = input[slow - 1];
fast = input[fast - 1];
//optional idx++
}
//optional idx would tell us after how many jumps from the start we encounter the loop start
return slow;
}
/*
Given an array of integers in the range 1..N and length N+1 find one of the possible duplicate elements.
Without using the graph idea it is still possible to return the result in constant space without destroying the input
but time will be slower O(N log N).
This implementation applies the binary search logic to iteratively limit the set of possible duplicate elements
and zone in one of them.
REMEMBER 0-base index arrays! In this case it ONLY applies to ceiling being length-1!
We divide the search space in two halves: 1..N/2 and (N/2 + 1)..N so both halves together still cover the full range 1..N
CAUTION! starting floor is 1 and NOT 0 since items are in range 1..N!
and we know at least one of the halves contains at least one duplicate element. Therefore we can count the number of
distinct possibilities for an item in the range we are considering and compare it to the actual number of items in that range
in the FULL array! We are just looking at each time if in a specific, constantly shrinking search space, we have one or more
items that in the FULL input appear multiple times.
If this is greater than the possibilities, we definitely have a duplicate there, therefore we can repeat the process
ITERATIVELY otherwise we get an increased space cost because of the call stack, until we are left with a single element, which
is one of our duplicates
*/
public static int findRepeatBinarySearch(int[] input) {
if(input == null || input.length < 2) throw new IllegalArgumentException("Array length must be at least 2");
//value range is 1..N and arrays are 0-based!
int floor = 1, ceil = input.length - 1, mid, lowFloor, lowCeil, highFloor, highCeil, possibilities, count;
//keep searching the possibilities space by evaluating a shrinking range of candidates for the duplicate value
while(floor < ceil){
mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
lowFloor = floor;
lowCeil = mid;
highFloor = mid + 1;
highCeil = ceil;
//how many distinct candidates do we have in this search range
possibilities = lowCeil - lowFloor + 1;
count = 0;
/*
Count how many items are in this search range from the FULL array because we are NOT reducing the array size
but the SEARCH SPACE size. We will perform O(log N) iterations before the search range is so small we identify the item
but each time we have to evaluate the FULL array O(N) against the current search range -> O(N log N)
This is the key: if there are no duplicates, the number of candidates and the number of values in the range must match
But since we know there IS at least one duplicate, we will at some point find a range where the number of items
is greater than the possibilities, meaning we have at least a duplicate there
*/
for(int i : input) if(i >= lowFloor && i <= lowCeil) count++;
//move the search range to the one that contains the duplicate item
if(count > possibilities){
floor = lowFloor;
ceil = lowCeil;
}
else{
floor = highFloor;
ceil = highCeil;
}
}
//when the search range has converged, we have identified the duplicate
return floor;
}
/*
The input is an array of elevations, the goal is to determine the maximum
amount of water that can be stored in the lakes, if any. Sea level is 0
*/
public static int getIslandLakesCapacity(int[] island){
if(island == null) throw new IllegalArgumentException("Island cannot be null!");
//track each depression how much water can it take
//we scan twice, from both directions. In the first pass we find how much water can be stored
//from the left border to the highest peak. At the end we track the highest peak height and
//scan again from the right border. We stop as soon as we find a peak as high as the highest
//then we're sure we correctly calculated the full island capacity
int[] water = new int[island.length];
int curr = 0;
//left to right scan
for(int i = 0; i < island.length; i++){
if(island[i] > curr) curr = island[i];
water[i] = curr - island[i];
}
//track highest peak
int max = curr;
curr = 0;
//right to left scan
for(int i = island.length - 1; i >= 0; i--){
//stop as soon as we reached the highest peak
if(island[i] == max) break;
if(island[i] > curr) curr = island[i];
water[i] = curr - island[i];
}
int tot = 0;
//get the final lake capacity
for(int i = 0; i < water.length; i++){
tot += water[i];
}
return tot;
}
//performs the Fisher-Yates shuffle of a given array
//guaranteed to generated a uniformly distributed random permutation of the input array
public static void FisherYatesShuffle(int[] a){
//nothing to shuffle, return input
if(a == null || a.length < 2) return;
//instance a new random generator
Random gen = new Random();
//place all randomly selected elements at the end of the array
//then decrease the pool size before getting the next random element position
//repeat for the whole array
for(int curr = a.length - 1; curr >= 0; curr--){
//random between 0 inclusive and curr + 1 exclusive, effectively making [0..curr] range
int swapWith = gen.nextInt(curr + 1);
//no need to swap a place with itself
if(curr != swapWith){
swap(a, curr, swapWith);
}
}
}
//helper for getMaxGoldFromPotsNoDP
private static int findMaxPickNoDP(int[] pots, int floor, int ceil, boolean player){
//stop when we are at the last pot
if(floor == ceil){
//if it's our pick, add it to the value, else we get nothing
return player ? pots[floor] : 0;
}
//if it's not our turn, the opponent will leave us with the worst pick. use MIN and do NOT pick!
if(!player) return Math.min(findMaxPickNoDP(pots, floor + 1, ceil, !player),
findMaxPickNoDP(pots, floor, ceil - 1, !player)
);
//otherwise PICK and find the MAX!
return Math.max(pots[floor] + findMaxPickNoDP(pots, floor + 1, ceil, !player),
pots[ceil] + findMaxPickNoDP(pots, floor, ceil - 1, !player)
);
}
/*
Given a series of gold pots with different contents, two players can alternatively pick a pot from either side.
Find the maximum amount of gold the first player can get.
starter indicates the starting player (true = first player, false = second player)
*/
public static int getMaxGoldFromPotsNoDP(int[] pots, boolean starter){
if(pots == null) throw new IllegalArgumentException("Pots can't be null!");
return findMaxPickNoDP(pots, 0, pots.length - 1, starter);
}
//helper for getMaxGoldFromPots
private static int findMaxPick(int[] pots, int floor, int ceil, boolean player, int[][][] cache){
//convert player to matrix index
int p = player ? 1 : 0;
//if we already calculated the value, return immediately
int pick = cache[p][floor][ceil];
if(pick != -1) return pick;
//stop when we are at the last pot
if(floor == ceil){
//if it's our pick, add it to the value, else we get nothing
pick = player ? pots[floor] : 0;
//cache the value
cache[p][floor][ceil] = pick;
return pick;
}
//maybe we already calculated it
int left = cache[p][floor + 1][ceil];
int right = cache[p][floor][ceil - 1];
//if not, calculate
if(left == -1) left = findMaxPick(pots, floor + 1, ceil, !player, cache);
if(right == -1) right = findMaxPick(pots, floor, ceil - 1, !player, cache);
//if it's not our turn, the opponent will leave us with the worst pick. use MIN and do NOT pick!
if(!player){
pick = Math.min(left, right);
//otherwise PICK and find the MAX!
} else {
pick = Math.max(pots[floor] + left, pots[ceil] + right);
}
//cache the value
cache[p][floor][ceil] = pick;
return pick;
}
/*
Given a series of gold pots with different contents, two players can alternatively pick a pot from either side.
Find the maximum amount of gold the first player can get.
starter indicates the starting player (true = first player, false = second player)
*/
public static int getMaxGoldFromPots(int[] pots, boolean starter){
if(pots == null) throw new IllegalArgumentException("Pots can't be null!");
//initialize cache for players and picks
int[][][] cache = new int[2][pots.length][pots.length];
for(int i = 0; i < pots.length; i++){
for(int j = 0; j < pots.length; j++){
cache[0][i][j] = -1;
cache[1][i][j] = -1;
}
}
return findMaxPick(pots, 0, pots.length - 1, starter, cache);
}
//helper for reverseWords. Simply reverses all items in a given array from start to end index
private static void reverseWord(char[] in, int start, int end){
for(; start < end; start++, end--){
swap(in, start, end);
}
}
//reverse words order in a char array. Words are separated by a single space and no other characters are allowed
public static void reverseWords(char[] in){
if(in == null || in.length == 1) return;
for(int i = 0, j = in.length - 1; i < j; i++, j--){
swap(in, i, j);
}
int start_idx = 0;
//walk the full array and track start and end index for each word, reversing it if we find a delimiter or reach the end of the array
for(int i = 0; i <= in.length; i++){
if(i == in.length || in[i] == ' '){
//i-1! the word ends BEFORE us
reverseWord(in, start_idx, i - 1);
//next word starts for sure after the delimiter
start_idx = i + 1;
}
}
}
/*find the subarray with the largest sum
for an array of all negative numbers, return the smallest of them
for an array of all positive numbers, return the sum of all element
*/
public static Range getLargestSumSubarray(int[] in){
if(in == null) throw new IllegalArgumentException("null array is not allowed!");
//track the global maximum and the local maximum. Local maximum is the sum of elements until the one being currently considered
//initialize both the first array element
int curr = in[0], max = in[0], start = 0;
Range res = new Range(0, 0, max);
//and then remember to skip if when starting the loop!
for(int i = 1; i < in.length; i++){
//local sum can either be extended if the current element improves on it, or reset to start from the current element otherwise
int partial = curr + in[i];
if(partial > in[i]){
curr = partial;
}
else{
curr = in[i];
start = i;
}
//always update the global maximum
if(curr > max){
max = curr;
res.start = start;
res.end = i;
res.tot = max;
}
}
return res;
}
/*
Given an array indicating how many people live at the specific index, return the index where to place
a postbox to minimize the total travel time to it for all people.
People residing in the same place of the postbox have a total travel time of 0
N people needing to take 1 step to reach the postbox have a total travel time of N
Multiple solutions could be acceptable, this algorithm will return the first best found starting from the END
*/
public static int getPostboxPlace(int[] in){
if(in == null) throw new IllegalArgumentException("null array is not allowed!");
if(in.length == 1) return 0; //no need to do anything
//track here all the steps people would have to do to reach each position moving
//from left to right, do not consider the other positions yet
int[] steps = new int[in.length];
steps[0] = 0;
int tot = in[0];
//at each position, the people in the previous position must do ONE step
//PLUS ALL the people from before have to do ONE EXTRA step
for(int i = 1; i < in.length; i++){
steps[i] = tot + steps[i - 1];
tot += in[i];
}
int min = Integer.MAX_VALUE, place = -1, prev = 0, curr = 0;
tot = 0;
//walking right to left, use the previously calculated information from the array
//with its counterpart which we can now calculate on the fly, to determine the cost of placing the postbox in the current position
//and keep tracking the best result found so far
for(int i = in.length - 1; i > 0; i--){
//steps[i] (right to left) + steps[i] (left to right)
if(steps[i] + curr < min){
min = steps[i] + curr;
place = i;
}
//we do not need to build another array, these lines are the key
prev = curr; //prev is steps[i+1] for the array of steps from left to right
tot += in[i];
curr = tot + prev; //this is steps[i] for the next round
}
return place;
}
/*
Given a start and end array, generate a list of positions where to swap the 0 element in order to move all elements
from start to end position.
Elements can only be swapped with the 0
Each element can only appear once
There can be more than one way to obtain the desired swap
*/
public static List<Integer> forcedSwaps(int[] start, int[] end){
if(start == null || end == null) throw new IllegalArgumentException("Start and end array cannot be null!");
if(start.length != end.length) throw new IllegalArgumentException("Start and end array must have same length!");
//current position in the intermediate array (we recycle start) of each element
Map<Integer, Integer> pos = new HashMap<>();
boolean needsSwap = false;
//needed to check validity of input
Set<Integer> start_elements = new HashSet<>();
Set<Integer> end_elements = new HashSet<>();
for(int i = 0; i < start.length; i++){
if(start_elements.contains(start[i]) || end_elements.contains(end[i])) throw new IllegalArgumentException("Duplicate entries not allowed!");
start_elements.add(start[i]);
end_elements.add(end[i]);
pos.put(start[i], i);
needsSwap = needsSwap || start[i] != end[i];
}
if(!start_elements.containsAll(end_elements)) throw new IllegalArgumentException("Start and end array must have the same elements!");
if(!start_elements.contains(0)) throw new IllegalArgumentException("Element 0 must be in the arrays!");
List<Integer> out = new LinkedList<>();
if(!needsSwap) return out;
//start from first element and verify if it matches the desired position
//if not, swap it with the 0
//then check again, if it still does not match, swap the 0 with the expected element
//always track the new position of each element
//then move to the next position
int curr = 0;
while(curr < end.length){
if(start[curr] != end[curr]){
//move 0 to current position
int val = start[curr]; //element at current position
int to;
//if current element is already 0, no need to do anything
if(val != 0){
start[curr] = 0;
to = pos.get(0); //we move the element at the current position of the 0
pos.put(0, curr);
start[to] = val;
pos.put(val, to);
out.add(curr); //we moved the 0 here
}
//if necessary swap again the 0 with the desired element
if(start[curr] != end[curr]){
start[curr] = end[curr];
to = pos.get(end[curr]);
pos.put(end[curr], curr);
pos.put(0, to);
start[to] = 0;
out.add(to); //we moved the 0 here as well
}
}
curr++;
}
return out;
}
/*
Given an array of Apartments, each indicating which Facilities are present there, and an array of relevant facilities
we need to find which apartment minimizes the farthest travel distance from all relevant facilities.
If a facility is present at the apartment, travel distance is 0.
If two facilities are at distance one from the apartment, the maximum travel distance is 1, not the sum.
We need to return the index of the apartment that satisfies our criteria.
If multiple apartments satisfy our criteria, we return any of them.
If no apartment satisfies our criteria, we return -1.
*/
public static int apartmentWithMinimumFarthestDistanceFromFacilities(Apartment[] apartments, Facility[] facilities){
if(apartments == null || facilities == null || apartments.length == 0 || facilities.length == 0) throw new IllegalArgumentException("At least one apartment and facility must be provided");
//rows = apartment, columns = minimum distance from facility
int[][] distances = new int[apartments.length][facilities.length];
//init our matrix with MAX_INT as initial distance
for(int i = 0; i < apartments.length; i++){
for(int j = 0; j < facilities.length; j++){
if(apartments[i] == null || facilities[j] == null) throw new IllegalArgumentException("Apartments and facilities cannot be null");
distances[i][j] = Integer.MAX_VALUE;
}
}
/*walk from first apartment to the right, we calculate PARTIAL distances for all facilities seeing what information we
could gather given the apartments visited so far
If an apartment has a facility there, distance is 0, otherwise we use distance for that facility
from previous apartment + 1, if it existed, otherwise we haven't encountered that facility yet and we keep MAX_INT
*/
for(int i = 0; i < apartments.length; i++){
for(int j = 0; j < facilities.length; j++){
//this facility is present at this apartment
if(apartments[i].facilities.get(facilities[j])){
distances[i][j] = 0;
continue;
}
//if not present, use distance for that facility from previous apt + 1
//if distance was infinite, avoid overflow
//if we are first apartment, avoid out of bounds
if(i - 1 >= 0 && distances[i - 1][j] != Integer.MAX_VALUE){
distances[i][j] = distances[i - 1][j] + 1;
}
}
}
//at this point, the last apartment is the only one with full knowledge, since there is no other after him
//so we can initialize the result to it
//we look for the MAXIMUM travel distance for this apartment from ANY facility
//later, we want to MINIMIZE this value
int currMinFarthestDistance = Integer.MIN_VALUE;
for(int j = 0; j < facilities.length; j++){
currMinFarthestDistance = Math.max(currMinFarthestDistance, distances[apartments.length - 1][j]);
}
int currBestApartment = apartments.length - 1;
/*since we already processed the last apartment, walk from second to last apartment to the left,
we calculate FINAL distances for all facilities seeing what information we
gathered given all apartments have been visited
We update if necessary the distance to the minimum between the current distance value at that apartment and
the distance from the previous apartment + 1
This means we now know where the closest facility is, therefore we might need to update our current value
*/
for(int i = apartments.length - 2; i >= 0; i--){
//while we update our knowledge, we also track where is the farthest facility for us
int farthestDistanceForThisApartment = Integer.MIN_VALUE;
for(int j = 0; j < facilities.length; j++){
//see if we now know there is a closer facility than previously thought
//it's either us or the next apartment + 1
//if distance was infinite, avoid overflow
if(distances[i + 1][j] != Integer.MAX_VALUE) {
distances[i][j] = Math.min(distances[i][j], distances[i + 1][j] + 1);
}
farthestDistanceForThisApartment = Math.max(farthestDistanceForThisApartment, distances[i][j]);
}
//if we are the best apartment so far, we update this information
if(currMinFarthestDistance > farthestDistanceForThisApartment){
currMinFarthestDistance = farthestDistanceForThisApartment;
currBestApartment = i;
}
}
//if a facility was missing everywhere, there is no best apartment
if(currMinFarthestDistance == Integer.MAX_VALUE){
return -1;
}
//otherwise we have here one of the best
return currBestApartment;
}
/*
Given an array of elements, find any position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearch(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearch(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
return binarySearch(elements, element, 0, elements.length - 1);
}
//helper for binarySearch
private static <T extends Comparable<T>> int binarySearch(T[] elements, T element, int floor, int ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)) return mid;
while(floor < ceil){
//if middle element is bigger than the one we're looking for, search on its left
if(elements[mid].compareTo(element) > 0) return binarySearch(elements, element, floor, mid - 1);
//otherwise search on its right
return binarySearch(elements, element, mid + 1, ceil);
}
return -1;
}
/*
Given an array of elements, find the FIRST position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchFirstOccurrenceRecursive(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearchFirstOccurrenceRecursive(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
return binarySearchFirstOccurrenceRecursive(elements, element, 0, elements.length - 1, -1);
}
//helper for binarySearchFirstOccurrenceRecursive
private static <T extends Comparable<T>> int binarySearchFirstOccurrenceRecursive(T[] elements, T element, int floor, int ceil, int result){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)) {
result = mid;
}
while(floor < ceil){
//if middle element is bigger or equal to the one we're looking for, search on its left since we're looking for FIRST occurrence
if(elements[mid].compareTo(element) >= 0) return binarySearchFirstOccurrenceRecursive(elements, element, floor, mid - 1, result);
//otherwise search on its right
return binarySearchFirstOccurrenceRecursive(elements, element, mid + 1, ceil, result);
}
return result;
}
/*
Given an array of elements, find the LAST position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchLastOccurrenceRecursive(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearchLastOccurrenceRecursive(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
return binarySearchLastOccurrenceRecursive(elements, element, 0, elements.length - 1, -1);
}
//helper for binarySearchLastOccurrenceRecursive
private static <T extends Comparable<T>> int binarySearchLastOccurrenceRecursive(T[] elements, T element, int floor, int ceil, int result){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)) {
result = mid;
}
while(floor < ceil){
//if middle element is bigger than the one we're looking for, search on its left
if(elements[mid].compareTo(element) > 0) return binarySearchLastOccurrenceRecursive(elements, element, floor, mid - 1, result);
//otherwise search on its right, even if equal since the LAST occurrence must be AFTER this one
return binarySearchLastOccurrenceRecursive(elements, element, mid + 1, ceil, result);
}
return result;
}
/*
Given an array of elements, find the any position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchIterative(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearchIterative(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)){
return mid;
}
//if middle element is bigger than the one we're looking for, search on its left
if(elements[mid].compareTo(element) >= 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return -1;
}
/*
Given an array of elements, find the FIRST occurrence of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchFirstOccurrence(elements, element)
If array is null, element does not fit inside
We use iterative approach since we need to remember the last found element in case of duplicates and it's easier than recursive to track
*/
public static <T extends Comparable<T>> int binarySearchFirstOccurrence(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1, result = -1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
//in this case, do not return immediately, rather save this location and search on its left
//if there are other prior duplicates, we will find them until we terminate the search
//at that point, in result we will have the last seen index of the element, which is the first occurrence
if(elements[mid].equals(element)){
result = mid;
}
//if middle element is bigger or equals than the one we're looking for, search on its left
//in this case we use equals since we are looking for first occurrence, so we want to move left
//even when a result was found
if(elements[mid].compareTo(element) >= 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return result;
}
/*
Given an array of elements, find the FIRST occurrence of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchFirstOccurrence(elements, element)
If array is null, element does not fit inside
We use iterative approach since we need to remember the last found element in case of duplicates and it's easier than recursive to track
*/
public static <T extends Comparable<T>> int binarySearchFirstOccurrence(T[] elements, T element, Comparator<T> comparator){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1, result = -1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
//in this case, do not return immediately, rather save this location and search on its left
//if there are other prior duplicates, we will find them until we terminate the search
//at that point, in result we will have the last seen index of the element, which is the first occurrence
if(elements[mid].equals(element)){
result = mid;
}
//if middle element is bigger or equals than the one we're looking for, search on its left
//in this case we use equals since we are looking for first occurrence, so we want to move left
//even when a result was found
if(comparator.compare(elements[mid],element) >= 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return result;
}
/*
Given an array of elements, find the FIRST occurrence of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchLastOccurrence(collection, element)
If array is null, element does not fit inside
We use iterative approach since we need to remember the last found element in case of duplicates and it's easier than recursive to track
*/
public static <T extends Comparable<T>> int binarySearchLastOccurrence(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1, result = -1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
//in this case, do not return immediately, rather save this location and search on its right
//if there are other duplicates after it, we will find them until we terminate the search
//at that point, in result we will have the last seen index of the element, which is the last occurrence
if(elements[mid].equals(element)){
result = mid;
}
//if middle element is bigger than the one we're looking for, search on its left
//in this case we do NOT use equals since we are looking for last occurrence
//therefore we want to move right even if element was found
if(elements[mid].compareTo(element) > 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return result;
}
/**
* Given a sorted array of unique elements that might have been rotated, find the position of a given element
* if it exists.
* @param target the element to find.
* @param nums the input elements.
* @return the position of the element if it exists, -1 otherwise
*/
public static int rotatedBinarySearch(int target, int... nums){
if(nums == null || nums.length == 0){
return -1;
}
/*
Do normal binary search, but each time we need to understand where to continue searching.
Given a middle element, either the left or right side will be sorted and the other side not (there will
be at least one element whose successor is not in order, unless array is all ascending or mid is the break point).
After checking the middle, we look at both sides to determine where the item would be and move in the correct
direction:
- element at floor < element at mid: left side is sorted
if element at floor <= target < element at mid, search there, otherwise search on the right of mid
- otherwise if that side is not sorted
if right side is sorted (element at mid <= element at ceil) AND
element at mid < target <= element at ceil
search on right side of mid
otherwise search on left side
*/
int floor = 0;
int ceil = nums.length - 1;
while(floor <= ceil){
int mid = floor + (ceil - floor) / 2;
if(nums[mid] == target){
return mid;
}
//left is sorted
if(nums[floor] <= nums[mid]){
//and solution is there
if(nums[mid] > target && nums[floor] <= target){
ceil = mid - 1;
}
else{
floor = mid + 1;
}
}
else{
//if right is sorted and solution is there
if(nums[mid] <= nums[ceil] && nums[mid] < target && nums[ceil] >= target){
floor = mid + 1;
}
else{
ceil = mid - 1;
}
}
}
return -1;
}
/**
* Given a sorted array of unique elements which might have been rotated, find the minimum element in it.
* @param nums the array.
* @return the minimum element in the array.
*/
public static int minimumRotatedSortedArray(int... nums){
if(nums == null || nums.length == 0){
throw new IllegalArgumentException("null or empty array not allowed");
}
int floor = 0;
int ceil = nums.length - 1;
while(floor <= ceil){
int mid = floor + (ceil - floor) / 2;
//if we don't have a next element, the array is of size 1
//or the array was sorted and not rotated so we always moved on the right
//until we reached the end, where we would wrap around and as next element have the first element
//which is the smallest
if(mid + 1 >= nums.length){
return nums[0];
}
//if this element is bigger than the next, this is the rotation point
//since elements are unique, the next is the smallest
if(nums[mid] > nums[mid + 1]){
return nums[mid + 1];
}
//if elem at floor > elem at mid the rotation is somewhere on the left
//which is where the smallest would be
if(nums[floor] > nums[mid]){
ceil = mid - 1;
}
//otherwise rotation is somewhere on the right
//this could also be the case the array is sorted and not rotated
//so we keep going right never find the element, but in that case we'll wrap around
//and pick the first as next element of the last, therefore finding the smallest
else{
floor = mid + 1;
}
}
//if we got here, the array was sorted and not rotated, first element is the smallest
//we need it for the code to compile, could just as well break in the loop when mid+1 > array size instead
return nums[0];
}
/*
Given a list of boolean arrays in the format
[
[true, true, true, false],
[true, false, false, false, false],
[true, true, true, true, true, true, false, false]
]
Where each internal array ALWAYS has at least 2 elements and starts with a "true"
Also, after the first "false" there can ONLY be "false" until end of array
We calculate for each of them the percentage of false elements over all elements, call it pct
We then look for the longest strictly increasing pct sequence
An empty list or a list of only one element returns 0
In all other cases, the sequence starts counting after the starting element
*/
public static int longestIncreasingFailedBuildSequence(List<Boolean[]> runs){
if(runs == null || runs.size() <= 1) return 0;
int longestIncreasing = 0, currLongestIncreasing = 0;
//init to calculation of first element
float lastFailPct = calculateFailedBuildPercentage(runs.get(0));
//start from second element and keep calculating and comparing with known longest sequences
for(int i = 1; i < runs.size(); i++){
float currFailPct = calculateFailedBuildPercentage(runs.get(i));
//if fail pct is increasing, update local counter
if(currFailPct > lastFailPct){
currLongestIncreasing++;
}
//otherwise check if we have found a new longest increasing sequence
else {
if(currLongestIncreasing > longestIncreasing){
longestIncreasing = currLongestIncreasing;
}
//reset curr longest counter
currLongestIncreasing = 0;
}
//track last fail pct for comparison with next sequence
lastFailPct = currFailPct;
}
//we also need to do one last comparison in case the longest sequence is at the very end
//otherwise we never reach the breaking point where we do the update
if(currLongestIncreasing > longestIncreasing){
longestIncreasing = currLongestIncreasing;
}
return longestIncreasing;
}
//helper for longestIncreasingFailedBuildSequence
private static float calculateFailedBuildPercentage(Boolean[] runSequence){
//we could scan the whole array, but given the structure it has
//it's more efficient to binary search the first occurrence of false
//since all elements before it will be true and all elements after it will be false
Comparator<Boolean> comparator = new BooleanInvertedComparator();
int firstFail = binarySearchFirstOccurrence(runSequence, false, comparator);
//we can now easily find the percentage of false
return (float)(runSequence.length - firstFail) / (float)(runSequence.length) * 100;
}
/**
* Given a target number k and a set of numbers, return two possible numbers that sum up to k.
* @param k the target number.
* @param numbers the available numbers.
* @return two possible numbers that sum up to k or null if no given pair satisfies the requirement.
*/
public static Pair<Integer, Integer> numbersAddUpToK(int k, int... numbers){
//if we have less than 2 numbers, no need to even try
if(numbers == null || numbers.length < 2){
return null;
}
Set<Integer> complements = new HashSet<>();
//for each number we have, we need to find its complement to sum up to k
for(int i : numbers){
//if we are the complement of someone, we have found a solution
if(complements.contains(i)){
//added in this order they also reflect the order in which we found them in the input
return new Pair<>(k - i, i);
}
//otherwise track our complement
complements.add(k - i);
}
return null;
}
/**
* Given an array of numbers, create a new array where each position is the result of the multiplication of
* all other elements in the array, excluding the element at that position.
* This variant does not allow zeros in input.
* @param numbers the input numbers, zeros not allowed.
* @return an array where each position is the result of the multiplication of all other element in the source
* excluding the element in that position.
*/
public static int[] prodAllExcludingElementAtINotAllowZero(int... numbers){
if(numbers == null || numbers.length < 2){
return new int[]{};
}
//we multiply all values in the array and find a grand total
int tot = 1;
for(int number : numbers){
if(number == 0){
throw new IllegalArgumentException("Zero elements not allowed");
}
tot *= number;
}
int[] out = new int[numbers.length];
//now for each element, the result is total excluded the element, so we just divide it out
for(int i = 0; i < out.length; i++){
out[i] = tot/numbers[i];
}
return out;
}
/**
* Given an array of numbers, create a new array where each position is the result of the multiplication of
* all other elements in the array, excluding the element at that position.
* This variant allows zeros in input.
* @param numbers the input numbers, zeros allowed.
* @return an array where each position is the result of the multiplication of all other element in the source
* excluding the element in that position.
*/
public static int[] prodAllExcludingElementAtIAllowZero(int... numbers){
if(numbers == null || numbers.length < 2){
return new int[]{};
}
int[] leftRight = new int[numbers.length], rightLeft = new int[numbers.length], out = new int[numbers.length];
//pass input from left to right and use formula lr[i] = lr[i-1] * input[i-1]
//first element has no predecessor, we set to 1
//this way we have the partial result of the multiplications of all elements BEFORE me, therefore in MY position
//i am excluded from that multiplication
leftRight[0] = 1;
for(int i = 1; i < numbers.length; i++){
leftRight[i] = leftRight[i - 1] * numbers[i - 1];
}
//repeat the same logic but this time move right to left to get the partial results from that pass
//so we have the product of all elements AFTER me and in MY position I am excluded from the multiplication
rightLeft[numbers.length - 1] = 1;
for(int i = numbers.length - 2; i >= 0; i--){
rightLeft[i] = rightLeft[i + 1] * numbers[i + 1];
}
//now the solution is simply multiplying all partial results for the same position
//since i have the results for products BEFORE and AFTER me, I just need to combine the two sides
for(int i = 0; i < numbers.length; i++){
out[i] = leftRight[i] * rightLeft[i];
}
return out;
}
/**
* Given K sorted arrays, return one array with the sorted content from all.
* @param arrays the input arrays.
* @return one array containing all sorted elements from the input arrays.
* @throws IllegalArgumentException if any array is null or empty.
*/
public static int[] mergeKSortedArrays(int[]... arrays){
if(arrays == null || arrays.length == 0){
return new int[]{};
}
//elements are (value, (input array index, position in array))
//we need to pass comparator to order on values of the pair only
PriorityQueue<Pair<Integer, Pair<Integer, Integer>>> minHeap = new PriorityQueue<>(arrays.length, Comparator.comparing(Pair::getFirst));
//initialize heap, add from each array the first element
int totalLength = 0;
for(int i = 0; i < arrays.length; i++){
if(arrays[i] == null || arrays[i].length == 0){
throw new IllegalArgumentException("Input arrays cannot be empty");
}
minHeap.add(new Pair<>(arrays[i][0], new Pair<>(i, 0)));
totalLength += arrays[i].length;
}
int[] out = new int[totalLength];
int totItems = 0;
//we keep polling elements until we're done
while(!minHeap.isEmpty()){
//set this element in the result array
Pair<Integer, Pair<Integer, Integer>> p = minHeap.poll();
out[totItems] = p.getFirst();
totItems++;
//if the source array for this value has more elements, add the next to the heap
Pair<Integer, Integer> arrayLocation = p.getSecond();
arrayLocation.setSecond(arrayLocation.getSecond() + 1);
if(arrayLocation.getSecond() < arrays[arrayLocation.getFirst()].length) {
minHeap.add(new Pair<>(arrays[arrayLocation.getFirst()][arrayLocation.getSecond()], arrayLocation));
}
}
return out;
}
/**
* Given a set of numbers and a target find all unique triple of numbers that summed together reach the target.
* @param target the target to reach.
* @param numbers the available numbers.
* @return a list of triples that summed together reach the given target.
*/
public static List<int[]> threeSums(int target, int... numbers){
List<int[]> out = new ArrayList<>();
if(numbers == null || numbers.length < 3){
return out;
}
//very important, if we don't sort this logic does not work
Arrays.sort(numbers);
/*
Given a sorted array of integers, we can scour the whole array for each integer in it to find a pair that
sums up to the remainder of target - i, which therefore satisfies our a+b+c = K condition.
If the array is ordered, we can walk from both ends towards each other looking for our pairs,
checking at each time how off mark we are.
If the current pair sums up to a bigger value than we're looking for, keeping same lower bound but reducing the upper bound
will yield a smaller sum than before. We can do the opposite if we want to increase the value instead.
We can also skip duplicate values.
We therefore use 3 pointers: current element, start and end of range for our pair search.
*/
for(int i = 0; i <= numbers.length - 3; i++){
//if this element is equal to the previous one, skip it as it can only return duplicate results
//or no result at all if all duplicates were necessary to match our target
if(i > 0 && numbers[i] == numbers[i - 1]){
continue;
}
int start = i + 1, end = numbers.length - 1;
//keep shrinking the search window for the pair completing the triple including element i, if any
while(start < end){
//if we have found a triple, add it to result set but CONTINUE the search
//we might have other pairs that complement our triple for this element i
if(numbers[i] + numbers[start] + numbers[end] == target){
out.add(new int[]{numbers[i], numbers[start], numbers[end]});
}
//if the current pair sums up to a value that make us short of the target
//try to increase that value by moving up from the start of the search range
//skipping duplicates, we must encounter a higher value since the array is sorted
if(numbers[i] + numbers[start] + numbers[end] < target){
int currStart = start;
while(numbers[start] == numbers[currStart] && start < end) {
start++;
}
}
//otherwise apply same logic but move down from the end of the current search range
else {
int currEnd = end;
while(numbers[end] == numbers[currEnd] && start < end) {
end--;
}
}
}
}
return out;
}
/**
* Given an array of integers, find a subarray whose sum is 0.
* @param numbers the given integers.
* @return the subarray whose sum is 0, if it exists, empty array otherwise.
*/
public static int[] findSubarrayOfSumZero(int... numbers){
if(numbers == null || numbers.length < 2){
return new int[]{};
}
/*
We start summing up all array elements and store the results in a set.
Whenever we find a result we've already seen, we stop since we must have gone through some subarray whose
contribution to our sum was 0.
We however skip zeros, since a single zero is NOT a valid subarray as a sum requires at least 2 operands.
*/
int currSum = numbers[0];
//two immediate consecutive zeros are already our result
if(numbers[0] == numbers[1] && numbers[0] == 0){
return new int[]{0, 0};
}
Set<Integer> seen = new HashSet<>();
//if the first value is a zero, skip it
if(currSum != 0) {
seen.add(currSum);
}
//sum all elements from the next until we either reach the end or find a sum we've already calculated
int i = 1;
for(; i < numbers.length; i++){
//if by any chance we have two consecutive zeros, that's a valid answer and we return it
//otherwise we won't handle this case correctly
if(i + 1 < numbers.length && numbers[i] == 0 && numbers[i + 1] == 0){
return new int[]{0, 0};
}
currSum += numbers[i];
//if the current sum is zero it means the current element is complement of everything before it, we have a valid subarray
if(currSum == 0){
break;
}
//if the current value is a zero, do not perform the check since we did not actually find a subarray
//whose sum was 0, only the single zero element
if(numbers[i] != 0){
//if we have already seen this value for the sum AND the current number was not a zero, we have found a zero-sum subarray
if(seen.contains(currSum)){
break;
}
seen.add(currSum);
}
}
//if we have reached the end of the array AND the sum is zero, the whole array is our result (and arguably also a valid subarray)
if(currSum == 0 && i == numbers.length){
return numbers;
}
//if a subarray or the whole array would sum up to 0 our index would be within the array bounds
//if it's out of bounds, definitely we haven't found a solution
//also handles the case where input is [1,0] where if we only looked at seen.contains(currSum) we would get the wrong answer
if(i == numbers.length){
return new int[]{};
}
//now walk back and find where does the subarray that sums to 0 start
int end = i, target = currSum;
for(; i >= 0; i--){
currSum -= numbers[i];
//we ignore zeros so we want to reach our target with a non zero value
if(numbers[i] != 0 && currSum == target){
break;
}
}
//now we have the start and end indices for our subarray, just need to fill the result
int[] out = new int[end - i + 1];
//if we fill from the back we have easier math for the indices
for(; end >= i; end--){
out[end - i] = numbers[end];
}
return out;
}
/**
* Given an array of integers, move all zeros and negative values at the end.
* @param numbers the given array to partition.
* @return the index where the positive partition ends, -1 if there is no positive partition.
*/
public static int partitionArrayZerosAndNegativesLast(int... numbers){
if(numbers == null || numbers.length == 0){
return -1;
}
int low = 0, high = numbers.length - 1;
while(low < high){
//stop at numbers.length - 1 so the last (if any) low++ will leave low = numbers.length - 1 which is end of array
//otherwise we go out of bounds, eg if array is all positives
while(numbers[low] > 0 && low < numbers.length - 1){
low++;
}
//same but going down
while(numbers[high] <= 0 && high > 0){
high--;
}
if(low >= high){
break;
}
swap(numbers, low, high);
low++;
high--;
}
//if array was all negatives, high is on position 0 but there is no positive partition
if(numbers[high] > 0){
return high;
}
return high - 1;
}
/**
* Given an array of integers allowing duplicates, zeros and negative values, find the first missing positive integer.
* If no positive integer is missing, return N+1;
* @param numbers the given numbers.
* @return the first missing positive integer in the array or N+1 if all elements are present.
*/
public static int findFirstMissingPositiveIntegerWithZerosAndDuplicates(int... numbers){
if(numbers == null || numbers.length == 0){
return 1;
}
//move all zeros and negative values at end of array
int end = partitionArrayZerosAndNegativesLast(numbers);
//if start of positive partition is -1, there were no positives, so first missing is 1
if(end < 0){
return 1;
}
//otherwise the first element AFTER the last positive is at the next index
end++;
/*
Assuming we had a perfect array of all positives, no duplicates and no missing values, each array value converted to 0-based (value - 1)
would point to a cell in the array. If we somehow mark the content of the cell each value points to (eg turn negative),
at the end we would have an array of only negative values.
If there would be duplicates, we would have multiple references to the same cell, therefore we cannot simply toggle a value,
but need to preserve negatives. We want to know if at least one value in the array was pointing to this cell.
Additionally, since we are converting values to negatives for marking, when we extract them to use as cell reference
we must remember to take the absolute value!
The available cells in an array are 0..N-1 which means all consecutive positive numbers 1..N
If there is a duplicate, it means some other number can't fit in this range as its place would be taken by the duplicate,
therefore we have a missing value.
Therefore if a number is missing, the cell it would point to won't be turned negative by our previous logic.
When we scan the array again then, if we encounter a positive value, we know that the number pointing to this cell
0-based (therefore the number is equal to cell index + 1) was missing.
If there are no duplicates but simply missing numbers, we would have the same situation.
If there are more duplicates or missing numbers or a combination of the two, when walking the array we would find
the very first missing item every time, even though our array might have multiple positive values left after our processing.
If the array contained all positive values from 1 to N, then the first missing would be AFTER the end of the array, N+1.
*/
for(int i = 0; i < end; i++){
//we track visited values as negative, but must treat them as positive array index
int absVal = Math.abs(numbers[i]);
//we might have elements that are bigger than the array length, they are not valid indices
//but also we are looking for the FIRST missing integer, therefore there must be some other element before this
//which is the answer
if(absVal > end){
continue;
}
//mark the element at this position in the array (0-based) as negative. If it was already negative, leave untouched
if(numbers[absVal - 1] > 0){
numbers[absVal - 1] = -numbers[absVal - 1];
}
}
//walk again on positive portion of the array looking for the first positive value
//its cell was NOT pointed by anyone, therefore the number that would point to it is the very first missing
//convert cell index (0-based) to positive number -> index + 1
for(int i = 0; i < end; i++) {
if(numbers[i] > 0){
return i + 1;
}
}
//if all numbers were in the array, the first missing is the very next that would not fit in the array anyway
return end + 1;
}
//helper for maxSumNonAdjacentElements
private static int getZeroIfNegative(int number){
return (number < 0) ? 0 : number;
}
/**
* Given an array of integers allowing zeros, negatives and duplicates, find the maximum
* sum that can be be created choosing only non adjacent elements.
* @param numbers the input numbers.
* @return the maximum sum that can be be created choosing only non adjacent elements.
*/
public static Integer maxSumNonAdjacentElements(int... numbers){
//since we must choose at least 2 numbers and they can't be adjacent, we need a minimum of 3 numbers in input
if(numbers == null || numbers.length < 3){
return null;
}
/*
We alternate between picking the current number or not, each time we make the choice that maximises our sum
Therefore we wonder whether it's better to add the current number to the preceding sum of non adjacent numbers
(which is the sum that EXCLUDED the previous element)
or skip this number and keep the maximum sum found so far (which is the sum that INCLUDED the previous element)
If we encounter a zero or negative value, we count it as 0
We view the problem as a moving window where for each element we look at the triple:
sumExcludingPreviousElement, sumIncludingPreviousElement, currentElement
if sumExcludingPreviousElement + currentElement > sumIncludingPreviousElement then
that's our best choice, and the result becomes the sumIncludingPreviousElement for the NEXT item
*/
//base case is special, as we would pick from index -1 (the preceding starting sum non adjacent to item in position 1)
//therefore we get the first item
int sumExcludingPreviousElement = getZeroIfNegative(numbers[0]);
//and the choice that maximises our initial operand pick between first and second item
int sumIncludingPreviousElement = Math.max(sumExcludingPreviousElement, getZeroIfNegative(numbers[1]));
//then for all subsequent items we instead apply our logic
for(int i = 2; i < numbers.length; i++){
//should I pick (i - 2 + i) or (i - 1), that is add this element to the sum that excluded the previous or not
int sumIncludingThis = Math.max(sumIncludingPreviousElement, sumExcludingPreviousElement + getZeroIfNegative(numbers[i]));
//then we slide our view and for the NEXT item the values are inverted
sumExcludingPreviousElement = sumIncludingPreviousElement;
sumIncludingPreviousElement = sumIncludingThis;
}
//at the end we pick the highest
return Math.max(sumExcludingPreviousElement, sumIncludingPreviousElement);
}
/**
* Given an array of unique integers in the range 1..N representing the moments when a light in position
* input[i] is turned on, return the number of moments when ALL lights are on (Y) in an interrupted sequence.
* We call these sequences "blue lights"
* Initially all lights are off (I)
*
* Example:
*
* 1 2 3 4
* I I I I
*
* at moment 1 we turn on light 2:
* 1 2 3 4
* I Y I I
*
* light 2 is on, but not all lights up to the current moment (1) are on in an interrupted sequence
* as we would need also light 1 to be on
*
* at moment 2 we turn on light 1:
* 1 2 3 4
* Y Y I I
*
* lights 1 and 2 are on, at this moment all lights up to the current moment (2) are on in an interrupted sequence
* as there are no OFF lights in between
*
* at moment 3 we turn on light 4:
* 1 2 3 4
* Y Y I Y
*
* lights 1,2,4 are on but light 3 is off. At this moment (3) not all lights are on in an interrupted sequence
* since light 3 (end of current sequence) is off
*
* at moment 4 we turn on light 3:
* 1 2 3 4
* Y Y Y Y
*
* at this moment (4) all lights are on in an interrupted sequence
*
* @param lights the order in which the lights are turned on.
* @return the number of moments when all lights up to the current one are lit on.
*/
public static int momentsWhenBulbsAreBlue(int... lights){
//if we have no lights, there are no moments these are on
if(lights == null || lights.length == 0){
return 0;
}
/*
We need an uninterrupted sequence of lights to be on at a precise moment in order to consider it a blue sequence
Since we have array of length 1..N with values 1..N and no duplicates, there is only one moment where a certain
light will be turned on.
If at moment X all lights 1..X are on, it means we flipped all switches from 1 to X, which means we have seen
all values in range 1..X in our array so far.
The sum of all numbers in this range is X(X+1)/2
Therefore if at a certain moment X the sum of all values seen in the array is exactly equal to the sum of all
numbers in range 1..X, we have flipped all switches up to X and therefore have an uninterrupted sequence of lights
We count how many times does this happen while walking over the whole array and have our result.
Simple examples:
4 lights, array is 1,2,3,4
at moment 1 we turn on light 1, all lights up to 1 are on -> blue sequence
at moment 2 we turn on light 2, all lights up to 2 are on -> blue sequence
at moment 3 we turn on light 3, all lights up to 3 are on -> blue sequence
at moment 4 we turn on light 4, all lights up to 4 are on -> blue sequence
we have 4 moments where a blue sequence is found
4 lights, array is 4,3,2,1
at moment 1 we turn on light 4, not all lights up to 1 are on -> no blue sequence
at moment 2 we turn on light 4, not all lights up to 2 are on -> no blue sequence
at moment 3 we turn on light 2, not all lights up to 3 are on -> no blue sequence
at moment 4 we turn on light 1, all lights up to 4 are on -> blue sequence
we have only one moment where a blue sequence is found
*/
int sum = 0;
int totBlues = 0;
//start from 1 and adjust for index otherwise formula N(N+1)/2 fails on index 0
//we could also have adjusted the formula instead
//i is our current moment, we want all lights in range 1..i to be on at the same time
//this only happens if we have flipped all switches in range 1..i so far
//we means we have seen all values 1..i in our array up to this instant
for(int i = 1; i <= lights.length; i++){
sum += lights[i - 1];
if(sum == ((i * (i + 1)) / 2)){
totBlues++;
}
}
return totBlues;
}
/**
* Given an unsorted array of numbers, with duplicates allowed, find the length of the longest sequence of consecutive numbers.
* @param numbers the input numbers.
* @return the length of the longest sequence of consecutive numbers.
*/
public static int longestSequenceOfConsecutiveNumbers(int... numbers){
if(numbers == null || numbers.length == 0){
return 0;
}
/*
We add all numbers to a set, this will remove duplicates and let us test if a given number was in the input in constant time.
Now we view each number as the leftmost point in a sequence. If we have a number before that,
we are part of the longer sequence starting at that number.
If we do not have a number before it, that is the starting point a sequence, we search for all consecutive numbers
starting from the current one in our set and keep going until we find a match.
We will either cover all numbers (the input contains an uninterrupted sequence from first to last element)
or stop when we can't find a successor.
When we move on to the next element in the input, we will NOT repeat this process IF its predecessor is in the set
We therefore scan the array multiple times, but each time we only walk a portion of it.
The length of all these portions together is N, meaning we do at most 2N work and not N^2
Example:
123
Our set contains 1,2,3
start at element 1, 0 (1-1) is not in the set, we start our scan and find that all elements up to 3 are in the set
The longest sequence starting at 1 is 3.
move to element 2, 1 (2-1) is in the set, we skip it
move to element 3, 2 (3-1) is in the set, we skip it
We reached end of array
321
Our set contains 1,2,3
start at element 3, 2 (3-1) is in the set, we skip it
move to element 2, 1 (2-1) is in the set, we skip it
move to element 1, 0 (1-1) is not in the set, we start our scan and find that all elements up to 3 are in the set
The longest sequence starting at 1 is 3.
We reached end of array
134
Our set contains 1,3,4
start at element 1, 0 (1-1) is not in the set, we start our scan and find that the next 2 (1+1) is missing. We stop
searching, the current longest subsequence is 1.
move to element 3, 2 (3-1) is in the set, we start our scan and find that all elements up to 4 are in the set
The longest sequence starting at 3 is 2.
move to element 4, 3 (4-1) is in the set, we skip it
We reached end of array
*/
Set<Integer> uniqueNumbers = new HashSet<>();
for(int i = 0; i < numbers.length; i++){
uniqueNumbers.add(numbers[i]);
}
int longest = 1, curr = 0;
for(int i = 0; i < numbers.length; i++){
if(!uniqueNumbers.contains(numbers[i] - 1)) {
curr = 1;
int next = numbers[i] + 1;
while(uniqueNumbers.contains(next)){
curr++;
next++;
}
if(curr > longest){
longest = curr;
}
}
}
return longest;
}
/**
* Given an array where each value is in the range 1..N, find all duplicates.
* @param numbers the input numbers.
* @return the duplicate numbers.
*/
public static Set<Integer> findAllDuplicates(int... numbers){
Set<Integer> duplicates = new HashSet<>();
if(numbers == null || numbers.length < 2){
return duplicates;
}
/*
Use each value as a pointer to another cell in the array.
While walking down the array, flip each value to negative.
If while walking we see we point to a negative, we are a duplicate.
*/
for(int i = 0; i < numbers.length; i++){
if(numbers[Math.abs(numbers[i]) - 1] < 0){
duplicates.add(Math.abs(numbers[i]));
}
else{
numbers[numbers[i] - 1] = -numbers[numbers[i] - 1];
}
}
return duplicates;
}
/**
* Given an array of integers and a window size K, find the maximum element for all subarrays in each window.
* @param k the window size.
* @param numbers the input numbers.
* @return a list of the maximum elements for each window of size k.
*/
public static List<Integer> findMaximumForEachSubarrayOfLengthK(int k, int... numbers){
List<Integer> maximums = new ArrayList<>();
if(numbers == null || numbers.length == 0 || k > numbers.length){
return maximums;
}
/**
* We use a doubled ended queue (doubly linked list) and enqueue the first K elements.
* We keep a pointer to the beginning of the window and start our walk from position K.
* Each new element that comes can be:
*
* (1) biggest of all other elements in the queue, this means that until the window cuts him out
* he will be the maximum
*
* (2) bigger than some other elements in the queue, this means that as soon as the window cuts out the current maximum
* he MIGHT be the new maximum, for sure not the other currently smaller elements
*
* (3) smallest of all other elements in the queue, this means that as soon as the window cuts out the current maximum
* he MIGHT be the new maximum, unless bigger elements come after us that would end up in the same window as us
*
* Simply put, at each step we:
* (1) verify if the window has cut out the current maximum and if so, remove it from front of the list
* (2) add the new element at the back of the list. While adding, we try to push ourselves as far towards the
* front as possible, removing all elements smaller than us while doing so. This is because within this window size,
* they will never be the maximum and since we came after them, even when the window moves forward they have
* no chance of being the maximum.
*
* Example:
*
* 4321 window size 2
*
* queue is 4,3 maximum is 4
* window slides and cuts off 4 and includes 2 - queue is 3,2 maximum is 3
* window slides and cuts off 3 and includes 1 - queue is 2,1 and maximum is 2
*
* 1234 window size 2
*
* queue is 1,2 maximum is 2
* window slides and cuts off 1 and includes 3 - queue is 2,3 but in this window and until the window cuts 3 off
* (unless a bigger element appears) 3 will be the maximum, we push it to the front and queue is 3 only
* window slides and cuts off 2 and includes 4 - queue is 3,4 as before we push 4 to the front, queue is 4 only
*/
//Queue interface does NOT offer first and last methods, use a LinkedList exactly
LinkedList<Integer> q = new LinkedList<>();
//find the maximum of the first K values and add all to the queue from the back
int max = numbers[0];
for(int i = 0; i < k; i++){
q.add(numbers[i]);
if(numbers[i] > max){
max = numbers[i];
}
}
maximums.add(max);
//keep pointer to start of window and start with end at K exactly
//if queue head is equal to element at start range, cut it out and increase start range
//push element at end of window as far up as possible in the queue
//the head of the queue is maximum for current window, add to result
int i = 0;
for(int j = k; j < numbers.length; j++){
//check if we need to cut head off when we move window start up
if(numbers[i] == q.getFirst()){
q.removeFirst();
}
i++;
//push element at end of window as far up as possible
while(!q.isEmpty() && q.getLast() < numbers[j]){
q.removeLast();
}
q.addLast(numbers[j]);
//head of queue always has maximum for this window
maximums.add(q.getFirst());
}
return maximums;
}
/**
* Given an array of positive integers and a positive integer, find the length of the shortest consecutive
* subarray whose sum is >= target.
* @param k the target.
* @param numbers the input numbers.
* @return the length of the shortest consecutive subarray whose sum is >= target or 0 if none exists.
*/
public static int minSubarrayOfSumK(int k, int... numbers){
if(numbers == null || numbers.length == 0 || k <= 0){
return 0;
}
/*
Use a sliding window technique, we keep summing until we reach the threshold.
Then we check if we can reduce the current subarray size by reducing the window start range
while remaining above the threshold. We keep doing that as long as it's possible (either we reached current element in array
or sum of subarray would fall below threshold)
*/
int min = Integer.MAX_VALUE;
int curr = 0;
int i = 0;
for(int j = 0; j < numbers.length; j++){
curr += numbers[j];
while(curr >= k && i <= j){
if(j - i + 1 < min){
min = j - i + 1;
}
if(curr - numbers[i] >= k){
curr -= numbers[i];
i++;
}
else{
break;
}
}
}
if(min == Integer.MAX_VALUE){
return 0;
}
return min;
}
//helper for howManySubsequencesSumUpToTargetDP
private static int howManySubsequencesSumUpToTargetDP(int target, int[] numbers, int curr, Map<Pair<Integer, Integer>, Integer> cache){
//if we already have seen this target for this element, return that partial value
Pair<Integer, Integer> key = new Pair(curr, target);
Integer cached = cache.get(key);
if(cached != null ){
return cached;
}
//only the empty subsequence sums up to 0
if(target == 0){
return 1;
}
//no subsequence can be generated from these values
if(target < 0 || curr >= numbers.length){
return 0;
}
//calculate current value
int out;
//if we can't subtract the current element from the current sum, the only choice is to not pick the current element
if(target < numbers[curr]){
out = howManySubsequencesSumUpToTargetDP(target, numbers, curr + 1, cache);
}
//otherwise we execute both choices
else{
out = howManySubsequencesSumUpToTargetDP(target - numbers[curr], numbers, curr + 1, cache) +
howManySubsequencesSumUpToTargetDP(target, numbers, curr + 1, cache);
}
//track new value in cache
cache.put(key, out);
return out;
}
/**
* Given an array of numbers and a target, find how many subsequences in the array sum up to the target.
* Numbers are all positive and no duplicates are present, array is not sorted.
* Use dynamic programming.
* @param target the target to reach.
* @param numbers the available numbers.
* @return the number of subsequences that sum up to given target, -1 if none exist.
*/
public static int howManySubsequencesSumUpToTargetDP(int target, int... numbers){
if(target <= 0 || numbers == null || numbers.length == 0){
return -1;
}
Map<Pair<Integer, Integer>, Integer> cache = new HashMap<>();
/*
Starting from target, make the choices to subtract the current element or not and continue until we reach 0 (found a valid sum)
or terminate available elements. The cache contains the information: from a given set of numbers that summed up to X
*/
int out = howManySubsequencesSumUpToTargetDP(target, numbers, 0, cache);
if(out == 0){
return -1;
}
return out;
}
/**
* Given two sorted arrays, find all elements that appear in both arrays, return a sorted array of those as result.
* @param a the first array.
* @param b the second array.
* @return a sorted array of all duplicate elements between the two arrays.
*/
public static int[] findDuplicatesInArraysBinarySearch(Integer[] a, Integer[] b){
if(a == null || b == null || a.length == 0 || b.length == 0){
return new int[]{};
}
/*
Since either array could be much bigger than the other, if we use a two pointer approach
we have a O(M+N) runtime, if instead we use binary search on the bigger array for each element in the
smaller, we get a O(M logN) runtime.
We have a generic binarySearch method so we use Integer[] instead of int[] for input.
*/
Integer[] small, large;
if(a.length < b.length){
small = a;
large = b;
}
else{
small = b;
large = a;
}
//collect results in a queue as we don't know the size of output yet
//we track them in order of insertion so later we can create the desired output array
Queue<Integer> out = new LinkedList<>();
for(int elem : small){
if(binarySearchIterative(large, elem) != -1){
out.add(elem);
}
}
int[] res = new int[out.size()];
int pos = 0;
for(int i : out){
res[pos] = i;
pos++;
}
return res;
}
/**
* Given an array representing dominoes, determine the final position of each piece. Array contains following chars:
* . = domino is standing still
* R = domino is falling right
* L = domino is falling left
* @param dominoes the dominoes initial state.
* @return the dominoes final state.
*/
public static char[] fallingDominoes(char[] dominoes){
if(dominoes == null || dominoes.length == 0){
return dominoes;
}
/*
Scan the array twice, first time left to right to determine which pieces would be falling right.
We toggle a boolean to track whether pieces are falling right or not.
A piece falls right if it is after another piece that also is falling right AND the NEXT piece is NOT
falling left.
Second time we scan both input and partial result array right to left at the same time and repeat the process
however we now have information about right falling pieces therefore we can determine the final domino position.
We use the same toggle to indicate whether pieces are falling left or not this time. We have the following cases:
- piece in input was standing still:
if we are now falling left, look at the partial result, if that piece is marked as falling right we are pushing
on it from both directions, leave it as it was in the input, otherwise mark it as falling left
- piece in input was falling right:
toggle the falling switch to false
- piece was falling left:
toggle the falling switch to true
We need to be careful in handling boundary cases for example when a piece that was standing still at the borders
of the array is being pushed to fall, in that case no other force can balance it and we need to correctly update
it to fall
*/
char[] out = new char[dominoes.length];
//left right, track partial results of pieces falling right
boolean isFalling = false;
for(int i = 0; i < dominoes.length; i++){
switch (dominoes[i]){
//this piece is standing, if we were falling right, look at the next piece
case '.':
//if next piece does not exist or is not falling left, update this piece to fall right
if(isFalling && ((i + 1 < dominoes.length && dominoes[i + 1] != 'L') || (i + 1 == dominoes.length))){
out[i] = 'R';
}
//otherwise keep it as it was
else {
out[i] = dominoes[i];
}
break;
//this piece is falling right, toggle the switch to falling and copy the value
case 'R':
isFalling = true;
out[i] = dominoes[i];
break;
//this piece is falling left, toggle the switch to not falling and copy the value
case 'L':
isFalling = false;
out[i] = dominoes[i];
break;
default:
throw new IllegalArgumentException("Invalid char: " + dominoes[i]);
}
}
//right left, track final results considering pieces falling left
isFalling = false;
for(int i = dominoes.length - 1; i >= 0; i--){
switch (dominoes[i]){
//this piece was standing in the input
case '.':
//if we are falling left and the previous piece does not exist or is not falling right
if(isFalling && ((i - 1 >= 0 && dominoes[i - 1] != 'R') || (i - 1 < 0))){
//we check in the partial result if there was a right falling piece that would balance us out
//if so, we leave the piece as it was in the input (either R or .)
if(out[i] == 'R'){
out[i] = dominoes[i];
}
//otherwise update this piece to be falling left
else{
out[i] = 'L';
}
}
break;
//this piece is falling right, toggle the switch to not falling
case 'R':
isFalling = false;
break;
//this piece is falling left, toggle the switch to falling
case 'L':
isFalling = true;
break;
default:
throw new IllegalArgumentException("Invalid char: " + dominoes[i]);
}
}
return out;
}
/**
* Given an array representing height of people standing behind each other from left to right, find how many
* people can see in front of them.
* @param people the heights of all the people lined up.
* @return the number of people with an unobstructed view.
*/
public static int witnessOfTheTallPeople(int[] people){
if(people == null || people.length == 0){
return 0;
}
int tot = 0;
int maxHeight = 0;
for(int i = people.length - 1; i >= 0; i--){
if(people[i] <= 0){
throw new IllegalArgumentException("Person cannot have 0 or negative height");
}
if(people[i] > maxHeight){
tot++;
maxHeight = people[i];
}
}
return tot;
}
/**
* Given an integer array, move all zeros to the end and preserve ordering of other elements.
* @param numbers the input array.
*/
public static void moveZeros(int... numbers){
if(numbers == null){
return;
}
/*
We are supposed to alter the given array, therefore let's make use of it.
We want to scan the array to find all non zero elements and put them to the front of the array,
then fill the rest of the positions with zeros.
We notice though that any non zero element would be placed at the beginning of the array, then the next non zero
would be placed after that and so on.
If an element is non zero and there were no zeros before it, it stays in its place.
Therefore we can use a pointer to track where would be insert a non zero element, then scan all other elements
of the array.
When we encounter a non zero element, we copy it to the location pointed by our pointer UNLESS it's exactly our position.
If we did move, we replace the current position with a zero.
In both cases, we increase the pointer.
*/
int pos = 0;
for(int i = 0; i < numbers.length; i++){
if(numbers[i] != 0){
if(pos != i){
numbers[pos] = numbers[i];
numbers[i] = 0;
}
pos++;
}
}
}
/**
* Given an array of numbers, find the largest product that can be made using three elements.
* @param numbers the input numbers.
* @return the largest product that can be made using three elements.
*/
public static int maxThreeProduct(int... numbers) {
if (numbers == null || numbers.length < 3) {
return 0;
}
/*
If we sort the array, we have the smallest elements on the left and biggest on the right.
Since we must pick three elements, the max product is either:
- biggest * secondBiggest * thirdBiggest (if all are positive)
- smallest * secondSmallest * biggest (if first two are negative and last is positive)
This also applies if all elements are negative (case 2 would return the biggest negative number achievable
using 3 negative numbers, which would therefore be the largest product)
But then sorting the array is not necessary as we can track these five elements in a single pass
over the unsorted array.
*/
int smallest = Integer.MAX_VALUE, secondSmallest = Integer.MAX_VALUE,
largest = Integer.MIN_VALUE, secondLargest = Integer.MIN_VALUE, thirdLargest = Integer.MIN_VALUE;
for(int i = 0; i < numbers.length; i++){
//must use else ifs, otherwise we wrongly update all variables to smallest/biggest element!
if(numbers[i] < smallest){
secondSmallest = smallest;
smallest = numbers[i];
}
else if(numbers[i] < secondSmallest){
secondSmallest = numbers[i];
}
if(numbers[i] > largest){
thirdLargest = secondLargest;
secondLargest = largest;
largest = numbers[i];
}
else if(numbers[i] > secondLargest){
thirdLargest = secondLargest;
secondLargest = numbers[i];
}
else if(numbers[i] > thirdLargest){
thirdLargest = numbers[i];
}
}
return Math.max(largest * secondLargest * thirdLargest, largest * smallest * secondSmallest);
}
/**
* Given an array of unique integers and a target K, find all pairs that satisfy the equation X - Y = K.
* Elements must be added to the output respecting the order in which they are found considering as if they
* were Y in the given equation.
* Example: 0,-1,1 target 1
* Result: (1,0), (0,-1)
* @param k the target.
* @param numbers the numbers.
* @return a list of pairs that satisfy the equation X - Y = K with ordered preserved on Y.
*/
public static List<Pair<Integer, Integer>> pairsWithDifferenceK(int k, int... numbers){
List<Pair<Integer,Integer>> out = new ArrayList<>();
if(numbers == null || numbers.length < 2 || k < 0){
return out;
}
/*
We add all elements to a set, then walk the array again and calculate the complement we would need
to satisfy the X - Y = K equation, that is, given an element Y, we look for X = K + Y
If that element is in our set, we have a pair and we add it to the output. Doing so, we preserve the
order on Y since this is the first Y element we encountered that does form a valid pair with some other
array element.
*/
Set<Integer> nums = new HashSet<>();
for(int i = 0; i < numbers.length; i++){
nums.add(numbers[i]);
}
for(int i = 0; i < numbers.length; i++){
int complement = k + numbers[i];
if(nums.contains(complement)){
Pair<Integer, Integer> pair = new Pair<>(complement, numbers[i]);
out.add(pair);
}
}
return out;
}
/**
* Given an array of numbers with duplicates, zeros and negatives, find if any subarray sums up to a given target.
* @param k the target.
* @param numbers the numbers.
* @return true if a subarray exists that sums up to the target.
*/
public static boolean subarrayOfSumK(int k, int... numbers){
if(numbers == null || numbers.length == 0){
return false;
}
/*
We need at least two elements to form a sum, we scan the array and track in a map the pair
rollingSumAtIndex, firstIndexForThisSum
At each index, we check whether we have already encountered rollingSum - k as that would mean that
we have two subarrays whose difference is exactly K, therefore there is somewhere a subarray whose sum
is K.
By tracking the index of the first found occurrence of a sum, we can check if the subarray we are considering
contains at least two elements.
*/
Map<Integer, Integer> sums = new HashMap<>();
//we need at least two elements, so init position for 0 sum to -1
//to prevent wrong answer in case first element is K
sums.put(0, -1);
int rollingSum = 0;
for(int i = 0; i < numbers.length; i++){
rollingSum += numbers[i];
//if current sum - k = some previous sum,we might have an answer
if(sums.containsKey(rollingSum - k)){
//as long as the subarray we consider is at least two elements
//i is us (sum so far) - index where the other sum was found
if(i - sums.get(rollingSum - k) > 1) {
return true;
}
}
//otherwise track the sum (only the sum) so far
if(!sums.containsKey(rollingSum)){
sums.put(rollingSum, i);
}
}
return false;
}
/**
* Given an array of integers, find 4 elements that sum up to a given target. Each quadruple must be unique.
* @param nums the numbers.
* @param target the target to reach.
* @return the unique quadruples that sum up to the given target.
*/
public static List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> out = new LinkedList<>();
if(nums == null || nums.length < 4){
return out;
}
//key = sum, value = all pairs with this sum, including their indices
Map<Integer, List<Quadruple>> twoSums = new HashMap<>();
//for each pair of elements, sum them up and track them in a map
for(int i = 0; i < nums.length - 1; i++){
for(int j = i + 1; j < nums.length; j++){
int sum = nums[i] + nums[j];
List<Quadruple> sums = twoSums.getOrDefault(sum, new ArrayList<>());
//first element, second element, first index, second index
Quadruple q = new Quadruple(nums[i], nums[j], i, j);
sums.add(q);
twoSums.put(sum, sums);
}
}
//VERY important Quadruple must have a correct hashCode and equals!!
Set<Quadruple> seen = new HashSet<>();
//for each pair again, search for k - sum of this pair in the map
//a + b + c + d = k -> a+b = k - (c+d)
for(int i = 0; i < nums.length - 1; i++){
for(int j = i + 1; j < nums.length; j++){
int sum = target - (nums[i] + nums[j]);
//check if we had any pair that matches k - (c+d), which is our complement for sum K
List<Quadruple> sums = twoSums.getOrDefault(sum, new ArrayList<>());
//the current pair
Quadruple curr = new Quadruple(nums[i], nums[j], i, j);
//for each matching pair, scan list of potential values to see if we can form a UNIQUE quadruple
for(Quadruple q : sums){
//to be unique, no index can be reused between the two pairs
if(curr.c != q.d && curr.d != q.c && curr.c != q.c && curr.d != q.d){
Quadruple res = new Quadruple(curr.a, curr.b, q.a, q.b);
//only track unique quadruples
if(!seen.contains(res)){
seen.add(res);
List<Integer> quad = new ArrayList<>();
quad.add(curr.a);
quad.add(curr.b);
quad.add(q.a);
quad.add(q.b);
out.add(quad);
}
}
}
}
}
return out;
}
//helper for countOutOfOrderElements
//finds in a list of elements where would we insert an element in the natural order
private static int findIdxToInsertAt(List<Integer> sorted, int elem){
int floor = 0;
int ceil = sorted.size() - 1;
while(floor <= ceil){
int mid = floor + (ceil - floor) / 2;
//elements are unique so no case for elem = sorted.get(mid)
if(elem > sorted.get(mid)){
floor = mid + 1;
}
else{
ceil = mid - 1;
}
}
//only special case if the element is the smallest of all, we keep floor same and always decrease ceil
//until it goes out of bounds, in that case, we need to return 0 as the first valid position
if(ceil == -1){
return 0;
}
//otherwise this is always where we stopped and is also the position where our element goes
return floor;
}
/**
* Given an array of unique integers, find how many elements are out of order. For each pair of indices, elements are
* out of order if a smaller element appears after a larger element.
* @param numbers the input array.
* @return the number of out of order elements in the array.
*/
public static int countOutOfOrderElements(int... numbers){
if(numbers == null || numbers.length < 2){
return 0;
}
/*
Since each element is unique and we are checking for relative ordering, we can use an empty list and add elements
there in the correct order.
We can find the correct position where to place an element using binary search.
When we add an element, it is the first time we encounter it, therefore if there are elements in the list already
in a position AFTER ours, we are out of order with all those since they appeared before in the array but we are
smaller than them. We can count this, and add to our answer.
*/
int count = 0;
List<Integer> sorted = new ArrayList<>(numbers.length);
for(int i = 0; i < numbers.length; i++){
//binary search where this element would go in our list
int idx = findIdxToInsertAt(sorted, numbers[i]);
//all elements AFTER us already in the list are out of order with us since they appeared before us in the array
count += sorted.size() - idx;
//insert this element in position i (all other elements are moved to the right)
sorted.add(idx, numbers[i]);
}
return count;
}
//merge part of mergesort
//need to pass the res array as input since we are pass by value!
private static int[] merge(int[] res, int[] left, int[] right, IntWrapper tot){
int left_idx = 0, right_idx = 0, res_idx = 0;
if(left == null || right == null || left.length == 0 || right.length == 0) return null;
//scan both left and right until we reach either end
//CAUTION: the indexes are the easiest part to screw up!
//DOUBLE CAUTION: we must loop BOTH arrays at the same time! We don't know how are they sorted!
while(left_idx < left.length && right_idx < right.length){
//the number of elements from the beginning of the left portion up to the current (inclusive)
//is the number of out of order elements, as ALL of them are bigger than this element from the right
//portion, so we need to count all of them
if(left[left_idx] > right[right_idx]){
tot.addVal(left.length - left_idx);
}
//order the elements while keeping track of the indexes
//if left is smaller than right, copy left, increase left, increase result
if(left[left_idx] <= right[right_idx]){
res[res_idx] = left[left_idx];
left_idx++;
res_idx++;
}
//otherwise, copy right, increase right, increase result
else{
res[res_idx] = right[right_idx];
right_idx++;
res_idx++;
}
}
//we are still not finished, maybe we have some elements still in left or right, copy them as well
while(left_idx < left.length){
res[res_idx] = left[left_idx];
res_idx++;
left_idx++;
}
while(right_idx < right.length){
res[res_idx] = right[right_idx];
res_idx++;
right_idx++;
}
return res;
}
//sort part of mergesort, modified to track number of out of place elements we have
private static int[] sort(int[] list, IntWrapper tot){
int[] left, right;
if(list == null || list.length == 0) return null;
//our base case, a list of a single element is already sorted
if(list.length == 1) return list;
//split array in two halves
//fun fact: Math.floor returns a double
left = new int[list.length - (int)Math.floor(list.length / 2)];
right = new int[list.length - left.length];
//copy the elements in the two arrays left and right
for(int i = 0; i < left.length; i++){
left[i] = list[i];
}
for(int i = left.length, j = 0; i < list.length; i++, j++){
right[j] = list[i];
}
//sort both parts separately
sort(left, tot);
sort(right, tot);
//then put everything together at each step
return merge(list, left, right, tot);
}
/**
* Given an array of unique integers, find how many elements are out of order. For each pair of indices, elements are
* out of order if a smaller element appears after a larger element.
* @param numbers the input array.
* @return the number of out of order elements in the array.
*/
public static int countOutOfOrderElementsMergeSort(int... numbers) {
if (numbers == null || numbers.length < 2) {
return 0;
}
/*
Unlike in the case of countOutOfOrderElements where we have O(N^2) due to the pushing
of elements in the ArrayList, here we work on extra O(N) space and run a mergesort.
The key is counting for each merge operation we perform how many out of place elements we have, that is
how many elements in the left side should be on the right side. This is the total number of elements up to
the current index for the merge operation from the BEGINNING of the left portion!
The reasoning is the same as before: any number of sorted elements that we move between left and right, was out of order
We use an int wrapper to allow tracking the count during recursion.
If we didn't want the input to be destroyed, we could copy it and sort the copy.
*/
IntWrapper tot = new IntWrapper(0);
sort(numbers, tot);
return tot.getVal();
}
/**
* Given array of positive integers including zeros and duplicates, partition it in portions left and right
* such that every element in left is smaller than every element in right. Keep left as small as possible.
* @param numbers the input numbers.
* @return the shortest length of the left partition so that every element is smaller than every element in right.
*/
public static int disjointPartition(int... numbers){
if(numbers == null || numbers.length == 0){
return 0;
}
//track minimum so far walking from right to left
int[] min_rl = new int[numbers.length];
int min = Integer.MAX_VALUE;
for(int i = numbers.length - 1; i >= 0; i--){
if(numbers[i] < min){
min = numbers[i];
}
min_rl[i] = min;
}
//then walk left to right tracking maximum so far
int max = Integer.MIN_VALUE;
for(int i = 0; i < numbers.length; i++){
if(numbers[i] > max){
max = numbers[i];
}
//if we encounter a point where current max is smaller or equal to min from right to left of NEXT element
//it means every element BEFORE us, including us, will be smaller or equal to every element AFTER us
//since we are walking left to right, this means we have the smallest possible partition
//we MUST check against NEXT otherwise for example
//1,0,6,12
//we would break on 6 as every element left is smaller than 12, but the smallest possible partition
//instead is 1,0 since both are smaller than 6,12
if(i + 1 < numbers.length && max <= min_rl[i + 1]){
return i + 1;
}
}
//otherwise partition is the whole array (last element)
return numbers.length;
}
/**
* Given an array of integers, find the maximum product that can be made using contiguous elements.
* The product of all elements in input is guaranteed to fit in an integer.
* @param numbers the input numbers.
* @return the maximum product that can be made using contiguous elements.
*/
public static int maxProdSubarray(int... numbers){
if(numbers == null || numbers.length < 1){
return 0;
}
/*
Similar problem to max subarray sum (Kadane's algorithm), however, in that case we restarted our max search
when adding a new element to the sum so far was worse than picking the new element alone.
Example:
10,-2,20
We want to pick the -2 since 28 (10 + -2 + 20) > 20 (20)
-2,20
We want to discard the 2 since 18 (-2 + 20) < 20 (20)
This is true since sum of negatives is always negative
In this problem instead multiplication of negatives is a positive, therefore:
-2,20,-1
We want to pick both -2 and -1 since product 40 (-2 * 20 * -1) > 20 (20)
So we shouldn't discard too early a negative as in the sum problem. Specifically:
- as long as items so far are positive, we want to pick all of them
- if items so far have an even number of negatives, we want to pick all of them
- if items so far have an odd number of negatives, we might want to wait and see if there is another negative
later on, in which case we pick all, otherwise we discard the smallest negative if possible
What this means is at each moment, we don't keep a max so far only, but also a min so far
as an incoming negative number might transform the min so far to the global maximum, as in the example above.
As with Kadane's case, we don't need to keep a whole array of elements as at each step we only look at the previous
sum, which in our case is the product and specifically the pair (min, max)
An edge case which didn't affect the sum problem is 0. In that instance, the sum would be unaltered, but the product
would become zero and stay like that forever.
Therefore, when the current element is 0, we are forced to break our subarray and restart. However, 0 could be
a result in itself, for example:
-2,0 or 0,-2
0 is the largest subarray product
so if the global maximum up to 0 is < 0, then 0 is our answer. We then reinitialize both max so far and min so far to 1
*/
//initially first element is global maximum, it's also the max and min for the next element.
int res = numbers[0];
int maxSoFar = numbers[0];
int minSoFar = numbers[0];
//if first element is 0, we discard it and restart from next element as first in the subarray
if(numbers[0] == 0){
maxSoFar = 1;
minSoFar = 1;
}
for(int i = 1; i < numbers.length; i++){
//again handle case of 0, reinitialize and skip this element
if(numbers[i] == 0){
if(res < 0){
res = 0;
}
maxSoFar = 1;
minSoFar = 1;
}
else{
//if current element is not zero we have to check:
//max so far was positive and we are positive
//min so far was negative and we are negative
//either could be the new global maximum
int max = maxSoFar * numbers[i];
int min = minSoFar * numbers[i];
//options for max now are: max (positive * positive), min (negative * negative), the number itself
//same for min: max(positive * negative), min (negative * positive), the number itself
maxSoFar = Math.max(max, Math.max(min, numbers[i]));
minSoFar = Math.min(max, Math.min(min, numbers[i]));
//the global maximum is the best between current global maximum and max so far
res = Math.max(res, maxSoFar);
}
}
return res;
}
/**
* Given two arrays indicating the amount of fuel that can be put in a car at city i and the amount of fuel
* necessary to move from city i to the next, return if possible a start city that allows a round trip from a city
* to itself passing through all other cities in the order they are given.
* Therefore, given a circular array, find an index where a rolling sum starting from there never falls below zero.
* @param gasAtCity the amount of fuel that can be put in the car at city i.
* @param gasForNextCity the amount of fuel necessary to travel from city i to the next.
* @return the index of any city that can be used as start for a round trip that passes through all other cities
* in the order they are given.
*/
public static int startIndexForSumNeverBelowZero(int[] gasAtCity, int[] gasForNextCity){
if(gasAtCity == null || gasForNextCity == null || gasAtCity.length != gasForNextCity.length || gasAtCity.length == 0){
return -1;
}
if(gasAtCity.length == 1){
return 0;
}
int tot = 0;
int tank = 0;
int start = 0;
for(int i = 0; i < gasAtCity.length; i++){
tot += gasAtCity[i] - gasForNextCity[i];
tank += gasAtCity[i];
if(tank - gasForNextCity[i] < 0){
tank = 0;
start = i + 1;
}
else{
tank -= gasForNextCity[i];
}
}
if(tot < 0){
return -1;
}
return start;
}
/**
* Given an array of integers return true if it is possible to reorder its elements in pairs so that
* arr[2 * i + 1] = 2 * arr[2 * i] for all i up to length/2.
* @param numbers the input numbers.
* @return true if array can be reordered as requested.
*/
public static boolean arrayOfDoubledPairs(int... numbers){
//if length is odd, not possible, one element would be without a pair
if(numbers == null || numbers.length == 0 || numbers.length % 2 != 0){
return false;
}
/*
The problem statement is a bit confusing, so we need some examples.
From the description, we need pairs of elements where the second element in a pair must be double the first element:
arr[2 * i + 1] = 2 * arr[2 * i]
means for i = 0
arr[1] = 2 * arr[0]
or
arr[0] = arr[1] / 2
Examples:
5,1,2,4
Cannot be done since (1,2) or (2,4) would be a valid pair but we leave in any case 5 without a pair
4,2,2,1
Can be done using pairs (1,2), (2,4)
-4,1,2,-2
Can be done using pairs (-4,-2), (1,2)
-5,1,2,4
Cannot be done since -5/2 does not exist as integer and -10 is not in the array so there is no pair with -5 available
We notice that the smallest element only has one option for a pair: an element that's its double,
while the biggest element has only one option for the pair: an element that's its half,
every other element instead has 2 choices, either pair with an element that's its double or its half.
We can start by sorting the array in increasing order so that all smaller elements are first, we try and search
for each of them their double. If we can't find a double, there is no possibility to make a pair, therefore
we stop and return failure.
When we look for a double, we need to apply some logic:
- if element is negative its double is actually element / 2
- if element is positive its double is element * 2
We can count how many times does each element appear in the array to know how many times we have it available to use
in a pair, either as first or second element.
Then we scan the array another time and for each element check whether a valid double exists (only necessary for
negative numbers) and if it exists, whether it's still available.
If an element cannot be paired, then there is no solution as we were going from the smallest elements up so the only
pairing chance is with an element that's its double.
*/
//very important, if we don't sort this logic won't work
Arrays.sort(numbers);
Map<Integer, Integer> numCounts = new HashMap<>();
for(int n : numbers){
numCounts.put(n, numCounts.getOrDefault(n, 0) + 1);
}
for(int n : numbers){
//we've already used up all occurrences of this element in some previous pairs
if(numCounts.get(n) == 0){
continue;
}
//n is negative so its double would be its half, but it's impossible to halve this odd element
//since we have sorted the array we must look for its double as either we had a previous element
//for which we are the double, or we are the first element in a pair looking for our double
if(n < 0 && n % 2 != 0){
return false;
}
int complement;
if(n < 0){
complement = n / 2;
}
else{
complement = n * 2;
}
//if this complement does not exist in the array or all of its occurrences have already been used up
//we can't form a pair
if(numCounts.getOrDefault(complement, 0) == 0){
return false;
}
//mark that we used n and its complement in a pair so they are not available for future pairs
numCounts.put(n, numCounts.get(n) - 1);
numCounts.put(complement, numCounts.get(complement) - 1);
}
return true;
}
//helper for canPartitionInKSubsetsOfEqualSum
private static boolean canPartitionInKSubsetsOfEqualSum(int[] numbers, int currSets, boolean[] available, int targetSum, int currSum, int start){
//if we got here we generated k sets correctly, so we have a solution
if(currSets == 0){
return true;
}
//if the current set reached the target sum, try to generate the remaining ones with the elements we have left
if(currSum == targetSum){
return canPartitionInKSubsetsOfEqualSum(numbers, currSets - 1, available, targetSum, 0, 0);
}
//otherwise choose one element among the available ones and try to add it to the current set
for(int i = start; i < numbers.length; i++){
if(available[i]){
//if this element overshoots the target, no need to explore it
if(currSum + numbers[i] > targetSum){
continue;
}
//choose this element and mark it unavailable
available[i] = false;
//check if this choice leads to a solution
if(canPartitionInKSubsetsOfEqualSum(numbers, currSets, available, targetSum, currSum + numbers[i], i + 1)){
return true;
}
//otherwise, mark this element available again, maybe a different set later on can pick it as a solution
available[i] = true;
}
}
return false;
}
/**
* Given an array of positive integers and a target K, determine if it is possible to partition the array in K subsets
* of equal sum.
* @param numbers the input numbers.
* @param k the number of desired sets.
* @return true if a partition can be made.
*/
public static boolean canPartitionInKSubsetsOfEqualSum(int[] numbers, int k){
if(k <= 0 || numbers == null || numbers.length == 0 || k > numbers.length){
return false;
}
//find out what would each partition sum up to
int tot = 0;
for(int n : numbers){
tot += n;
}
//if partitions are impossible, nothing to do, we need to have tot / k sum for each partition
if(k > tot || tot % k != 0){
return false;
}
//available[i] = true means this element is available to be used in a subset
boolean[] available = new boolean[numbers.length];
Arrays.fill(available, true);
//need to create k sets, every element is initially available, target sum is tot/k, initial sum = 0, start from first element
return canPartitionInKSubsetsOfEqualSum(numbers, k, available, tot / k, 0, 0);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BinarySearchFirstOccurrenceJTests {
Integer[] in;
int elem, out;
@Test
public void emptyArray() {
in = null;
elem = 0;
out = -1;
assertEquals("empty array expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void singleElementArrayMatches() {
elem = 0;
in = new Integer[1];
in[0] = elem;
out = 0;
assertEquals("single element array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void singleElementArrayNotMatches() {
elem = 1;
in = new Integer[1];
in[0] = 0;
out = -1;
assertEquals("single element array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void twoElementsArrayMatchesLow() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem + 1;
out = 0;
assertEquals("two element array matches low expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void twoElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem;
out = 1;
assertEquals("two element array matches high expected 1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void twoElementsArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void threeElementsArrayMatchesMid() {
elem = 0;
in = new Integer[3];
in[0] = elem - 1;
in[1] = elem;
in[2] = elem + 1;
out = 1;
assertEquals("three element array matches mid expected 1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void threeElementsArrayMatchesLow() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem + 1;
in[2] = elem + 2;
out = 0;
assertEquals("three element array matches low expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void threeElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
out = 2;
assertEquals("three element array matches high expected 2", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void threeElementsArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
out = -1;
assertEquals("even array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenArrayMatchesLeft() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
out = 1;
assertEquals("even array matches left expected 1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenArrayMatchesRight() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem + 2;
out = 2;
assertEquals("even array matches right expected 2", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = -1;
assertEquals("odd array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddArrayMatchesLeft() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = 1;
assertEquals("odd array matches left expected 1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddArrayMatchesRight() {
elem = 0;
in = new Integer[5];
in[0] = elem - 3;
in[1] = elem - 2;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem + 1;
out = 3;
assertEquals("odd array matches right expected 3", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void twoElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem;
out = 0;
assertEquals("two element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void twoElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem + 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void threeElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem;
in[2] = elem;
out = 0;
assertEquals("three element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void threeElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
in[3] = elem + 1;
out = -1;
assertEquals("even element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem;
out = 0;
assertEquals("even element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
in[3] = elem + 1;
in[4] = elem + 1;
out = -1;
assertEquals("odd element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem;
in[4] = elem;
out = 0;
assertEquals("odd element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenElementsAllSameInHighArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem;
out = 2;
assertEquals("even element all same in high array matches expected 2", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void evenElementsAllSameInLowArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 1;
out = 0;
assertEquals("even element all same in low array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddElementsAllSameInHighArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem;
out = 3;
assertEquals("odd element all same in high array matches expected 3", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
@Test
public void oddElementsAllSameInLowArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem + 1;
in[4] = elem + 1;
out = 0;
assertEquals("odd element all same in low array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrence(in, elem));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BinarySearchFirstOccurrenceRecursiveJTests {
Integer[] in;
int elem, out;
@Test
public void emptyArray() {
in = null;
elem = 0;
out = -1;
assertEquals("empty array expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void singleElementArrayMatches() {
elem = 0;
in = new Integer[1];
in[0] = elem;
out = 0;
assertEquals("single element array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void singleElementArrayNotMatches() {
elem = 1;
in = new Integer[1];
in[0] = 0;
out = -1;
assertEquals("single element array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void twoElementsArrayMatchesLow() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem + 1;
out = 0;
assertEquals("two element array matches low expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void twoElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem;
out = 1;
assertEquals("two element array matches high expected 1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void twoElementsArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void threeElementsArrayMatchesMid() {
elem = 0;
in = new Integer[3];
in[0] = elem - 1;
in[1] = elem;
in[2] = elem + 1;
out = 1;
assertEquals("three element array matches mid expected 1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void threeElementsArrayMatchesLow() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem + 1;
in[2] = elem + 2;
out = 0;
assertEquals("three element array matches low expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void threeElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
out = 2;
assertEquals("three element array matches high expected 2", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void threeElementsArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
out = -1;
assertEquals("even array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenArrayMatchesLeft() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
out = 1;
assertEquals("even array matches left expected 1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenArrayMatchesRight() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem + 2;
out = 2;
assertEquals("even array matches right expected 2", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = -1;
assertEquals("odd array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddArrayMatchesLeft() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = 1;
assertEquals("odd array matches left expected 1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddArrayMatchesRight() {
elem = 0;
in = new Integer[5];
in[0] = elem - 3;
in[1] = elem - 2;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem + 1;
out = 3;
assertEquals("odd array matches right expected 3", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void twoElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem;
out = 0;
assertEquals("two element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void twoElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem + 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void threeElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem;
in[2] = elem;
out = 0;
assertEquals("three element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void threeElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
in[3] = elem + 1;
out = -1;
assertEquals("even element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem;
out = 0;
assertEquals("even element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
in[3] = elem + 1;
in[4] = elem + 1;
out = -1;
assertEquals("odd element all same array not matches expected -1", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem;
in[4] = elem;
out = 0;
assertEquals("odd element all same array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenElementsAllSameInHighArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem;
out = 2;
assertEquals("even element all same in high array matches expected 2", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void evenElementsAllSameInLowArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 1;
out = 0;
assertEquals("even element all same in low array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddElementsAllSameInHighArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem;
out = 3;
assertEquals("odd element all same in high array matches expected 3", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddElementsAllSameInLowArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem + 1;
in[4] = elem + 1;
out = 0;
assertEquals("odd element all same in low array matches expected 0", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem));
}
@Test
public void oddElementsSameInHighArrayMatches() {
elem = 0;
in = new Integer[9];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem - 1;
in[3] = elem - 1;
in[4] = elem;
in[5] = elem + 1;
in[6] = elem + 1;
in[7] = elem + 1;
in[8] = elem + 2;
out = 5;
assertEquals("odd element all same in high array matches expected 4", out, ArrayUtils.binarySearchFirstOccurrenceRecursive(in, elem + 1));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BinarySearchIterativeJTests {
Integer[] in;
int elem, out;
@Test
public void emptyArray() {
in = null;
elem = 0;
out = -1;
assertEquals("empty array expected -1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void singleElementArrayMatches() {
elem = 0;
in = new Integer[1];
in[0] = elem;
out = 0;
assertEquals("single element array matches expected 0", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void singleElementArrayNotMatches() {
elem = 1;
in = new Integer[1];
in[0] = 0;
out = -1;
assertEquals("single element array not matches expected -1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void twoElementsArrayMatchesLow() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem + 1;
out = 0;
assertEquals("two element array matches low expected 0", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void twoElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem;
out = 1;
assertEquals("two element array matches high expected 1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void twoElementsArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element array not matches expected -1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void threeElementsArrayMatchesMid() {
elem = 0;
in = new Integer[3];
in[0] = elem - 1;
in[1] = elem;
in[2] = elem + 1;
out = 1;
assertEquals("three element array matches mid expected 1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void threeElementsArrayMatchesLow() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem + 1;
in[2] = elem + 2;
out = 0;
assertEquals("three element array matches low expected 0", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void threeElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
out = 2;
assertEquals("three element array matches high expected 2", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void threeElementsArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element array not matches expected -1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void evenArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
out = -1;
assertEquals("even array not matches expected -1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void evenArrayMatchesLeft() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
out = 1;
assertEquals("even array matches left expected 1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void evenArrayMatchesRight() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem + 2;
out = 2;
assertEquals("even array matches right expected 2", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void oddArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = -1;
assertEquals("odd array not matches expected -1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void oddArrayMatchesLeft() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = 1;
assertEquals("odd array matches left expected 1", out, ArrayUtils.binarySearchIterative(in, elem));
}
@Test
public void oddArrayMatchesRight() {
elem = 0;
in = new Integer[5];
in[0] = elem - 3;
in[1] = elem - 2;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem + 1;
out = 3;
assertEquals("odd array matches right expected 3", out, ArrayUtils.binarySearchIterative(in, elem));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BinarySearchJTests {
Integer[] in;
int elem, out;
@Test
public void emptyArray() {
in = null;
elem = 0;
out = -1;
assertEquals("empty array expected -1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void singleElementArrayMatches() {
elem = 0;
in = new Integer[1];
in[0] = elem;
out = 0;
assertEquals("single element array matches expected 0", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void singleElementArrayNotMatches() {
elem = 1;
in = new Integer[1];
in[0] = 0;
out = -1;
assertEquals("single element array not matches expected -1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void twoElementsArrayMatchesLow() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem + 1;
out = 0;
assertEquals("two element array matches low expected 0", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void twoElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem;
out = 1;
assertEquals("two element array matches high expected 1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void twoElementsArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element array not matches expected -1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void threeElementsArrayMatchesMid() {
elem = 0;
in = new Integer[3];
in[0] = elem - 1;
in[1] = elem;
in[2] = elem + 1;
out = 1;
assertEquals("three element array matches mid expected 1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void threeElementsArrayMatchesLow() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem + 1;
in[2] = elem + 2;
out = 0;
assertEquals("three element array matches low expected 0", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void threeElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
out = 2;
assertEquals("three element array matches high expected 2", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void threeElementsArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element array not matches expected -1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void evenArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
out = -1;
assertEquals("even array not matches expected -1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void evenArrayMatchesLeft() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
out = 1;
assertEquals("even array matches left expected 1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void evenArrayMatchesRight() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem + 2;
out = 2;
assertEquals("even array matches right expected 2", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void oddArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = -1;
assertEquals("odd array not matches expected -1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void oddArrayMatchesLeft() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = 1;
assertEquals("odd array matches left expected 1", out, ArrayUtils.binarySearch(in, elem));
}
@Test
public void oddArrayMatchesRight() {
elem = 0;
in = new Integer[5];
in[0] = elem - 3;
in[1] = elem - 2;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem + 1;
out = 3;
assertEquals("odd array matches right expected 3", out, ArrayUtils.binarySearch(in, elem));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BinarySearchLastOccurrenceJTests {
Integer[] in;
int elem, out;
@Test
public void emptyArray() {
in = null;
elem = 0;
out = -1;
assertEquals("empty array expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void singleElementArrayMatches() {
elem = 0;
in = new Integer[1];
in[0] = elem;
out = 0;
assertEquals("single element array matches expected 0", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void singleElementArrayNotMatches() {
elem = 1;
in = new Integer[1];
in[0] = 0;
out = -1;
assertEquals("single element array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void twoElementsArrayMatchesLow() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem + 1;
out = 0;
assertEquals("two element array matches low expected 0", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void twoElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem;
out = 1;
assertEquals("two element array matches high expected 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void twoElementsArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem - 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void threeElementsArrayMatchesMid() {
elem = 0;
in = new Integer[3];
in[0] = elem - 1;
in[1] = elem;
in[2] = elem + 1;
out = 1;
assertEquals("three element array matches mid expected 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void threeElementsArrayMatchesLow() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem + 1;
in[2] = elem + 2;
out = 0;
assertEquals("three element array matches low expected 0", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void threeElementsArrayMatchesHigh() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
out = 2;
assertEquals("three element array matches high expected 2", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void threeElementsArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
out = -1;
assertEquals("even array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenArrayMatchesLeft() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
out = 1;
assertEquals("even array matches left expected 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenArrayMatchesRight() {
elem = 0;
in = new Integer[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem + 2;
out = 2;
assertEquals("even array matches right expected 2", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = -1;
assertEquals("odd array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddArrayMatchesLeft() {
elem = 0;
in = new Integer[5];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = 1;
assertEquals("odd array matches left expected 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddArrayMatchesRight() {
elem = 0;
in = new Integer[5];
in[0] = elem - 3;
in[1] = elem - 2;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem + 1;
out = 3;
assertEquals("odd array matches right expected 3", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void twoElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem;
in[1] = elem;
out = in.length - 1;
assertEquals("two element all same array matches expected in.length - 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void twoElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[2];
in[0] = elem + 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element all same array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void threeElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem;
in[1] = elem;
in[2] = elem;
out = in.length - 1;
assertEquals("three element all same array matches expected in.length - 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void threeElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[3];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element all same array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
in[3] = elem + 1;
out = -1;
assertEquals("even element all same array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem;
out = in.length - 1;
assertEquals("even element all same array matches expected in.length - 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddElementsAllSameArrayNotMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem + 1;
in[1] = elem + 1;
in[2] = elem + 1;
in[3] = elem + 1;
in[4] = elem + 1;
out = -1;
assertEquals("odd element all same array not matches expected -1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddElementsAllSameArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem;
in[4] = elem;
out = in.length - 1;
assertEquals("odd element all same array matches expected in.length - 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenElementsAllSameInHighArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem;
out = in.length - 1;
assertEquals("even element all same in high array matches expected in.length - 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void evenElementsAllSameInLowArrayMatches() {
elem = 0;
in = new Integer[4];
in[0] = elem;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 1;
out = 1;
assertEquals("even element all same in low array matches expected 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddElementsAllSameInHighArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem - 1;
in[1] = elem - 1;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem;
out = in.length - 1;
assertEquals("odd element all same in high array matches expected in.length - 1", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
@Test
public void oddElementsAllSameInLowArrayMatches() {
elem = 0;
in = new Integer[5];
in[0] = elem;
in[1] = elem;
in[2] = elem;
in[3] = elem + 1;
in[4] = elem + 1;
out = 2;
assertEquals("odd element all same in low array matches expected 2", out, ArrayUtils.binarySearchLastOccurrence(in, elem));
}
}
package com.blogspot.groglogs.arrayutils;
import com.blogspot.groglogs.structures.Apartment;
import com.blogspot.groglogs.structures.Facility;
import com.blogspot.groglogs.structures.Range;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
public class ArrayUtils {
private static void swap(int[] a, int i, int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
private static void swap(char[] a, int i, int j){
char tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
/*
Moves all the elements that are smaller than the one at the index on the left
followed by the ones that are equal to it and lastly the ones that are greater
*/
public static void partitionArray(int[] a, int index){
if(a == null || index > a.length - 1) throw new IllegalArgumentException("Array must be not null and index must exist in array");
/*
We divide the array in 4 regions: smaller, equal, unclassified, larger
We then proceed to partition the elements in a single pass
equal is the pointer to the beginning of the unclassified section and larger the pointer to the end
At the start smaller = equal and larger = end of array
It is not guaranteed that at each pass the array will be in a consistent state
but it will be by the end of the algorithm
*/
int smaller = 0, equal = 0, larger = a.length - 1, pivot = a[index];
//Use <= because in the else we do not update equals, so we might be left with a last swap to make
while(equal <= larger){
/*
if current element is smaller, swap with last of the smaller elements and move both pointers up
we might swap the element with itself, but we are also reducing the unclassified section by 1
*/
if(a[equal] < pivot){
swap(a, equal, smaller);
smaller++;
equal++;
}
/*
if current element is same, reduce unclassified by 1 but do not move smaller because we might have
another element to add there later and therefore we might have to swap again the first equal element
we are borrowing space from
*/
else if(a[equal] == pivot) equal++;
/*
if current element is larger, swap with first of larger elements (last of unclassified)
and only move larger pointer back by 1 but do not move equals, because we borrowed space from
an unclassified element we still need to check
*/
else{
swap(a, equal, larger);
larger--;
}
}
}
/*
Evaluate a reverse polish notation expression. Operators immediately follow the operands or partial result they should be applied to
eg:
3 + 4 -> 3 4 +
(3 x 2) - 6 -> 3 2 x 6 -
3 - (4 x 5) -> 3 4 5 x -
We handle only doubles at most
*/
public static double evaluateRPN(String[] expression){
if(expression == null || expression.length < 3) throw new IllegalArgumentException("expression must have at least an operand and two operators: " + expression);
Stack<Double> s = new Stack<>();
/*
Start processing the string.
To avoid creating the stack beforehand, everytime we encounter an operand, we push it on the stack
then later when we encounter an operator, we retrieve the two operands from the stack,
process them in REVERSE order, then push the result back on the stack.
The reverse order is important for asymmetrical operations such as - and / since the stack is LIFO!
*/
String curr;
Double op1, op2, tmp;
for(int i = 0; i < expression.length; i++){
curr = expression[i];
switch(curr){
case "+":
op1 = s.pop();
op2 = s.pop();
s.push(op2 + op1);
break;
case "-":
op1 = s.pop();
op2 = s.pop();
s.push(op2 - op1);
break;
case "*":
case "x":
case "X":
op1 = s.pop();
op2 = s.pop();
s.push(op2 * op1);
break;
case "/":
op1 = s.pop();
op2 = s.pop();
//double objects do not fail on division by 0 :)
tmp = op2 / op1;
if(tmp.isInfinite()) throw new ArithmeticException("Divide by 0: " + op2 + "/" + op1);
s.push(tmp);
break;
default:
//push the operand onto the stack
s.push(Double.parseDouble(curr));
break;
}
}
//get the result
tmp = s.pop();
//if the stack is not empty by now, the expression was malformed!
if(!s.isEmpty()) throw new IllegalArgumentException("Malformed expression, got at least two operands with no operator");
return tmp;
}
//helper for evaluateRPNinPlace
private static double findOperand(String[] expression, int i){
double result;
//get the value to return. If we are not a pointer (no null precedes us), return us
if(i - 1 < 0 || expression[i - 1] != null){
result = Double.parseDouble(expression[i]);
//mark us and set the pointer in the next element to whatever precedes us
expression[i] = null;
expression[i + 1] = String.valueOf(i - 1);
}
//otherwise find the value at the pointer and return that
else{
int idx = Integer.parseInt(expression[i]);
result = Double.parseDouble(expression[idx]);
//mark us and set the pointer in the next element to whatever precedes us, unless it's another pointer
//in which case, set it to that specific pointer
expression[i] = null;
//careful with out of bounds, check if the pointer is not to the first element
if(idx - 1 >= 0){
//check if the pointer has a previous element
if(idx - 2 >= 0){
//verify whether it's a marker, if not, point to the item preceding the pointer
if(expression[idx - 2] != null) expression[i + 1] = String.valueOf(idx - 1);
//if yes, point to the same item as it, so we create a jump and avoid the need for scanning the next time we come here
else expression[i + 1] = expression[idx - 1];
}
//point to the first element, otherwise we would miss this
else expression[i + 1] = String.valueOf(idx - 1);
}
}
return result;
}
/*
Evaluate a reverse polish notation expression. Operators immediately follow the operands or partial result they should be applied to
eg:
3 + 4 -> 3 4 +
(3 x 2) - 6 -> 3 2 x 6 -
3 - (4 x 5) -> 3 4 5 x -
We handle only doubles at most
Do NOT use a stack and do this like real men in constant space. Idea is to recycle the input array, who needs it anymore anyway.
Everytime we find an operator, the two operands must appear before it, in a normal case they are at index -1 (second operand) and -2 (first operand)
We calculate the result and store it in place of the operator, then we mark where the next operand would be and reuse the space
from the two operands we just had. From left to right, the second operand will be the marker (null value)
to indicate that a pointer is following this item and the first operand will be the pointer.
Whenever we encounter a marker, we know we have to return the element at the index specified in marker + 1
To keep this in constant space, we have to avoid scanning the array backwards if more pointers appear together
so whenever we set a pointer value, we also verify that it is not already a pointer itself, in which case we reuse the
previous pointer value to jump at the desired position, thus avoiding the scanning.
The result will always be in the last element of the array
eg:
3 4 5 x -
3 n 0 20 -
3 n n 20 -17
*/
public static double evaluateRPNinPlace(String[] expression){
if(expression == null || expression.length < 3) throw new IllegalArgumentException("expression must have at least an operand and two operators: " + expression);
try{
Double.parseDouble(expression[expression.length - 1]);
//if we can parse the last symbol, the expression is malformed
throw new IllegalArgumentException("Malformed expression, got at least two operands with no operator");
}catch(NumberFormatException e){
//all good, we expect an operator as last symbol
}
String curr;
double op1, op2, tmp;
for(int i = 0; i < expression.length; i++){
curr = expression[i];
//for correct pointer handling, we ALWAYS retrieve the second operand first (it's always before the operator)
//then we retrieve the first operand (we might need to jump in this case)
switch(curr){
case "+":op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
expression[i] = String.valueOf(op1 + op2);
break;
case "-":
op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
expression[i] = String.valueOf(op1 - op2);
break;
case "*":
case "x":
case "X":
op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
expression[i] = String.valueOf(op1 * op2);
break;
case "/":
op2 = Double.parseDouble(expression[i - 1]);
op1 = findOperand(expression,i - 2);
//double objects do not fail on division by 0 :)
tmp = op1 / op2;
if(Double.isInfinite(tmp)) throw new ArithmeticException("Divide by 0: " + op1 + "/" + op2);
expression[i] = String.valueOf(op1 / op2);
break;
default:
//nothing to do here
break;
}
}
return Double.parseDouble(expression[expression.length - 1]);
}
/*
Helper for convertToRPN
if op1 has same or higher precedence than op2, return true
Ranking:
- multiplication and division
- sum and subtraction
*/
public static boolean hasPrecedence(String op1, String op2){
switch(op2) {
case "+":
case "-":
case "*":
case "x":
case "X":
case "/":
//all good, we accept these operators
break;
default:
throw new IllegalArgumentException("Unknown operator: " + op2);
}
switch(op1) {
case "+":
case "-":
return op2.equals("+") || op2.equals("-");
case "*":
case "x":
case "X":
case "/":
//these are always top priority and we already tested for the lower priority ones
return true;
default:
throw new IllegalArgumentException("Unknown operator: " + op1);
}
}
/*
Convert infix expression to reverse polish notation (postfix)
Based on Dijkstra shunting yard algorithm
*/
public static String[] convertToRPN(String[] expression){
if(expression == null || expression.length < 3) throw new IllegalArgumentException("expression must have at least an operand and two operators: " + expression);
Stack<String> operators = new Stack<>();
Queue<String> out = new LinkedList<>();
for(String curr : expression){
switch(curr) {
case "+":
case "-":
case "*":
case "x":
case "X":
case "/":
//check operator precedence: multiplication and division are same and go before sum and subtraction
//as long as we find same or higher precedence, we need to add those to the result
//skip parenthesis! they might be the very first element, respect their meaning!
while (!operators.isEmpty() && !operators.peek().equals("(") && !operators.peek().equals(")") && hasPrecedence(operators.peek(), curr)) {
out.add(operators.pop());
}
operators.push(curr);
break;
case "(":
operators.push(curr);
break;
case ")":
while(!operators.isEmpty() && !operators.peek().equals("(")) out.add(operators.pop());
//if we do not have a matching parentheses, we got a malformed expression, otherwise we remove the matching opening bracket
try{
operators.pop();
}catch(EmptyStackException e){
throw new IllegalArgumentException("Mismatched parentheses found, missing '('");
}
break;
default:
//check if it's a number or garbage, if ok, move to output
try {
//this will throw an exception for us if it is not a number, therefore the expression is malformed
Double.parseDouble(curr);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Unknown token found while parsing, expected number, got: " + curr);
}
out.add(curr);
break;
}
}
//finish adding the last operators to the output, if we have parentheses, the expression was malformed
while(!operators.isEmpty()){
String curr = operators.pop();
if(curr.equals("(") || curr.equals(")")) throw new IllegalArgumentException("Mismatched parentheses found: " + curr);
out.add(curr);
}
//convert the output to a properly sized array
String[] res = new String[out.size()];
int i = -1; //convenient for ++i later
while(!out.isEmpty()){
res[++i] = out.poll();
}
return res;
}
/*
Given an array of integers in the range 1..N and length N+1 find one of the possible duplicate elements.
To run in constant space without destroying the input, we treat the array as a graph
where each value is a pointer to another element of the same array.
Start from the LAST element, since range is 1..N and length is N+1 but arrays are 0-based, so nothing can point to it
and keep iterating until a cycle is identified, therefore a duplicate element is found.
Always handle pointers - 1 since array is 0 based!
Cycle detection is based on Floyd's algorithm
*/
public static int findRepeat(int[] input) {
if(input == null || input.length < 2) throw new IllegalArgumentException("Array length must be at least 2");
//use two pointers, one ahead of the other starting from the end of the array
int slow = input[input.length - 1], fast = input[slow - 1];
//identify the loop
while(slow != fast){
slow = input[slow - 1];
fast = input[input[fast - 1] - 1];
}
//search for the duplicate element
slow = input.length;
while(slow != fast){
slow = input[slow - 1];
fast = input[fast - 1];
}
return slow;
}
/*
Given an array of integers in the range 1..N and length N+1 find one of the possible duplicate elements.
To run in constant space without destroying the input, we treat the array as a graph
where each value is a pointer to another element of the same array.
Start from the LAST element, since range is 1..N and length is N+1 but arrays are 0-based, so nothing can point to it
and keep iterating until a cycle is identified, therefore a duplicate element is found.
Always handle pointers - 1 since array is 0 based!
Cycle detection is based on Brent's algorithm
*/
public static int findRepeatBrent(int[] input) {
if(input == null || input.length < 2) throw new IllegalArgumentException("Array length must be at least 2");
int slow = input[input.length - 1], fast = input[slow - 1], limit = 1, step = 1; //optional: idx = 0
while(slow != fast){
if(step == limit){
slow = fast;
limit *= 2;
step = 0;
}
fast = input[fast - 1];
step++;
}
slow = fast = input[input.length - 1];
for(int i = 0; i < step; i++) fast = input[fast - 1];
while(slow != fast){
slow = input[slow - 1];
fast = input[fast - 1];
//optional idx++
}
//optional idx would tell us after how many jumps from the start we encounter the loop start
return slow;
}
/*
Given an array of integers in the range 1..N and length N+1 find one of the possible duplicate elements.
Without using the graph idea it is still possible to return the result in constant space without destroying the input
but time will be slower O(N log N).
This implementation applies the binary search logic to iteratively limit the set of possible duplicate elements
and zone in one of them.
REMEMBER 0-base index arrays! In this case it ONLY applies to ceiling being length-1!
We divide the search space in two halves: 1..N/2 and (N/2 + 1)..N so both halves together still cover the full range 1..N
CAUTION! starting floor is 1 and NOT 0 since items are in range 1..N!
and we know at least one of the halves contains at least one duplicate element. Therefore we can count the number of
distinct possibilities for an item in the range we are considering and compare it to the actual number of items in that range
in the FULL array! We are just looking at each time if in a specific, constantly shrinking search space, we have one or more
items that in the FULL input appear multiple times.
If this is greater than the possibilities, we definitely have a duplicate there, therefore we can repeat the process
ITERATIVELY otherwise we get an increased space cost because of the call stack, until we are left with a single element, which
is one of our duplicates
*/
public static int findRepeatBinarySearch(int[] input) {
if(input == null || input.length < 2) throw new IllegalArgumentException("Array length must be at least 2");
//value range is 1..N and arrays are 0-based!
int floor = 1, ceil = input.length - 1, mid, lowFloor, lowCeil, highFloor, highCeil, possibilities, count;
//keep searching the possibilities space by evaluating a shrinking range of candidates for the duplicate value
while(floor < ceil){
mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
lowFloor = floor;
lowCeil = mid;
highFloor = mid + 1;
highCeil = ceil;
//how many distinct candidates do we have in this search range
possibilities = lowCeil - lowFloor + 1;
count = 0;
/*
Count how many items are in this search range from the FULL array because we are NOT reducing the array size
but the SEARCH SPACE size. We will perform O(log N) iterations before the search range is so small we identify the item
but each time we have to evaluate the FULL array O(N) against the current search range -> O(N log N)
This is the key: if there are no duplicates, the number of candidates and the number of values in the range must match
But since we know there IS at least one duplicate, we will at some point find a range where the number of items
is greater than the possibilities, meaning we have at least a duplicate there
*/
for(int i : input) if(i >= lowFloor && i <= lowCeil) count++;
//move the search range to the one that contains the duplicate item
if(count > possibilities){
floor = lowFloor;
ceil = lowCeil;
}
else{
floor = highFloor;
ceil = highCeil;
}
}
//when the search range has converged, we have identified the duplicate
return floor;
}
/*
The input is an array of elevations, the goal is to determine the maximum
amount of water that can be stored in the lakes, if any. Sea level is 0
*/
public static int getIslandLakesCapacity(int[] island){
if(island == null) throw new IllegalArgumentException("Island cannot be null!");
//track each depression how much water can it take
//we scan twice, from both directions. In the first pass we find how much water can be stored
//from the left border to the highest peak. At the end we track the highest peak height and
//scan again from the right border. We stop as soon as we find a peak as high as the highest
//then we're sure we correctly calculated the full island capacity
int[] water = new int[island.length];
int curr = 0;
//left to right scan
for(int i = 0; i < island.length; i++){
if(island[i] > curr) curr = island[i];
water[i] = curr - island[i];
}
//track highest peak
int max = curr;
curr = 0;
//right to left scan
for(int i = island.length - 1; i >= 0; i--){
//stop as soon as we reached the highest peak
if(island[i] == max) break;
if(island[i] > curr) curr = island[i];
water[i] = curr - island[i];
}
int tot = 0;
//get the final lake capacity
for(int i = 0; i < water.length; i++){
tot += water[i];
}
return tot;
}
//performs the Fisher-Yates shuffle of a given array
//guaranteed to generated a uniformly distributed random permutation of the input array
public static void FisherYatesShuffle(int[] a){
//nothing to shuffle, return input
if(a == null || a.length < 2) return;
//instance a new random generator
Random gen = new Random();
//place all randomly selected elements at the end of the array
//then decrease the pool size before getting the next random element position
//repeat for the whole array
for(int curr = a.length - 1; curr >= 0; curr--){
//random between 0 inclusive and curr + 1 exclusive, effectively making [0..curr] range
int swapWith = gen.nextInt(curr + 1);
//no need to swap a place with itself
if(curr != swapWith){
swap(a, curr, swapWith);
}
}
}
//helper for getMaxGoldFromPotsNoDP
private static int findMaxPickNoDP(int[] pots, int floor, int ceil, boolean player){
//stop when we are at the last pot
if(floor == ceil){
//if it's our pick, add it to the value, else we get nothing
return player ? pots[floor] : 0;
}
//if it's not our turn, the opponent will leave us with the worst pick. use MIN and do NOT pick!
if(!player) return Math.min(findMaxPickNoDP(pots, floor + 1, ceil, !player),
findMaxPickNoDP(pots, floor, ceil - 1, !player)
);
//otherwise PICK and find the MAX!
return Math.max(pots[floor] + findMaxPickNoDP(pots, floor + 1, ceil, !player),
pots[ceil] + findMaxPickNoDP(pots, floor, ceil - 1, !player)
);
}
/*
Given a series of gold pots with different contents, two players can alternatively pick a pot from either side.
Find the maximum amount of gold the first player can get.
starter indicates the starting player (true = first player, false = second player)
*/
public static int getMaxGoldFromPotsNoDP(int[] pots, boolean starter){
if(pots == null) throw new IllegalArgumentException("Pots can't be null!");
return findMaxPickNoDP(pots, 0, pots.length - 1, starter);
}
//helper for getMaxGoldFromPots
private static int findMaxPick(int[] pots, int floor, int ceil, boolean player, int[][][] cache){
//convert player to matrix index
int p = player ? 1 : 0;
//if we already calculated the value, return immediately
int pick = cache[p][floor][ceil];
if(pick != -1) return pick;
//stop when we are at the last pot
if(floor == ceil){
//if it's our pick, add it to the value, else we get nothing
pick = player ? pots[floor] : 0;
//cache the value
cache[p][floor][ceil] = pick;
return pick;
}
//maybe we already calculated it
int left = cache[p][floor + 1][ceil];
int right = cache[p][floor][ceil - 1];
//if not, calculate
if(left == -1) left = findMaxPick(pots, floor + 1, ceil, !player, cache);
if(right == -1) right = findMaxPick(pots, floor, ceil - 1, !player, cache);
//if it's not our turn, the opponent will leave us with the worst pick. use MIN and do NOT pick!
if(!player){
pick = Math.min(left, right);
//otherwise PICK and find the MAX!
} else {
pick = Math.max(pots[floor] + left, pots[ceil] + right);
}
//cache the value
cache[p][floor][ceil] = pick;
return pick;
}
/*
Given a series of gold pots with different contents, two players can alternatively pick a pot from either side.
Find the maximum amount of gold the first player can get.
starter indicates the starting player (true = first player, false = second player)
*/
public static int getMaxGoldFromPots(int[] pots, boolean starter){
if(pots == null) throw new IllegalArgumentException("Pots can't be null!");
//initialize cache for players and picks
int[][][] cache = new int[2][pots.length][pots.length];
for(int i = 0; i < pots.length; i++){
for(int j = 0; j < pots.length; j++){
cache[0][i][j] = -1;
cache[1][i][j] = -1;
}
}
return findMaxPick(pots, 0, pots.length - 1, starter, cache);
}
//helper for reverseWords. Simply reverses all items in a given array from start to end index
private static void reverseWord(char[] in, int start, int end){
for(; start < end; start++, end--){
swap(in, start, end);
}
}
//reverse words order in a char array. Words are separated by a single space and no other characters are allowed
public static void reverseWords(char[] in){
if(in == null || in.length == 1) return;
for(int i = 0, j = in.length - 1; i < j; i++, j--){
swap(in, i, j);
}
int start_idx = 0;
//walk the full array and track start and end index for each word, reversing it if we find a delimiter or reach the end of the array
for(int i = 0; i <= in.length; i++){
if(i == in.length || in[i] == ' '){
//i-1! the word ends BEFORE us
reverseWord(in, start_idx, i - 1);
//next word starts for sure after the delimiter
start_idx = i + 1;
}
}
}
/*find the subarray with the largest sum
for an array of all negative numbers, return the smallest of them
for an array of all positive numbers, return the sum of all element
*/
public static Range getLargestSumSubarray(int[] in){
if(in == null) throw new IllegalArgumentException("null array is not allowed!");
//track the global maximum and the local maximum. Local maximum is the sum of elements until the one being currently considered
//initialize both the first array element
int curr = in[0], max = in[0], start = 0;
Range res = new Range(0, 0, max);
//and then remember to skip if when starting the loop!
for(int i = 1; i < in.length; i++){
//local sum can either be extended if the current element improves on it, or reset to start from the current element otherwise
int partial = curr + in[i];
if(partial > in[i]){
curr = partial;
}
else{
curr = in[i];
start = i;
}
//always update the global maximum
if(curr > max){
max = curr;
res.start = start;
res.end = i;
res.tot = max;
}
}
return res;
}
/*
Given an array indicating how many people live at the specific index, return the index where to place
a postbox to minimize the total travel time to it for all people.
People residing in the same place of the postbox have a total travel time of 0
N people needing to take 1 step to reach the postbox have a total travel time of N
Multiple solutions could be acceptable, this algorithm will return the first best found starting from the END
*/
public static int getPostboxPlace(int[] in){
if(in == null) throw new IllegalArgumentException("null array is not allowed!");
if(in.length == 1) return 0; //no need to do anything
//track here all the steps people would have to do to reach each position moving
//from left to right, do not consider the other positions yet
int[] steps = new int[in.length];
steps[0] = 0;
int tot = in[0];
//at each position, the people in the previous position must do ONE step
//PLUS ALL the people from before have to do ONE EXTRA step
for(int i = 1; i < in.length; i++){
steps[i] = tot + steps[i - 1];
tot += in[i];
}
int min = Integer.MAX_VALUE, place = -1, prev = 0, curr = 0;
tot = 0;
//walking right to left, use the previously calculated information from the array
//with its counterpart which we can now calculate on the fly, to determine the cost of placing the postbox in the current position
//and keep tracking the best result found so far
for(int i = in.length - 1; i > 0; i--){
//steps[i] (right to left) + steps[i] (left to right)
if(steps[i] + curr < min){
min = steps[i] + curr;
place = i;
}
//we do not need to build another array, these lines are the key
prev = curr; //prev is steps[i+1] for the array of steps from left to right
tot += in[i];
curr = tot + prev; //this is steps[i] for the next round
}
return place;
}
/*
Given a start and end array, generate a list of positions where to swap the 0 element in order to move all elements
from start to end position.
Elements can only be swapped with the 0
Each element can only appear once
There can be more than one way to obtain the desired swap
*/
public static List<Integer> forcedSwaps(int[] start, int[] end){
if(start == null || end == null) throw new IllegalArgumentException("Start and end array cannot be null!");
if(start.length != end.length) throw new IllegalArgumentException("Start and end array must have same length!");
//current position in the intermediate array (we recycle start) of each element
Map<Integer, Integer> pos = new HashMap<>();
boolean needsSwap = false;
//needed to check validity of input
Set<Integer> start_elements = new HashSet<>();
Set<Integer> end_elements = new HashSet<>();
for(int i = 0; i < start.length; i++){
if(start_elements.contains(start[i]) || end_elements.contains(end[i])) throw new IllegalArgumentException("Duplicate entries not allowed!");
start_elements.add(start[i]);
end_elements.add(end[i]);
pos.put(start[i], i);
needsSwap = needsSwap || start[i] != end[i];
}
if(!start_elements.containsAll(end_elements)) throw new IllegalArgumentException("Start and end array must have the same elements!");
if(!start_elements.contains(0)) throw new IllegalArgumentException("Element 0 must be in the arrays!");
List<Integer> out = new LinkedList<>();
if(!needsSwap) return out;
//start from first element and verify if it matches the desired position
//if not, swap it with the 0
//then check again, if it still does not match, swap the 0 with the expected element
//always track the new position of each element
//then move to the next position
int curr = 0;
while(curr < end.length){
if(start[curr] != end[curr]){
//move 0 to current position
int val = start[curr]; //element at current position
int to;
//if current element is already 0, no need to do anything
if(val != 0){
start[curr] = 0;
to = pos.get(0); //we move the element at the current position of the 0
pos.put(0, curr);
start[to] = val;
pos.put(val, to);
out.add(curr); //we moved the 0 here
}
//if necessary swap again the 0 with the desired element
if(start[curr] != end[curr]){
start[curr] = end[curr];
to = pos.get(end[curr]);
pos.put(end[curr], curr);
pos.put(0, to);
start[to] = 0;
out.add(to); //we moved the 0 here as well
}
}
curr++;
}
return out;
}
/*
Given an array of Apartments, each indicating which Facilities are present there, and an array of relevant facilities
we need to find which apartment minimizes the farthest travel distance from all relevant facilities.
If a facility is present at the apartment, travel distance is 0.
If two facilities are at distance one from the apartment, the maximum travel distance is 1, not the sum.
We need to return the index of the apartment that satisfies our criteria.
If multiple apartments satisfy our criteria, we return any of them.
If no apartment satisfies our criteria, we return -1.
*/
public static int apartmentWithMinimumFarthestDistanceFromFacilities(Apartment[] apartments, Facility[] facilities){
if(apartments == null || facilities == null || apartments.length == 0 || facilities.length == 0) throw new IllegalArgumentException("At least one apartment and facility must be provided");
//rows = apartment, columns = minimum distance from facility
int[][] distances = new int[apartments.length][facilities.length];
//init our matrix with MAX_INT as initial distance
for(int i = 0; i < apartments.length; i++){
for(int j = 0; j < facilities.length; j++){
if(apartments[i] == null || facilities[j] == null) throw new IllegalArgumentException("Apartments and facilities cannot be null");
distances[i][j] = Integer.MAX_VALUE;
}
}
/*walk from first apartment to the right, we calculate PARTIAL distances for all facilities seeing what information we
could gather given the apartments visited so far
If an apartment has a facility there, distance is 0, otherwise we use distance for that facility
from previous apartment + 1, if it existed, otherwise we haven't encountered that facility yet and we keep MAX_INT
*/
for(int i = 0; i < apartments.length; i++){
for(int j = 0; j < facilities.length; j++){
//this facility is present at this apartment
if(apartments[i].facilities.get(facilities[j])){
distances[i][j] = 0;
continue;
}
//if not present, use distance for that facility from previous apt + 1
//if distance was infinite, avoid overflow
//if we are first apartment, avoid out of bounds
if(i - 1 >= 0 && distances[i - 1][j] != Integer.MAX_VALUE){
distances[i][j] = distances[i - 1][j] + 1;
}
}
}
//at this point, the last apartment is the only one with full knowledge, since there is no other after him
//so we can initialize the result to it
//we look for the MAXIMUM travel distance for this apartment from ANY facility
//later, we want to MINIMIZE this value
int currMinFarthestDistance = Integer.MIN_VALUE;
for(int j = 0; j < facilities.length; j++){
currMinFarthestDistance = Math.max(currMinFarthestDistance, distances[apartments.length - 1][j]);
}
int currBestApartment = apartments.length - 1;
/*since we already processed the last apartment, walk from second to last apartment to the left,
we calculate FINAL distances for all facilities seeing what information we
gathered given all apartments have been visited
We update if necessary the distance to the minimum between the current distance value at that apartment and
the distance from the previous apartment + 1
This means we now know where the closest facility is, therefore we might need to update our current value
*/
for(int i = apartments.length - 2; i >= 0; i--){
//while we update our knowledge, we also track where is the farthest facility for us
int farthestDistanceForThisApartment = Integer.MIN_VALUE;
for(int j = 0; j < facilities.length; j++){
//see if we now know there is a closer facility than previously thought
//it's either us or the next apartment + 1
//if distance was infinite, avoid overflow
if(distances[i + 1][j] != Integer.MAX_VALUE) {
distances[i][j] = Math.min(distances[i][j], distances[i + 1][j] + 1);
}
farthestDistanceForThisApartment = Math.max(farthestDistanceForThisApartment, distances[i][j]);
}
//if we are the best apartment so far, we update this information
if(currMinFarthestDistance > farthestDistanceForThisApartment){
currMinFarthestDistance = farthestDistanceForThisApartment;
currBestApartment = i;
}
}
//if a facility was missing everywhere, there is no best apartment
if(currMinFarthestDistance == Integer.MAX_VALUE){
return -1;
}
//otherwise we have here one of the best
return currBestApartment;
}
/*
Given an array of elements, find any position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearch(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearch(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
return binarySearch(elements, element, 0, elements.length - 1);
}
//helper for binarySearch
private static <T extends Comparable<T>> int binarySearch(T[] elements, T element, int floor, int ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)) return mid;
while(floor < ceil){
//if middle element is bigger than the one we're looking for, search on its left
if(elements[mid].compareTo(element) > 0) return binarySearch(elements, element, floor, mid - 1);
//otherwise search on its right
return binarySearch(elements, element, mid + 1, ceil);
}
return -1;
}
/*
Given an array of elements, find the FIRST position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchFirstOccurrenceRecursive(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearchFirstOccurrenceRecursive(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
return binarySearchFirstOccurrenceRecursive(elements, element, 0, elements.length - 1, -1);
}
//helper for binarySearchFirstOccurrenceRecursive
private static <T extends Comparable<T>> int binarySearchFirstOccurrenceRecursive(T[] elements, T element, int floor, int ceil, int result){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)) {
result = mid;
}
while(floor < ceil){
//if middle element is bigger or equal to the one we're looking for, search on its left since we're looking for FIRST occurrence
if(elements[mid].compareTo(element) >= 0) return binarySearchFirstOccurrenceRecursive(elements, element, floor, mid - 1, result);
//otherwise search on its right
return binarySearchFirstOccurrenceRecursive(elements, element, mid + 1, ceil, result);
}
return result;
}
/*
Given an array of elements, find the LAST position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchLastOccurrenceRecursive(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearchLastOccurrenceRecursive(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
return binarySearchLastOccurrenceRecursive(elements, element, 0, elements.length - 1, -1);
}
//helper for binarySearchLastOccurrenceRecursive
private static <T extends Comparable<T>> int binarySearchLastOccurrenceRecursive(T[] elements, T element, int floor, int ceil, int result){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)) {
result = mid;
}
while(floor < ceil){
//if middle element is bigger than the one we're looking for, search on its left
if(elements[mid].compareTo(element) > 0) return binarySearchLastOccurrenceRecursive(elements, element, floor, mid - 1, result);
//otherwise search on its right, even if equal since the LAST occurrence must be AFTER this one
return binarySearchLastOccurrenceRecursive(elements, element, mid + 1, ceil, result);
}
return result;
}
/*
Given an array of elements, find the any position of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchIterative(elements, element)
If array is null, element does not fit inside
*/
public static <T extends Comparable<T>> int binarySearchIterative(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
if(elements[mid].equals(element)){
return mid;
}
//if middle element is bigger than the one we're looking for, search on its left
if(elements[mid].compareTo(element) >= 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return -1;
}
/*
Given an array of elements, find the FIRST occurrence of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchFirstOccurrence(elements, element)
If array is null, element does not fit inside
We use iterative approach since we need to remember the last found element in case of duplicates and it's easier than recursive to track
*/
public static <T extends Comparable<T>> int binarySearchFirstOccurrence(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1, result = -1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
//in this case, do not return immediately, rather save this location and search on its left
//if there are other prior duplicates, we will find them until we terminate the search
//at that point, in result we will have the last seen index of the element, which is the first occurrence
if(elements[mid].equals(element)){
result = mid;
}
//if middle element is bigger or equals than the one we're looking for, search on its left
//in this case we use equals since we are looking for first occurrence, so we want to move left
//even when a result was found
if(elements[mid].compareTo(element) >= 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return result;
}
/*
Given an array of elements, find the FIRST occurrence of a specific one using binary search
If element is found, return its index
Otherwise return -1
Array MUST be sorted first
Invoke as ArrayUtils.<TYPE>binarySearchLastOccurrence(collection, element)
If array is null, element does not fit inside
We use iterative approach since we need to remember the last found element in case of duplicates and it's easier than recursive to track
*/
public static <T extends Comparable<T>> int binarySearchLastOccurrence(T[] elements, T element){
if(elements == null || elements.length == 0) return -1;
int floor = 0, ceil = elements.length - 1, result = -1;
while(floor <= ceil){
//we don't always start from 0, so we need to offset our floor every time we find the new mid
int mid = floor + ((ceil - floor) / 2); //binary search magic! NEVER forget this
//in this case, do not return immediately, rather save this location and search on its right
//if there are other duplicates after it, we will find them until we terminate the search
//at that point, in result we will have the last seen index of the element, which is the last occurrence
if(elements[mid].equals(element)){
result = mid;
}
//if middle element is bigger than the one we're looking for, search on its left
//in this case we do NOT use equals since we are looking for last occurrence
//therefore we want to move right even if element was found
if(elements[mid].compareTo(element) > 0) {
ceil = mid - 1;
} else {
floor = mid + 1;
}
}
return result;
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BinarySearchRotatedJTests {
int[] in;
int elem, out;
@Test
public void emptyArray() {
in = null;
elem = 0;
out = -1;
assertEquals("empty array expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void singleElementArrayMatches() {
elem = 0;
in = new int[1];
in[0] = elem;
out = 0;
assertEquals("single element array matches expected 0", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void singleElementArrayNotMatches() {
elem = 1;
in = new int[1];
in[0] = 0;
out = -1;
assertEquals("single element array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void twoElementsArrayMatchesLow() {
elem = 0;
in = new int[2];
in[0] = elem;
in[1] = elem + 1;
out = 0;
assertEquals("two element array matches low expected 0", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void twoElementsArrayMatchesHigh() {
elem = 0;
in = new int[2];
in[0] = elem - 1;
in[1] = elem;
out = 1;
assertEquals("two element array matches high expected 1", out, ArrayUtils.rotatedBinarySearch(elem,in));
elem = 0;
in = new int[2];
in[0] = 1;
in[1] = 0;
out = 1;
assertEquals("rotated two element array matches high expected 1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void twoElementsArrayNotMatches() {
elem = 0;
in = new int[2];
in[0] = elem - 1;
in[1] = elem + 1;
out = -1;
assertEquals("two element array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
elem = 0;
in = new int[2];
in[0] = 1;
in[1] = -1;
out = -1;
assertEquals("rotated two element array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void threeElementsArrayMatchesMid() {
elem = 0;
in = new int[3];
in[0] = elem - 1;
in[1] = elem;
in[2] = elem + 1;
out = 1;
assertEquals("three element array matches mid expected 1", out, ArrayUtils.rotatedBinarySearch(elem,in));
elem = -1;
in = new int[3];
in[0] = 1;
in[1] = -1;
in[2] = 0;
out = 1;
assertEquals("rotated three element array matches mid expected 1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void threeElementsArrayMatchesLow() {
elem = 0;
in = new int[3];
in[0] = elem;
in[1] = elem + 1;
in[2] = elem + 2;
out = 0;
assertEquals("three element array matches low expected 0", out, ArrayUtils.rotatedBinarySearch(elem,in));
elem = 3;
in = new int[3];
in[0] = 3;
in[1] = 1;
in[2] = 2;
out = 0;
assertEquals("rotated three element array matches low expected 0", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void threeElementsArrayMatchesHigh() {
elem = 0;
in = new int[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
out = 2;
assertEquals("three element array matches high expected 2", out, ArrayUtils.rotatedBinarySearch(elem,in));
elem = 1;
in = new int[3];
in[0] = 2;
in[1] = 0;
in[2] = 1;
out = 2;
assertEquals("rotated three element array matches high expected 2", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void threeElementsArrayNotMatches() {
elem = 0;
in = new int[3];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
out = -1;
assertEquals("three element array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
elem = 0;
in = new int[3];
in[0] = 2;
in[1] = 1;
in[2] = -1;
out = -1;
assertEquals("rotated three element array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void evenArrayNotMatches() {
elem = 0;
in = new int[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
out = -1;
assertEquals("even array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void evenArrayMatchesLeft() {
elem = 0;
in = new int[4];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
out = 1;
assertEquals("even array matches left expected 1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void evenArrayMatchesRight() {
elem = 0;
in = new int[4];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem;
in[3] = elem + 2;
out = 2;
assertEquals("even array matches right expected 2", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void oddArrayNotMatches() {
elem = 0;
in = new int[5];
in[0] = elem - 2;
in[1] = elem - 1;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = -1;
assertEquals("odd array not matches expected -1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void oddArrayMatchesLeft() {
elem = 0;
in = new int[5];
in[0] = elem - 2;
in[1] = elem;
in[2] = elem + 1;
in[3] = elem + 2;
in[4] = elem + 3;
out = 1;
assertEquals("odd array matches left expected 1", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void oddArrayMatchesRight() {
elem = 0;
in = new int[5];
in[0] = elem - 3;
in[1] = elem - 2;
in[2] = elem - 1;
in[3] = elem;
in[4] = elem + 1;
out = 3;
assertEquals("odd array matches right expected 3", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
@Test
public void decreasing() {
elem = 0;
in = new int[5];
in[0] = 1;
in[1] = 2;
in[2] = 3;
in[3] = 4;
in[4] = 0;
out = 4;
assertEquals("rotated decreasing matches last", out, ArrayUtils.rotatedBinarySearch(elem,in));
}
}
package com.blogspot.groglogs.comparator;
import java.util.Comparator;
//to be used in longestIncreasingFailedBuildSequence
//we want to say true comes BEFORE false when comparing booleans
//so it's the opposite of the natural ordering
public class BooleanInvertedComparator implements Comparator<Boolean> {
@Override
public int compare(Boolean b1, Boolean b2) {
if(b1 == b2) return 0;
if(b1) return -1;
return 1;
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class CanPartitionInKSubsetsOfEqualSumJTests {
int[] numbers;
int k;
@Test
public void nullEmpty() {
numbers = null;
k = 10;
assertFalse("null array", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{};
k = 10;
assertFalse("empty array", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
@Test
public void badK() {
numbers = new int[]{1,2,3,4,5};
k = 100;
assertFalse("k > array length", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{1,2,3,4,5};
k = 0;
assertFalse("k = 0", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{1,1,1,1,1};
k = 3;
assertFalse("tot % k != 0", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
@Test
public void oneElement() {
numbers = new int[]{10};
k = 1;
assertTrue("10 - 10", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
@Test
public void twoElements() {
numbers = new int[]{10,10};
k = 1;
assertTrue("10,10 - 20", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{10,10};
k = 2;
assertTrue("10,10 - 10", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
@Test
public void threeElements() {
numbers = new int[]{3,7,10};
k = 1;
assertTrue("3,7,10 - 20", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{3,7,10};
k = 2;
assertTrue("3,7,10 - 10", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{3,7,10};
k = 3;
assertFalse("3,7,10 - impossible", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
@Test
public void split() {
numbers = new int[]{1,1,1,1,1,2,2,2,2,2};
k = 5;
//only solution is to pair each 1 with a 2
assertTrue("1,1,1,1,1,2,2,2,2,2 - 3", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
@Test
public void sample() {
numbers = new int[]{4,3,2,3,5,2,1};
k = 4;
assertTrue("4,3,2,3,5,2,1 - 5", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{4,3,2,3,5,2,1};
k = 2;
assertTrue("4,3,2,3,5,2,1 - 10", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
numbers = new int[]{4,3,2,3,5,2,1};
k = 3;
assertFalse("4,3,2,3,5,2,1 - impossible", ArrayUtils.canPartitionInKSubsetsOfEqualSum(numbers, k));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CountOutOfOrderElementsJTests {
int[] numbers;
@Test
public void nullEmpty() {
numbers = null;
assertEquals("null", 0, ArrayUtils.countOutOfOrderElements(numbers));
assertEquals("null mergesort", 0, ArrayUtils.countOutOfOrderElementsMergeSort(numbers));
numbers = new int[]{};
assertEquals("empty", 0, ArrayUtils.countOutOfOrderElements(numbers));
assertEquals("empty mergesort", 0, ArrayUtils.countOutOfOrderElementsMergeSort(numbers));
}
@Test
public void oneElement() {
numbers = new int[]{1};
assertEquals("1", 0, ArrayUtils.countOutOfOrderElements(numbers));
assertEquals("1 mergesort", 0, ArrayUtils.countOutOfOrderElementsMergeSort(numbers));
}
@Test
public void ascending() {
numbers = new int[]{1,2,3,4,5};
assertEquals("1,2,3,4,5", 0, ArrayUtils.countOutOfOrderElements(numbers));
assertEquals("1,2,3,4,5 mergesort", 0, ArrayUtils.countOutOfOrderElementsMergeSort(numbers));
}
@Test
public void descending() {
numbers = new int[]{5,4,3,2,1};
assertEquals("5,4,3,2,1", 10, ArrayUtils.countOutOfOrderElements(numbers));
assertEquals("5,4,3,2,1 mergesort", 10, ArrayUtils.countOutOfOrderElementsMergeSort(numbers));
}
@Test
public void sample() {
numbers = new int[]{2, 4, 1, 3, 5};
assertEquals("2, 4, 1, 3, 5", 3, ArrayUtils.countOutOfOrderElements(numbers));
assertEquals("2, 4, 1, 3, 5 mergesort", 3, ArrayUtils.countOutOfOrderElementsMergeSort(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class DisjointPartitionJTests {
int[] numbers;
@Test
public void nullEmpty() {
numbers = null;
assertEquals("null", 0, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{};
assertEquals("empty", 0, ArrayUtils.disjointPartition(numbers));
}
@Test
public void oneElement() {
numbers = new int[]{1};
assertEquals("1", numbers.length, ArrayUtils.disjointPartition(numbers));
}
@Test
public void twoElements() {
numbers = new int[]{1,1};
assertEquals("1,1", 1, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{1,2};
assertEquals("1,2", 1, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{2,1};
assertEquals("2,1", numbers.length, ArrayUtils.disjointPartition(numbers));
}
@Test
public void ascending() {
numbers = new int[]{1,2,3};
assertEquals("1,2,3", 1, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{1,1,2,3};
assertEquals("1,1,2,3", 1, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{1,2,2,3};
assertEquals("1,2,2,3", 1, ArrayUtils.disjointPartition(numbers));
}
@Test
public void descending() {
numbers = new int[]{3,2,1};
assertEquals("3,2,1", numbers.length, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{3,2,1,1,1};
assertEquals("3,2,1,1,1", numbers.length, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{3,3,2,2,1,1};
assertEquals("3,3,2,2,1,1", numbers.length, ArrayUtils.disjointPartition(numbers));
}
@Test
public void opposites() {
numbers = new int[]{100,2,3,99,5,6,101};
assertEquals("100,2,3,99,5,6,101", numbers.length - 1, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{100,6,5,99,3,2,101};
assertEquals("100,6,5,99,3,2,101", numbers.length - 1, ArrayUtils.disjointPartition(numbers));
}
@Test
public void middle() {
numbers = new int[]{5,4,3,2,100,101};
assertEquals("5,4,3,2,100,101", 4, ArrayUtils.disjointPartition(numbers));
}
@Test
public void sample() {
numbers = new int[]{1,1,1,1,0,6,12};
assertEquals("1,1,1,1,0,6,12", 5, ArrayUtils.disjointPartition(numbers));
numbers = new int[]{5,0,3,8,6};
assertEquals("5,0,3,8,6", 3, ArrayUtils.disjointPartition(numbers));
}
}
package com.blogspot.groglogs.structures;
//for apartmentWithMinimumFarthestDistanceFromFacilities
public enum Facility {
GYM,
SCHOOL,
STORE
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;
public class FallingDominoesJTests {
char[] dominoes, expected;
@Test
public void nullEmpty() {
dominoes = null;
assertNull("null", ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{};
expected = new char[]{};
assertArrayEquals("empty", expected, ArrayUtils.fallingDominoes(dominoes));
}
@Test(expected = IllegalArgumentException.class)
public void invalidChar() {
dominoes = new char[]{'.','R','L','x'};
ArrayUtils.fallingDominoes(dominoes);
}
@Test
public void allStanding() {
dominoes = new char[]{'.','.','.'};
expected = dominoes;
assertArrayEquals("all standing", expected, ArrayUtils.fallingDominoes(dominoes));
}
@Test
public void allFalling() {
dominoes = new char[]{'R','R','R'};
expected = dominoes;
assertArrayEquals("all falling R", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'L','L','L'};
expected = dominoes;
assertArrayEquals("all falling L", expected, ArrayUtils.fallingDominoes(dominoes));
}
@Test
public void balanced() {
dominoes = new char[]{'R','.','L'};
expected = dominoes;
assertArrayEquals("R.L", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','R','.','L','.'};
expected = dominoes;
assertArrayEquals(".R.L.", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'R','L'};
expected = dominoes;
assertArrayEquals("RL", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','R','L','.'};
expected = dominoes;
assertArrayEquals(".RL.", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'R','.','L','R','.','L'};
expected = dominoes;
assertArrayEquals("R.LR.L", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'R','.','.','L'};
expected = new char[]{'R','R','L','L'};
assertArrayEquals("R..L", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','R','.','.','L','.'};
expected = new char[]{'.','R','R','L','L','.'};
assertArrayEquals(".R..L.", expected, ArrayUtils.fallingDominoes(dominoes));
}
@Test
public void outward() {
dominoes = new char[]{'.','L','R','.'};
expected = new char[]{'L','L','R','R'};
assertArrayEquals(".LR.", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','L','.','R','.'};
expected = new char[]{'L','L','.','R','R'};
assertArrayEquals(".L.R.", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','.','L','R','.','.'};
expected = new char[]{'L','L','L','R','R','R'};
assertArrayEquals("..LR..", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','L','.','.','R','.'};
expected = new char[]{'L','L','.','.','R','R'};
assertArrayEquals(".L..R.", expected, ArrayUtils.fallingDominoes(dominoes));
}
@Test
public void sample() {
dominoes = new char[]{'.','.','R','.','.','.','L','.','.','R','.'};
expected = new char[]{'.','.','R','R','.','L','L','.','.','R','R'};
assertArrayEquals("..R...L..R.", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'.','L','.','R','.','.','.','L','R','.','.','L','.','.'};
expected = new char[]{'L','L','.','R','R','.','L','L','R','R','L','L','.','.'};
assertArrayEquals(".L.R...LR..L..", expected, ArrayUtils.fallingDominoes(dominoes));
dominoes = new char[]{'R','R','.','L'};
expected = dominoes;
assertArrayEquals("RR.L", expected, ArrayUtils.fallingDominoes(dominoes));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertTrue;
public class FindAllDuplicatesJTests {
int[] numbers;
Set<Integer> expected, out;
@Test
public void nullEmpty() {
numbers = null;
out = ArrayUtils.findAllDuplicates(numbers);
assertTrue("null", out.isEmpty());
numbers = new int[]{};
out = ArrayUtils.findAllDuplicates(numbers);
assertTrue("empty", out.isEmpty());
}
@Test
public void oneElement() {
numbers = new int[]{1};
out = ArrayUtils.findAllDuplicates(numbers);
assertTrue("one element", out.isEmpty());
}
@Test
public void allDuplicates() {
numbers = new int[]{1,1,1};
expected = new HashSet<>();
expected.add(1);
out = ArrayUtils.findAllDuplicates(numbers);
assertTrue("all duplicate element", out.size() == 1);
assertTrue("elements are expected", out.containsAll(expected));
}
@Test
public void noDuplicates() {
numbers = new int[]{1,2,3};
expected = new HashSet<>();
out = ArrayUtils.findAllDuplicates(numbers);
assertTrue("no duplicate element", out.isEmpty());
}
@Test
public void sample() {
numbers = new int[]{3,2,3,1,1};
expected = new HashSet<>();
expected.add(3);
expected.add(1);
out = ArrayUtils.findAllDuplicates(numbers);
assertTrue("3,2,3,1,1", out.size() == 2);
assertTrue("all elements expected", out.containsAll(expected));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class FindDuplicatesInArraysBinarySearchJTests {
Integer[] a, b;
int[] expected;
@Test
public void nullEmpty() {
a = null;
b = null;
expected = new int[]{};
assertArrayEquals("null", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
a = new Integer[]{};
b = new Integer[]{};
expected = new int[]{};
assertArrayEquals("null", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
}
@Test
public void oneElement() {
a = new Integer[]{1};
b = new Integer[]{1};
expected = new int[]{1};
assertArrayEquals("one element duplicate", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
a = new Integer[]{1};
b = new Integer[]{2};
expected = new int[]{};
assertArrayEquals("one element not duplicate", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
}
@Test
public void twoElement() {
a = new Integer[]{1,2};
b = new Integer[]{1,2};
expected = new int[]{1,2};
assertArrayEquals("two element duplicate", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
a = new Integer[]{1,2};
b = new Integer[]{3,4};
expected = new int[]{};
assertArrayEquals("two element not duplicate", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
}
@Test
public void sample() {
a = new Integer[]{1,2,3,5,6,7};
b = new Integer[]{3,6,7,8,20};
expected = new int[]{3,6,7};
assertArrayEquals("1,2,3,5,6,7 - 3,6,7,8,20", expected, ArrayUtils.findDuplicatesInArraysBinarySearch(a, b));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class FindFirstMissingPositiveIntegerWithZerosAndDuplicatesJTests {
int[] numbers;
@Test
public void nullEmpty() {
numbers = null;
assertEquals("null input", 1, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{};
assertEquals("empty input", 1, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
}
@Test
public void oneElement() {
numbers = new int[]{0};
assertEquals("0 input", 1, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{-1};
assertEquals("negative input", 1, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{1};
assertEquals("1 input", 2, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
}
@Test
public void allPositive() {
numbers = new int[]{1, 2, 3};
assertEquals("all positive input", 4, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
}
@Test
public void allNegative() {
numbers = new int[]{-1, -2, -3};
assertEquals("all negative input", 1, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
}
@Test
public void sample() {
numbers = new int[]{-1, 2, -3};
assertEquals("-1, 2, 3 input", 1, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{1, -1, 2, -3, 4, -5};
assertEquals("1, -1, 2, -3, 4, -5 input", 3, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{3, 4, -1, 1};
assertEquals("3, 4, -1, 1 input", 2, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{1, 2, 0};
assertEquals("1, 2, 0 input", 3, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
numbers = new int[]{1, -1, -5, -3, 3, 4, 2, 8};
assertEquals("1, -1, -5, -3, 3, 4, 2, 8 input", 5, ArrayUtils.findFirstMissingPositiveIntegerWithZerosAndDuplicates(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class FindMaximumForEachSubarrayOfLengthKJTests {
int[] numbers;
int k;
List<Integer> expected, out;
@Test
public void nullEmpty() {
numbers = null;
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
assertTrue("null", out.isEmpty());
numbers = new int[]{};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
assertTrue("empty", out.isEmpty());
}
@Test
public void kTooBig() {
numbers = new int[]{1};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
assertTrue("k too big", out.isEmpty());
}
@Test
public void oneElement() {
numbers = new int[]{1};
k = 1;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
assertEquals("one element", 1, out.size());
assertEquals("element is correct", 1, (int)out.get(0));
}
@Test
public void allSameElement() {
numbers = new int[]{1,1,1,1,1,1};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
assertEquals("all same element element", 4, out.size());
for(Integer i : out) {
assertEquals("element is correct", 1, (int)i);
}
}
@Test
public void decreasing() {
numbers = new int[]{5,4,3,2,1};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
expected = new ArrayList<>();
expected.add(5);
expected.add(4);
expected.add(3);
assertEquals("decreasing element", expected.size(), out.size());
for(int i = 0; i < out.size(); i++) {
assertEquals("element is correct", expected.get(i), out.get(i));
}
}
@Test
public void increasing() {
numbers = new int[]{1,2,3,4,5};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
expected = new ArrayList<>();
expected.add(3);
expected.add(4);
expected.add(5);
assertEquals("increasing element", expected.size(), out.size());
for(int i = 0; i < out.size(); i++) {
assertEquals("element is correct", expected.get(i), out.get(i));
}
}
@Test
public void sample() {
numbers = new int[]{10, 5, 2, 7, 8, 7};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
expected = new ArrayList<>();
expected.add(10);
expected.add(7);
expected.add(8);
expected.add(8);
assertEquals("10, 5, 2, 7, 8, 7", expected.size(), out.size());
for(int i = 0; i < out.size(); i++) {
assertEquals("element is correct", expected.get(i), out.get(i));
}
numbers = new int[]{10, 5, 2, 7, 8, 7, 11, 11, 0, 1, 2};
k = 3;
out = ArrayUtils.findMaximumForEachSubarrayOfLengthK(k, numbers);
expected = new ArrayList<>();
expected.add(10);
expected.add(7);
expected.add(8);
expected.add(8);
expected.add(11);
expected.add(11);
expected.add(11);
expected.add(11);
expected.add(2);
assertEquals("10, 5, 2, 7, 8, 7, 11, 11, 0, 1, 2", expected.size(), out.size());
for(int i = 0; i < out.size(); i++) {
assertEquals("element is correct", expected.get(i), out.get(i));
}
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class FindRepeatJTests {
int[] in;
int out;
@Test
public void badInput() {
in = null;
try{
out = ArrayUtils.findRepeat(in);
}catch(IllegalArgumentException e){
System.out.println("null array got IllegalArgumentException: " + e.getMessage());
}
in = new int[]{1};
try{
out = ArrayUtils.findRepeat(in);
}catch(IllegalArgumentException e){
System.out.println("single element array got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void allEqualsArray() {
in = new int[]{1,1,1,1,1,1};
out = 1;
assertEquals("all equals 1 expected 1", out, ArrayUtils.findRepeat(in));
}
@Test
public void repeatedFirstAndLastArray() {
in = new int[]{1,2,1};
out = 1;
assertEquals("repeated first and last array N = 2 expected 1", out, ArrayUtils.findRepeat(in));
in = new int[]{1,1,2};
out = 1;
assertEquals("repeated second and second to last array N = 2 expected 1", out, ArrayUtils.findRepeat(in));
}
@Test
public void incrementingArray() {
in = new int[]{1,2,3,4,5,1};
out = 1;
assertEquals("incrementing array N = 5 repeated last expected 1", out, ArrayUtils.findRepeat(in));
in = new int[]{1,1,2,3,4,5};
out = 1;
assertEquals("incrementing array N = 5 repeated first expected 1", out, ArrayUtils.findRepeat(in));
in = new int[]{1,2,3,1,4,5};
out = 1;
assertEquals("incrementing array N = 5 repeated middle expected 1", out, ArrayUtils.findRepeat(in));
}
@Test
public void decrementingArray() {
in = new int[]{5,4,3,2,1,1};
out = 1;
assertEquals("decrementing array N = 5 repeated last expected 1", out, ArrayUtils.findRepeat(in));
in = new int[]{1,5,4,3,2,1};
out = 1;
assertEquals("decrementing array N = 5 repeated first expected 1", out, ArrayUtils.findRepeat(in));
in = new int[]{1,5,1,4,3,2};
out = 1;
assertEquals("decrementing array N = 5 repeated middle expected 1", out, ArrayUtils.findRepeat(in));
}
@Test
public void randomArray() {
in = new int[]{2,5,1,1,4,3};
out = 1;
assertEquals("random array N = 5 expected 1", out, ArrayUtils.findRepeat(in));
}
/*
This test is VERY subjective to the specific algorithm being used
In case of multiple repeated elements, NOT all algorithm will return the same element
*/
@Test
public void multipleRepeatsArray() {
in = new int[]{2,1,2,2,2,1,2};
out = 2;
assertEquals("multiple repeats start 2 array N = 6 expected 2", out, ArrayUtils.findRepeat(in));
in = new int[]{2,1,2,2,2,2,1};
out = 1;
assertEquals("multiple repeats start 1 array N = 6 expected 1", out, ArrayUtils.findRepeat(in));
in = new int[]{2,1,2,1,2,1,2};
out = 2;
assertEquals("multiple repeats 2,1 end 2 array N = 6 expected 2", out, ArrayUtils.findRepeat(in));
in = new int[]{1,2,1,2,1,2,1};
out = 1;
assertEquals("multiple repeats 1,2 end 1 array N = 6 expected 1", out, ArrayUtils.findRepeat(in));
}
@Test
public void badInputBrent() {
in = null;
try{
out = ArrayUtils.findRepeatBrent(in);
}catch(IllegalArgumentException e){
System.out.println("null array Brent got IllegalArgumentException: " + e.getMessage());
}
in = new int[]{1};
try{
out = ArrayUtils.findRepeatBrent(in);
}catch(IllegalArgumentException e){
System.out.println("single element array Brent got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void allEqualsArrayBrent() {
in = new int[]{1,1,1,1,1,1};
out = 1;
assertEquals("all equals 1 Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
}
@Test
public void repeatedFirstAndLastArrayBrent() {
in = new int[]{1,2,1};
out = 1;
assertEquals("repeated first and last array N = 2 Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{1,1,2};
out = 1;
assertEquals("repeated second and second to last array N = 2 Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
}
@Test
public void incrementingArrayBrent() {
in = new int[]{1,2,3,4,5,1};
out = 1;
assertEquals("incrementing array N = 5 repeated last Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{1,1,2,3,4,5};
out = 1;
assertEquals("incrementing array N = 5 repeated first Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{1,2,3,1,4,5};
out = 1;
assertEquals("incrementing array N = 5 repeated middle Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
}
@Test
public void decrementingArrayBrent() {
in = new int[]{5,4,3,2,1,1};
out = 1;
assertEquals("decrementing array N = 5 repeated last Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{1,5,4,3,2,1};
out = 1;
assertEquals("decrementing array N = 5 repeated first Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{1,5,1,4,3,2};
out = 1;
assertEquals("decrementing array N = 5 repeated middle Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
}
@Test
public void randomArrayBrent() {
in = new int[]{2,5,1,1,4,3};
out = 1;
assertEquals("random array N = 5 Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
}
/*
This test is VERY subjective to the specific algorithm being used
In case of multiple repeated elements, NOT all algorithm will return the same element
*/
@Test
public void multipleRepeatsArrayBrent() {
in = new int[]{2,1,2,2,2,1,2};
out = 2;
assertEquals("multiple repeats start 2 array N = 6 Brent expected 2", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{2,1,2,2,2,2,1};
out = 1;
assertEquals("multiple repeats start 1 array N = 6 Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{2,1,2,1,2,1,2};
out = 2;
assertEquals("multiple repeats 2,1 end 2 array N = 6 Brent expected 2", out, ArrayUtils.findRepeatBrent(in));
in = new int[]{1,2,1,2,1,2,1};
out = 1;
assertEquals("multiple repeats 1,2 end 1 array N = 6 Brent expected 1", out, ArrayUtils.findRepeatBrent(in));
}
@Test
public void compareFloydAndBrent() {
long start, end, durationFloyd, durationBrent;
//no need to test the bad input case since it is the same for both
in = new int[]{1,1,1,1,1,1};
//Floyd
start = System.nanoTime();
out = ArrayUtils.findRepeat(in);
end = System.nanoTime();
durationFloyd = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("all equals 1 comparison Floyd = " + durationFloyd + " - Brent = " + durationBrent);
in = new int[]{1,2,1};
//Floyd
start = System.nanoTime();
out = ArrayUtils.findRepeat(in);
end = System.nanoTime();
durationFloyd = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("repeated first and last array N = 2 comparison Floyd = " + durationFloyd + " - Brent = " + durationBrent);
in = new int[]{1,2,3,4,5,1};
//Floyd
start = System.nanoTime();
out = ArrayUtils.findRepeat(in);
end = System.nanoTime();
durationFloyd = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("incrementing array N = 5 comparison Floyd = " + durationFloyd + " - Brent = " + durationBrent);
in = new int[]{5,4,3,2,1,1};
//Floyd
start = System.nanoTime();
out = ArrayUtils.findRepeat(in);
end = System.nanoTime();
durationFloyd = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("decrementing array N = 5 comparison Floyd = " + durationFloyd + " - Brent = " + durationBrent);
in = new int[]{2,5,1,1,4,3};
//Floyd
start = System.nanoTime();
out = ArrayUtils.findRepeat(in);
end = System.nanoTime();
durationFloyd = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("random array N = 5 comparison Floyd = " + durationFloyd + " - Brent = " + durationBrent);
in = new int[]{2,1,2,2,2,1,2};
//Floyd
start = System.nanoTime();
out = ArrayUtils.findRepeat(in);
end = System.nanoTime();
durationFloyd = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("multiple repeats start 2 array N = 6 comparison Floyd = " + durationFloyd + " - Brent = " + durationBrent);
}
@Test
public void badInputBinarySearch() {
in = null;
try{
out = ArrayUtils.findRepeatBinarySearch(in);
}catch(IllegalArgumentException e){
System.out.println("null array BinarySearch got IllegalArgumentException: " + e.getMessage());
}
in = new int[]{1};
try{
out = ArrayUtils.findRepeatBinarySearch(in);
}catch(IllegalArgumentException e){
System.out.println("single element array BinarySearch got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void allEqualsArrayBinarySearch() {
in = new int[]{1,1,1,1,1,1};
out = 1;
assertEquals("all equals 1 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
}
@Test
public void repeatedFirstAndLastArrayBinarySearch() {
in = new int[]{1,2,1};
out = 1;
assertEquals("repeated first and last array N = 2 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{1,1,2};
out = 1;
assertEquals("repeated second and second to last array N = 2 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
}
@Test
public void incrementingArrayBinarySearch() {
in = new int[]{1,2,3,4,5,1};
out = 1;
assertEquals("incrementing array N = 5 repeated last BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{1,1,2,3,4,5};
out = 1;
assertEquals("incrementing array N = 5 repeated first BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{1,2,3,1,4,5};
out = 1;
assertEquals("incrementing array N = 5 repeated middle BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
}
@Test
public void decrementingArrayBinarySearch() {
in = new int[]{5,4,3,2,1,1};
out = 1;
assertEquals("decrementing array N = 5 repeated last BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{1,5,4,3,2,1};
out = 1;
assertEquals("decrementing array N = 5 repeated first BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{1,5,1,4,3,2};
out = 1;
assertEquals("decrementing array N = 5 repeated middle BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
}
@Test
public void randomArrayBinarySearch() {
in = new int[]{2,5,1,1,4,3};
out = 1;
assertEquals("random array N = 5 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
}
/*
This test is VERY subjective to the specific algorithm being used
In case of multiple repeated elements, NOT all algorithm will return the same element
*/
@Test
public void multipleRepeatsArrayBinarySearch() {
in = new int[]{2,1,2,2,2,1,2};
out = 1;
assertEquals("multiple repeats start 2 array N = 6 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{2,1,2,2,2,2,1};
out = 1;
assertEquals("multiple repeats start 1 array N = 6 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{2,1,2,1,2,1,2};
out = 1;
assertEquals("multiple repeats 2,1 end 2 array N = 6 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
in = new int[]{1,2,1,2,1,2,1};
out = 1;
assertEquals("multiple repeats 1,2 end 1 array N = 6 BinarySearch expected 1", out, ArrayUtils.findRepeatBinarySearch(in));
}
@Test
public void compareBinarySearchAndBrent() {
long start, end, durationBinarySearch, durationBrent;
//no need to test the bad input case since it is the same for both
in = new int[]{1,1,1,1,1,1};
//BinarySearch
start = System.nanoTime();
out = ArrayUtils.findRepeatBinarySearch(in);
end = System.nanoTime();
durationBinarySearch = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("all equals 1 comparison BinarySearch = " + durationBinarySearch + " - Brent = " + durationBrent);
in = new int[]{1,2,1};
//BinarySearch
start = System.nanoTime();
out = ArrayUtils.findRepeatBinarySearch(in);
end = System.nanoTime();
durationBinarySearch = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("repeated first and last array N = 2 comparison BinarySearch = " + durationBinarySearch + " - Brent = " + durationBrent);
in = new int[]{1,2,3,4,5,1};
//BinarySearch
start = System.nanoTime();
out = ArrayUtils.findRepeatBinarySearch(in);
end = System.nanoTime();
durationBinarySearch = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("incrementing array N = 5 comparison BinarySearch = " + durationBinarySearch + " - Brent = " + durationBrent);
in = new int[]{5,4,3,2,1,1};
//BinarySearch
start = System.nanoTime();
out = ArrayUtils.findRepeatBinarySearch(in);
end = System.nanoTime();
durationBinarySearch = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("decrementing array N = 5 comparison BinarySearch = " + durationBinarySearch + " - Brent = " + durationBrent);
in = new int[]{2,5,1,1,4,3};
//BinarySearch
start = System.nanoTime();
out = ArrayUtils.findRepeatBinarySearch(in);
end = System.nanoTime();
durationBinarySearch = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("random array N = 5 comparison BinarySearch = " + durationBinarySearch + " - Brent = " + durationBrent);
in = new int[]{2,1,2,2,2,1,2};
//BinarySearch
start = System.nanoTime();
out = ArrayUtils.findRepeatBinarySearch(in);
end = System.nanoTime();
durationBinarySearch = end - start;
//Brent
start = System.nanoTime();
out = ArrayUtils.findRepeatBrent(in);
end = System.nanoTime();
durationBrent = end - start;
System.out.println("multiple repeats start 2 array N = 6 comparison BinarySearch = " + durationBinarySearch + " - Brent = " + durationBrent);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class FindSubarrayOfSumZeroJTests {
int[] numbers, out, expected;
@Test
public void nullEmpty() {
numbers = null;
assertEquals("null input", 0, ArrayUtils.findSubarrayOfSumZero(numbers).length);
numbers = new int[]{1};
assertEquals("one element in input", 0, ArrayUtils.findSubarrayOfSumZero(numbers).length);
}
@Test
public void twoElements() {
numbers = new int[]{1, -1};
expected = new int[]{1, -1};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("two elements sum to 0 expected result", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
numbers = new int[]{1, 0};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("two elements NOT sum to 0 expected no result", 0, out.length);
numbers = new int[]{0, 0};
expected = new int[]{0, 0};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("only zeros input expected result", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
}
@Test
public void resultIsWholeArray() {
numbers = new int[]{-2, -1, 0, 3};
expected = new int[]{-2, -1, 0, 3};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("result is whole array", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
numbers = new int[]{2, -1, -1};
expected = new int[]{2, -1, -1};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("result is whole array", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
numbers = new int[]{0, -1, 1};
expected = new int[]{-1, 1};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("result is whole array", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
numbers = new int[]{-1, 1, 0};
expected = new int[]{-1, 1};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("result is whole array", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
}
@Test
public void resultIsSubArray() {
numbers = new int[]{1, -2, 0, 2, -1};
expected = new int[]{-2, 0, 2};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("result is subarray", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
numbers = new int[]{1, -2, 0, 0, 2, -1};
expected = new int[]{0, 0};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("consecutive zeros result is subarray", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
numbers = new int[]{1, 2, -5, 1, 2, -1};
expected = new int[]{2, -5, 1, 2};
out = ArrayUtils.findSubarrayOfSumZero(numbers);
assertEquals("sample subarray", expected.length, out.length);
assertArrayEquals("result is expected", expected, out);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class FisherYatesJTests {
int[] in, original;
@Test
public void noSwap() {
in = null;
original = null;
ArrayUtils.FisherYatesShuffle(in);
assertEquals("null array has no swap", original, in);
in = new int[]{1};
original = new int[]{1};
ArrayUtils.FisherYatesShuffle(in);
assertArrayEquals("single element array has no swap", original, in);
}
@Test
public void allEqualsArray() {
in = new int[]{1,1,1,1,1,1};
original = new int[]{1,1,1,1,1,1};
ArrayUtils.FisherYatesShuffle(in);
assertArrayEquals("all same element array has no noticeable swap", original, in);
}
@Test
public void randomShuffleArray() {
original = new int[]{1,2,3,4,5,6};
int matches = 0, loops = 100;
for(int i = 0; i < loops; i++) {
in = new int[]{1, 2, 3, 4, 5, 6};
ArrayUtils.FisherYatesShuffle(in);
try {
assertArrayEquals("check if array after shuffle matches input", original, in);
matches++;
} catch(AssertionError ae){
//all right, we expect it to NOT always match the input
}
}
assertNotEquals("unless we're unlucky we should NOT always get the same array as the input", matches, loops);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import java.util.LinkedList;
import java.util.List;
import static org.junit.Assert.assertTrue;
public class ForcedSwapsJTests {
int[] start, end;
List<Integer> out;
@Test
public void badInput(){
start = null;
end = null;
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("both null input got IllegalArgumentException: " + e.getMessage());
}
start = new int[]{0};
end = null;
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("end null input got IllegalArgumentException: " + e.getMessage());
}
start = null;
end = new int[]{0};
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("start null input got IllegalArgumentException: " + e.getMessage());
}
start = new int[]{1, 0};
end = new int[]{0};
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("start longer input got IllegalArgumentException: " + e.getMessage());
}
start = new int[]{0};
end = new int[]{1, 0};
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("end longer input got IllegalArgumentException: " + e.getMessage());
}
start = new int[]{0, 1};
end = new int[]{0, 2};
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("not matching input got IllegalArgumentException: " + e.getMessage());
}
start = new int[]{0, 0};
end = new int[]{1, 0};
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("duplicate input got IllegalArgumentException: " + e.getMessage());
}
start = new int[]{0, 1};
end = new int[]{1, 2};
try{
out = ArrayUtils.forcedSwaps(start, end);
} catch(IllegalArgumentException e){
System.out.println("missing 0 input got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void base(){
start = new int[]{0};
end = new int[]{0};
out = new LinkedList<>();
assertTrue("single element", out.equals(ArrayUtils.forcedSwaps(start, end)));
start = new int[]{0, 1};
end = new int[]{0, 1};
out = new LinkedList<>();
assertTrue("no swap", out.equals(ArrayUtils.forcedSwaps(start, end)));
start = new int[]{1, 0};
end = new int[]{0, 1};
out = new LinkedList<>();
out.add(0);
assertTrue("single swap start", out.equals(ArrayUtils.forcedSwaps(start, end)));
start = new int[]{0, 1};
end = new int[]{1, 0};
out = new LinkedList<>();
out.add(1);
assertTrue("single swap end", out.equals(ArrayUtils.forcedSwaps(start, end)));
start = new int[]{0, 1, 2};
end = new int[]{0, 2, 1};
out = new LinkedList<>();
out.add(1);
out.add(2);
out.add(0);
assertTrue("need 0 in start to swap", out.equals(ArrayUtils.forcedSwaps(start, end)));
start = new int[]{1, 2, 0};
end = new int[]{2, 1, 0};
out = new LinkedList<>();
out.add(0);
out.add(1);
out.add(2);
assertTrue("need 0 in end to swap", out.equals(ArrayUtils.forcedSwaps(start, end)));
}
@Test
public void random(){
start = new int[]{1, 2, 3, 0};
end = new int[]{3, 1, 2, 0};
out = new LinkedList<>();
out.add(0);
out.add(2);
out.add(1);
out.add(3);
assertTrue("1, 2, 3, 0 to 3, 1, 2, 0", out.equals(ArrayUtils.forcedSwaps(start, end)));
start = new int[]{1, 2, 3, 0};
end = new int[]{3, 1, 0, 2};
out = new LinkedList<>();
out.add(0);
out.add(2);
out.add(1);
out.add(3);
out.add(2);
assertTrue("1, 2, 3, 0 to 3, 1, 0, 2", out.equals(ArrayUtils.forcedSwaps(start, end)));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class FourSumJTests {
int[] numbers;
int target;
@Test
public void nullEmpty() {
numbers = null;
target = 0;
assertTrue("null", ArrayUtils.fourSum(numbers, target).isEmpty());
numbers = new int[]{};
target = 0;
assertTrue("empty", ArrayUtils.fourSum(numbers, target).isEmpty());
numbers = new int[]{1,2,3};
target = 0;
assertTrue("three elements only", ArrayUtils.fourSum(numbers, target).isEmpty());
}
@Test
public void allSame() {
numbers = new int[]{1,1,1,1,1};
target = 4;
//expected 1,1,1,1
assertEquals("only one solution", 1, ArrayUtils.fourSum(numbers, target).size());
numbers = new int[]{1,1,1,1,1};
target = 5;
//expected 1,1,1,1
assertEquals("no solution", 0, ArrayUtils.fourSum(numbers, target).size());
}
@Test
public void sample() {
numbers = new int[]{1,0,-1,0,-2,2};
target = 0;
//expected [-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]
assertEquals("three solutions", 3, ArrayUtils.fourSum(numbers, target).size());
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class GoldPotsJTests {
int[] pots;
int out;
boolean player;
@Test
public void badInput() {
pots = null;
player = true;
try{
out = ArrayUtils.getMaxGoldFromPots(pots, player);
}catch(IllegalArgumentException e){
System.out.println("null pots got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void singleElementArray() {
pots = new int[]{1};
player = true;
out = 1;
assertEquals("single element array starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{1};
player = false;
out = 0;
assertEquals("single element array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
}
@Test
public void increasingEvenArray() {
pots = new int[]{1,2,3,4,5,6};
player = true;
out = 12;
assertEquals("increasing even array starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{1,2,3,4,5,6};
player = false;
out = 9;
assertEquals("increasing even array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
}
@Test
public void decreasingEvenArray() {
pots = new int[]{6,5,4,3,2,1};
player = true;
out = 12;
assertEquals("decreasing even array starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{6,5,4,3,2,1};
player = false;
out = 9;
assertEquals("decreasing even array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
}
@Test
public void increasingOddArray() {
pots = new int[]{1,2,3,4,5};
player = true;
out = 9;
assertEquals("increasing odd array starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{1,2,3,4,5};
player = false;
out = 6;
assertEquals("increasing odd array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
}
@Test
public void decreasingOddArray() {
pots = new int[]{5,4,3,2,1};
player = true;
out = 9;
assertEquals("decreasing odd array starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{5,4,3,2,1};
player = false;
out = 6;
assertEquals("decreasing odd array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
}
@Test
public void randomArray() {
pots = new int[]{10,100,1,50,1000,3};
player = true;
out = 1011;
assertEquals("random array starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{10,100,1,50,1000,3};
player = false;
out = 153;
assertEquals("random array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
pots = new int[]{10,100,1,50,1000,3,1,5,100,23};
player = false;
out = 181;
assertEquals("random array not starting player", out, ArrayUtils.getMaxGoldFromPots(pots, player));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class HowManySubsequencesSumUpToTargetDPJTests {
int target;
int[] numbers;
@Test
public void invalidInput() {
target = -1;
numbers = new int[]{1, 5, 10, 25};
assertEquals("target -1", -1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
target = 1;
numbers = new int[]{};
assertEquals("no numbers", -1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
target = 1;
numbers = null;
assertEquals("null numbers", -1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
}
@Test
public void impossibleTarget() {
target = 1;
numbers = new int[]{2};
assertEquals("impossible target", -1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
}
@Test
public void onlyOneNumber() {
target = 1;
numbers = new int[]{1};
assertEquals("only one number equal to target", 1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
target = 5;
numbers = new int[]{1};
assertEquals("only one number not equal to target", -1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
}
@Test
public void twoNumbers() {
target = 2;
numbers = new int[]{1, 5};
assertEquals("two numbers cannot add to target", -1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
target = 6;
numbers = new int[]{1, 5};
assertEquals("two numbers can add to target", 1, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
}
@Test
public void sampleTargets() {
target = 16;
numbers = new int[]{10,2,6,4};
assertEquals("10,2,6,4 target 16", 2, ArrayUtils.howManySubsequencesSumUpToTargetDP(target, numbers));
}
}
package com.blogspot.groglogs.structures;
/**
* A wrapper on integers, useful for tracking and updating int values during recursion.
*/
public class IntWrapper {
private int val;
public IntWrapper(int start){
this.val = start;
}
public void addVal(int val){
this.val += val;
}
public int getVal(){
return this.val;
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class IslandLakesCapacityJTests {
int[] in;
int out;
@Test
public void badInput() {
in = null;
try{
out = ArrayUtils.getIslandLakesCapacity(in);
}catch(IllegalArgumentException e){
System.out.println("null array got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void allEqualsArray() {
in = new int[]{1,1,1,1,1,1};
out = 0;
assertEquals("all equals 1 expected 0", out, ArrayUtils.getIslandLakesCapacity(in));
in = new int[]{0};
out = 0;
assertEquals("all equals 0 expected 0", out, ArrayUtils.getIslandLakesCapacity(in));
}
@Test
public void allIncreasingArray() {
in = new int[]{1,2,3,4,5,6};
out = 0;
assertEquals("all increasing expected 0", out, ArrayUtils.getIslandLakesCapacity(in));
}
@Test
public void allDecreasingArray() {
in = new int[]{6,5,4,3,2,1};
out = 0;
assertEquals("all decreasing expected 0", out, ArrayUtils.getIslandLakesCapacity(in));
}
@Test
public void mixedArray() {
in = new int[]{1,3,2,4,1,3,1,4,5,2,2,1,4,2,2};
out = 15;
assertEquals("mixed expected 15", out, ArrayUtils.getIslandLakesCapacity(in));
in = new int[]{0,1,2,3,4,5,4,3,2,1,0};
out = 0;
assertEquals("pyramid expected 0", out, ArrayUtils.getIslandLakesCapacity(in));
in = new int[]{0,2,0,2,0,3,0,2,0,2,0};
out = 8;
assertEquals("even basins expected 8", out, ArrayUtils.getIslandLakesCapacity(in));
in = new int[]{0,2,0,2,0,2,0,2,0,2,0};
out = 8;
assertEquals("even peaks expected 8", out, ArrayUtils.getIslandLakesCapacity(in));
in = new int[]{0,2,2,2,0};
out = 0;
assertEquals("plateau expected 0", out, ArrayUtils.getIslandLakesCapacity(in));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import com.blogspot.groglogs.structures.Range;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class LargestSumSubarrayJTests {
int[] input;
Range res;
@Test
public void nullEmpty() {
input = null;
try{
res = ArrayUtils.getLargestSumSubarray(input);
}catch (IllegalArgumentException e){
System.out.println("null input got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void allNegative() {
input = new int[]{-1};
res = new Range(0, 0, -1);
assertEquals("all negative -1 expected start 0, end 0, tot -1", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{-1, -2, -3};
res = new Range(0, 0, -1);
assertEquals("all negative -1,-2,-3 expected start 0, end 0, tot -1", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{-3, -2, -1};
res = new Range(2, 2, -1);
assertEquals("all negative -3,-2,-1 expected start 2, end 2, tot -1", res, ArrayUtils.getLargestSumSubarray(input));
}
@Test
public void allPositive() {
input = new int[]{1};
res = new Range(0, 0, 1);
assertEquals("all positive 1 expected start 0, end 0, tot 1", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{1, 2, 3};
res = new Range(0, 2, 6);
assertEquals("all positive 1,2,3 expected start 0, end 2, tot 6", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{3, 2, 1};
res = new Range(0, 2, 6);
assertEquals("all positive 3,2,1 expected start 0, end 2, tot 6", res, ArrayUtils.getLargestSumSubarray(input));
}
@Test
public void mixed() {
input = new int[]{-3, -2, -1, 0, 1, 2, 3};
res = new Range(4, 6, 6);
assertEquals("increasing -3,-2,-1,0,1,2,3 expected start 4, end 6, tot 6", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{3, 2, 1, 0, -1, -2, -3};
res = new Range(0, 2, 6);
assertEquals("decreasing 3,2,1,0,-1,-2,-3 expected start 0, end 2, tot 6", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{-2, 1, -3, 4, -1, 2, 1, -5, 4};
res = new Range(3, 6, 6);
assertEquals("mixed -2,1,-3,4,-1,2,1,-5,4 expected start 3, end 6, tot 6", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{-3, -2, -1, 0, 1, 2, 3, -3, -2, -1, 0, 1, 2, 3};
res = new Range(4, 6, 6);
assertEquals("same rollercoaster -3,-2,-1,0,1,2,3,-3,-2,-1,0,1,2,3 expected start 4, end 6, tot 6", res, ArrayUtils.getLargestSumSubarray(input));
input = new int[]{-3, -2, -1, 0, 1, 2, 3, -3, -2, -1, 0, 1, 2, 3, 4};
res = new Range(11, 14, 10);
assertEquals("increasing rollercoaster -3,-2,-1,0,1,2,3,-3,-2,-1,0,1,2,3,4 expected start 11, end 14, tot 10", res, ArrayUtils.getLargestSumSubarray(input));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class LongestIncreasingFailedBuildSequence {
List<Boolean[]> in;
int out;
@Test
public void emptyInput() {
in = null;
out = 0;
assertEquals("empty input expected 0", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
in = new ArrayList<>();
out = 0;
assertEquals("empty input expected 0", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void singleElement() {
in = new ArrayList<>();
Boolean[] run = {true, false};
in.add(run);
out = 0;
assertEquals("single element expected 0", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void allSameElement() {
in = new ArrayList<>();
Boolean[] run = {true, false};
in.add(run);
in.add(run);
in.add(run);
in.add(run);
in.add(run);
out = 0;
assertEquals("all same element expected 0", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void allDecreasing() {
in = new ArrayList<>();
Boolean[] run = {true, false};
in.add(run);
Boolean[] run1 = {true, false, false};
in.add(run1);
Boolean[] run2 = {true, false, false, false};
in.add(run2);
Boolean[] run3 = {true, false, false, false, false};
in.add(run3);
Boolean[] run4 = {true, false, false, false, false, false};
in.add(run4);
out = in.size() - 1;
assertEquals("all decreasing expected in.size() - 1", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void allIncreasing() {
in = new ArrayList<>();
Boolean[] run = {true, false, false, false, false, false};
in.add(run);
Boolean[] run1 = {true, false, false, false, false};
in.add(run1);
Boolean[] run2 = {true, false, false, false};
in.add(run2);
Boolean[] run3 = {true, false, false};
in.add(run3);
Boolean[] run4 = {true, false};
in.add(run4);
out = 0;
assertEquals("all increasing expected 0", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void triangle() {
in = new ArrayList<>();
Boolean[] run = {true, false};
in.add(run);
Boolean[] run1 = {true, false, false};
in.add(run1);
Boolean[] run2 = {true, false, false, false};
in.add(run2);
Boolean[] run3 = {true, false, false};
in.add(run3);
Boolean[] run4 = {true, false};
in.add(run4);
out = 2;
assertEquals("triangle expected 2", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void doubleTriangle() {
in = new ArrayList<>();
Boolean[] run = {true, false};
in.add(run);
Boolean[] run1 = {true, false, false};
in.add(run1);
Boolean[] run2 = {true, false, false, false};
in.add(run2);
Boolean[] run3 = {true, false, false};
in.add(run3);
Boolean[] run4 = {true, false};
in.add(run4);
Boolean[] run5 = {true, false, false};
in.add(run5);
Boolean[] run6 = {true, false, false, false};
in.add(run6);
Boolean[] run7 = {true, false, false};
in.add(run7);
Boolean[] run8 = {true, false};
in.add(run8);
out = 2;
assertEquals("double triangle expected 2", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
@Test
public void sampleSequence() {
in = new ArrayList<>();
Boolean[] run = {true, true, true, false, false};
in.add(run);
Boolean[] run1 = {true, true, true, true, false};
in.add(run1);
Boolean[] run2 = {true, true, true, true, true, true, false, false, false};
in.add(run2);
Boolean[] run3 = {true, false, false, false, false, false};
in.add(run3);
Boolean[] run4 = {true, true, true, true, true, true, true, true, true, true, true, true, false};
in.add(run4);
Boolean[] run5 = {true, false};
in.add(run5);
Boolean[] run6 = {true, true, true, true, false, false};
in.add(run6);
out = 2;
assertEquals("sample sequence expected 2", out, ArrayUtils.longestIncreasingFailedBuildSequence(in));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class LongestSequenceOfConsecutiveNumbersJTests {
int[] numbers;
int expected;
@Test
public void nullEmpty() {
numbers = null;
expected = 0;
assertEquals("null", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
numbers = new int[]{};
expected = 0;
assertEquals("empty", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
}
@Test
public void oneElement() {
numbers = new int[]{1};
expected = 1;
assertEquals("one element", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
}
@Test
public void twoElement() {
numbers = new int[]{1,2};
expected = 2;
assertEquals("two consecutive elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
numbers = new int[]{1,3};
expected = 1;
assertEquals("two non consecutive elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
numbers = new int[]{1,1};
expected = 1;
assertEquals("duplicate elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
}
@Test
public void allElements() {
numbers = new int[]{1,6,5,3,4,2};
expected = 6;
assertEquals("random all up to 6 elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
numbers = new int[]{1,2,3,4,5,6};
expected = 6;
assertEquals("increasing 6 elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
numbers = new int[]{6,5,4,3,2,1};
expected = 6;
assertEquals("decreasing 6 elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
}
@Test
public void sample() {
numbers = new int[]{1,6,5,4,2};
expected = 3;
assertEquals("1,6,5,4,2 elements", expected, ArrayUtils.longestSequenceOfConsecutiveNumbers(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MaxProdSubarrayJTests {
int[] numbers;
int expected;
@Test
public void nullEmpty() {
numbers = null;
expected = 0;
assertEquals("null", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{};
expected = 0;
assertEquals("empty", expected, ArrayUtils.maxProdSubarray(numbers));
}
@Test
public void oneElement() {
numbers = new int[]{-1};
expected = -1;
assertEquals("-1", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{0};
expected = 0;
assertEquals("0", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{1};
expected = 1;
assertEquals("1", expected, ArrayUtils.maxProdSubarray(numbers));
}
@Test
public void zero() {
numbers = new int[]{1,0,3};
expected = 3;
assertEquals("1,0,3", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{3,0,1};
expected = 3;
assertEquals("3,0,1", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{-3,0,-1};
expected = 0;
assertEquals("-3,0,-1", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{0,2};
expected = 2;
assertEquals("0,2", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{2,0};
expected = 2;
assertEquals("2,0", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{0,-2};
expected = 0;
assertEquals("0,-2", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{-2,0};
expected = 0;
assertEquals("-2,0", expected, ArrayUtils.maxProdSubarray(numbers));
}
@Test
public void allPositive() {
numbers = new int[]{1,2,3};
expected = 6;
assertEquals("1,2,3", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{3,2,1};
expected = 6;
assertEquals("3,2,1", expected, ArrayUtils.maxProdSubarray(numbers));
}
@Test
public void allNegative() {
numbers = new int[]{-1,-2,-3};
expected = 6;
assertEquals("-1,-2,-3", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{-3,-2,-1};
expected = 6;
assertEquals("-3,-2,-1", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{-5,-2,-3};
expected = 10;
assertEquals("-5,-2,-3", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{-3,-2,-5};
expected = 10;
assertEquals("-3,-2,-5", expected, ArrayUtils.maxProdSubarray(numbers));
}
@Test
public void sample() {
numbers = new int[]{2,3,-2,4};
expected = 6;
assertEquals("2,3,-2,4", expected, ArrayUtils.maxProdSubarray(numbers));
numbers = new int[]{2,-5,-2,-4,3};
expected = 24;
assertEquals("2,-5,-2,-4,3", expected, ArrayUtils.maxProdSubarray(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class MaxSumNonAdjacentElementsJTests {
int[] numbers;
@Test
public void nullEmpty() {
numbers = null;
assertNull("null input", ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{};
assertNull("empty input", ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{1, 2};
assertNull("too few elements input", ArrayUtils.maxSumNonAdjacentElements(numbers));
}
@Test
public void allPositive() {
numbers = new int[]{1, 2, 3};
assertEquals("all positive increasing input", 4, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{3, 2, 1};
assertEquals("all positive decreasing input", 4, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{1, 1, 1};
assertEquals("all positive same input", 2, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{5, 0, -1, 5, 0};
assertEquals("best pick ignores negative input", 10, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{5, 0, 0, 5, 0};
assertEquals("best pick ignores zero input", 10, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
}
@Test
public void sample() {
numbers = new int[]{2, 4, 6, 2, 5};
assertEquals("2, 4, 6, 2, 5 input", 13, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
numbers = new int[]{5, 1, 1, 5};
assertEquals("5, 1, 1, 5 input", 10, (int)ArrayUtils.maxSumNonAdjacentElements(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MaxThreeProductJTests {
int[] nums;
int expected;
@Test
public void nullEmpty() {
nums = null;
expected = 0;
assertEquals("null", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{};
expected = 0;
assertEquals("empty", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{1,2};
expected = 0;
assertEquals("2 elements", expected, ArrayUtils.maxThreeProduct(nums));
}
@Test
public void threeElements() {
nums = new int[]{1,2,3};
expected = 6;
assertEquals("1,2,3", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{-1,-2,-3};
expected = -6;
assertEquals("-1,-2,-3", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{1,-2,-3};
expected = 6;
assertEquals("1,-2,-3", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{1,1,1};
expected = 1;
assertEquals("1,1,1", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{-1,1,-1};
expected = 1;
assertEquals("-1,1,-1", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{1,0,1};
expected = 0;
assertEquals("1,0,1", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{-1,0,-1};
expected = 0;
assertEquals("-1,0,-1", expected, ArrayUtils.maxThreeProduct(nums));
}
@Test
public void sample() {
nums = new int[]{-4, -4, 2, 8};
expected = 128;
assertEquals("-4, -4, 2, 8", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{4, -4, 2, -8};
expected = 128;
assertEquals("4, -4, 2, -8", expected, ArrayUtils.maxThreeProduct(nums));
nums = new int[]{4, -4, 2, 8};
expected = 64;
assertEquals("4, -4, 2, 8", expected, ArrayUtils.maxThreeProduct(nums));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class MergeKSortedArraysJTests {
int[][] arrays;
int[] out;
@Test
public void nullEmpty() {
arrays = null;
assertEquals("null input", 0, ArrayUtils.mergeKSortedArrays(arrays).length);
}
@Test(expected = IllegalArgumentException.class)
public void emptyArrayInInputThrowsException() {
arrays = new int[][]{new int[]{1,2}, new int[]{}, new int[]{1}};
ArrayUtils.mergeKSortedArrays(arrays);
}
@Test
public void oneArray() {
arrays = new int[][]{new int[]{1}};
out = new int[]{1};
assertArrayEquals("one array one element", out, ArrayUtils.mergeKSortedArrays(arrays));
arrays = new int[][]{new int[]{1,2,3}};
out = new int[]{1,2,3};
assertArrayEquals("one array more elements", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void moreArraysOneElementAscending() {
arrays = new int[][]{new int[]{1}, new int[]{2}, new int[]{3}};
out = new int[]{1, 2, 3};
assertArrayEquals("more arrays one element ascending", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void moreArraysOneElementDescending() {
arrays = new int[][]{new int[]{3}, new int[]{2}, new int[]{1}};
out = new int[]{1, 2, 3};
assertArrayEquals("more arrays one element descending", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void moreArraysSameLengthAscending() {
arrays = new int[][]{new int[]{1,2}, new int[]{2,3}, new int[]{3,4}};
out = new int[]{1, 2, 2, 3, 3, 4};
assertArrayEquals("more arrays same length ascending", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void moreArraysSameLengthDescending() {
arrays = new int[][]{new int[]{3,4}, new int[]{2,3}, new int[]{1,2}};
out = new int[]{1, 2, 2, 3, 3, 4};
assertArrayEquals("more arrays same length descending", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void moreArraysDifferentLengthAscending() {
arrays = new int[][]{new int[]{1,2}, new int[]{2}, new int[]{3,4,5}};
out = new int[]{1, 2, 2, 3, 4, 5};
assertArrayEquals("more arrays different length ascending", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void moreArraysDifferentLengthDescending() {
arrays = new int[][]{new int[]{3,4,5}, new int[]{2}, new int[]{1,2}};
out = new int[]{1, 2, 2, 3, 4, 5};
assertArrayEquals("more arrays different length descending", out, ArrayUtils.mergeKSortedArrays(arrays));
}
@Test
public void sampleArrays() {
arrays = new int[][]{new int[]{10, 15, 30}, new int[]{12, 15, 20}, new int[]{17, 20, 32}};
out = new int[]{10, 12, 15, 15, 17, 20, 20, 30, 32};
assertArrayEquals("sample array", out, ArrayUtils.mergeKSortedArrays(arrays));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MinimumRotatedSortedArrayJTests {
int[] numbers;
int expected;
@Test(expected = IllegalArgumentException.class)
public void nullArray() {
ArrayUtils.minimumRotatedSortedArray(null);
}
@Test(expected = IllegalArgumentException.class)
public void empty() {
ArrayUtils.minimumRotatedSortedArray(new int[]{});
}
@Test
public void oneElement() {
numbers = new int[]{1};
expected = 1;
assertEquals("1", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
}
@Test
public void twoElement() {
numbers = new int[]{1,2};
expected = 1;
assertEquals("1,2", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
numbers = new int[]{2,1};
expected = 1;
assertEquals("2,1", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
}
@Test
public void threeElement() {
numbers = new int[]{1,2,3};
expected = 1;
assertEquals("1,2,3", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
numbers = new int[]{3,1,2};
expected = 1;
assertEquals("3,1,2", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
numbers = new int[]{2,3,1};
expected = 1;
assertEquals("2,3,1", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
}
@Test
public void sample() {
numbers = new int[]{4,5,6,7,0,1,2};
expected = 0;
assertEquals("4,5,6,7,0,1,2", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
numbers = new int[]{4,5,6,7,0,1};
expected = 0;
assertEquals("4,5,6,7,0,1", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
numbers = new int[]{5,6,7,0,1,2};
expected = 0;
assertEquals("5,6,7,0,1", expected, ArrayUtils.minimumRotatedSortedArray(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MinSubarrayOfSumKJTests {
int[] numbers;
int k;
@Test
public void nullEmpty() {
numbers = null;
k = 3;
assertEquals("null", 0, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{};
k = 3;
assertEquals("empty", 0, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
@Test
public void negativeK() {
numbers = new int[]{1,2};
k = -3;
assertEquals("bad k", 0, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
@Test
public void oneElement() {
numbers = new int[]{1};
k = 1;
assertEquals("1 - 1", 1, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{1};
k = 3;
assertEquals("1 - 3", 0, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
@Test
public void sameElement() {
numbers = new int[]{1,1,1,1};
k = 1;
assertEquals("1,1,1,1 - 1", 1, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{1,1,1,1};
k = 3;
assertEquals("1,1,1,1 - 3", 3, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
@Test
public void increasingElements() {
numbers = new int[]{1,2,3,4,5};
k = 1;
assertEquals("1,2,3,4,5 - 1", 1, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{1,2,3,4,5};
k = 15;
assertEquals("1,2,3,4,5 - 15", 5, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{1,2,3,4,5};
k = 5;
assertEquals("1,2,3,4,5 - 5", 1, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
@Test
public void decreasingElements() {
numbers = new int[]{5,4,3,2,1};
k = 1;
assertEquals("5,4,3,2,1 - 1", 1, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{5,4,3,2,1};
k = 15;
assertEquals("5,4,3,2,1 - 15", 5, ArrayUtils.minSubarrayOfSumK(k, numbers));
numbers = new int[]{5,4,3,2,1};
k = 5;
assertEquals("5,4,3,2,1 - 5", 1, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
@Test
public void sample() {
numbers = new int[]{2,3,1,2,4,3};
k = 7;
assertEquals("2,3,1,2,4,3 - 7", 2, ArrayUtils.minSubarrayOfSumK(k, numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MomentsWhenBulbsAreBlueJTests {
int[] lights;
int expected;
@Test
public void nullEmpty() {
lights = null;
expected = 0;
assertEquals("null input", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
lights = new int[]{};
expected = 0;
assertEquals("empty input", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
}
@Test
public void oneLight() {
lights = new int[]{1};
expected = 1;
assertEquals("one light", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
}
@Test
public void increasingOrder() {
lights = new int[]{1, 2, 3, 4, 5};
expected = 5;
assertEquals("lights on in increasing order", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
}
@Test
public void decreasingOrder() {
lights = new int[]{5, 4, 3, 2, 1};
expected = 1;
assertEquals("lights on in decreasing order", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
}
@Test
public void sample() {
lights = new int[]{2,1,3,5,4};
expected = 3;
assertEquals("2,1,3,5,4", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
lights = new int[]{3,2,4,1,5};
expected = 2;
assertEquals("3,2,4,1,5", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
lights = new int[]{4,1,2,3};
expected = 1;
assertEquals("4,1,2,3", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
lights = new int[]{2,1,4,3,6,5};
expected = 3;
assertEquals("2,1,4,3,6,5", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
lights = new int[]{2,1,4,3,6,5};
expected = 3;
assertEquals("2,1,4,3,6,5", expected, ArrayUtils.momentsWhenBulbsAreBlue(lights));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;
public class MoveZerosJTests {
int[] nums, expected;
@Test
public void nullEmpty() {
nums = null;
expected = null;
ArrayUtils.moveZeros(nums);
assertNull("null", nums);
nums = new int[]{};
expected = new int[]{};
ArrayUtils.moveZeros(nums);
assertArrayEquals("empty", expected, nums);
}
@Test
public void oneElement() {
nums = new int[]{0};
expected = new int[]{0};
ArrayUtils.moveZeros(nums);
assertArrayEquals("0", expected, nums);
nums = new int[]{1};
expected = new int[]{1};
ArrayUtils.moveZeros(nums);
assertArrayEquals("1", expected, nums);
}
@Test
public void twoElement() {
nums = new int[]{0,0};
expected = new int[]{0,0};
ArrayUtils.moveZeros(nums);
assertArrayEquals("0,0", expected, nums);
nums = new int[]{1,2};
expected = new int[]{1,2};
ArrayUtils.moveZeros(nums);
assertArrayEquals("1,2", expected, nums);
nums = new int[]{1,0};
expected = new int[]{1,0};
ArrayUtils.moveZeros(nums);
assertArrayEquals("1,0", expected, nums);
nums = new int[]{0,1};
expected = new int[]{1,0};
ArrayUtils.moveZeros(nums);
assertArrayEquals("0,1", expected, nums);
}
@Test
public void sample() {
nums = new int[]{0,1,0,3,12};
expected = new int[]{1,3,12,0,0};
ArrayUtils.moveZeros(nums);
assertArrayEquals("0,1,0,3,12", expected, nums);
nums = new int[]{0,0,0,1,3,12,0};
expected = new int[]{1,3,12,0,0,0,0};
ArrayUtils.moveZeros(nums);
assertArrayEquals("0,0,0,1,3,12,0", expected, nums);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import com.blogspot.groglogs.structures.Pair;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class NumbersAddUpToKJTests {
int k;
int[] numbers;
@Test
public void nullEmpty() {
k = 10;
numbers = null;
assertNull("null numbers, expected null", ArrayUtils.numbersAddUpToK(k, numbers));
numbers = new int[]{};
assertNull("empty numbers, expected null", ArrayUtils.numbersAddUpToK(k, numbers));
numbers = new int[]{1};
assertNull("only one number, expected null", ArrayUtils.numbersAddUpToK(k, numbers));
}
@Test
public void cannotMakeNumber() {
k = 10;
numbers = new int[]{20, 100, 1};
assertNull("target 10, numbers 20, 100, 1, expected null", ArrayUtils.numbersAddUpToK(k, numbers));
}
@Test
public void repeatedNumbers() {
k = 3;
numbers = new int[]{1, 1, 1};
assertNull("target 1, numbers 1, 1, 1, expected null", ArrayUtils.numbersAddUpToK(k, numbers));
k = 0;
numbers = new int[]{0};
assertNull("target 0, numbers 0, expected null", ArrayUtils.numbersAddUpToK(k, numbers));
k = 0;
numbers = new int[]{0, 0};
assertEquals("target 0, numbers 0,0, expected 0,0", new Pair<>(0,0), ArrayUtils.numbersAddUpToK(k, numbers));
k = 6;
numbers = new int[]{1, 3, 1, 2, 2, 3};
assertEquals("target 6, numbers 1, 3, 1, 2, 2, 3, expected 3,3", new Pair<>(3,3), ArrayUtils.numbersAddUpToK(k, numbers));
}
@Test
public void sampleNumbers() {
k = 10;
numbers = new int[]{1, 5, 7, 3};
assertEquals("target 10, numbers 1, 5, 7, 3, expected 7,3", new Pair<>(7,3), ArrayUtils.numbersAddUpToK(k, numbers));
k = 10;
numbers = new int[]{-10, 20};
assertEquals("target 10, numbers -10,20, expected -10,20", new Pair<>(-10,20), ArrayUtils.numbersAddUpToK(k, numbers));
k = 17;
numbers = new int[]{10, 15, 3, 7};
assertEquals("target 17, numbers 10, 15, 3, 7, expected 10,7", new Pair<>(10,7), ArrayUtils.numbersAddUpToK(k, numbers));
}
}
package com.blogspot.groglogs.structures;
import java.util.Objects;
/**
* Defines a Pair of objects.
* @param <T> type of the first object.
* @param <E> type of the second object.
*/
public class Pair<T, E> {
private T first;
private E second;
public Pair(T first, E second){
this.first = first;
this.second = second;
}
public T getFirst(){
return this.first;
}
public E getSecond(){
return this.second;
}
public void setFirst(T first){
this.first = first;
}
public void setSecond(E second){
this.second = second;
}
@Override
public int hashCode() {
return Objects.hash(this.first, this.second);
}
@Override
public boolean equals(Object o) {
if(this == o){
return true;
}
if(!(o instanceof Pair)){
return false;
}
//the other pair could be typed differently, we accept it since the equals later will factor these types in
Pair<?,?> other = (Pair<?,?>)o;
//Objects.equals logic is: this.first == null ? other.first == null : this.first.equals(other.first)
//which guarantees null safety AND factors in the typechecking by using the relevant equals logic of our field types
return Objects.equals(this.first, other.first) && Objects.equals(this.second, other.second);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import com.blogspot.groglogs.structures.Pair;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class PairsWithDifferenceKJTests {
int[] numbers;
int k;
List<Pair<Integer,Integer>> expected, out;
@Test
public void nullEmpty() {
numbers = null;
k = 1;
assertTrue("null", ArrayUtils.pairsWithDifferenceK(k, numbers).isEmpty());
numbers = new int[]{};
k = 1;
assertTrue("empty", ArrayUtils.pairsWithDifferenceK(k, numbers).isEmpty());
numbers = new int[]{1};
k = 1;
assertTrue("one element", ArrayUtils.pairsWithDifferenceK(k, numbers).isEmpty());
}
@Test
public void noPair() {
numbers = new int[]{10,1,100,6};
k = 1;
assertTrue("no pair", ArrayUtils.pairsWithDifferenceK(k, numbers).isEmpty());
}
@Test
public void sample() {
numbers = new int[]{0, -1, -2, 2, 1};
k = 1;
expected = new ArrayList<>();
expected.add(new Pair<>(1,0));
expected.add(new Pair<>(0,-1));
expected.add(new Pair<>(-1,-2));
expected.add(new Pair<>(2,1));
out = ArrayUtils.pairsWithDifferenceK(k, numbers);
assertEquals("0, -1, -2, 2, 1", expected.size(), out.size());
for(int i = 0; i < expected.size(); i++){
assertEquals("element is correct", expected.get(i), out.get(i));
}
}
}
package com.blogspot.groglogs.test.structures;
import com.blogspot.groglogs.structures.Pair;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class PairTests {
@Test
public void nullEmpty() {
Pair<Integer, Integer> p1 = null;
Pair<Integer, Integer> p2 = null;
assertEquals("null pairs are equal", p1, p2);
p1 = new Pair<>(null, null);
p2 = new Pair<>(null, null);
assertEquals("pairs of nulls are equal", p1, p2);
p1 = null;
Pair<Integer, String> p3 = null;
assertEquals("null pairs of different types are equal", p1, p3);
p1 = new Pair<>(null, null);
p3 = new Pair<>(null, null);
assertEquals("pairs of nulls of different types are equal", p1, p3);
}
@Test
public void oneNotnullOrEmpty() {
Pair<Integer, Integer> p1 = new Pair<>(1, 2);
Pair<Integer, Integer> p2 = null;
assertNotEquals("one pair is not equal to null pair", p1, p2);
Pair<Integer, String> p3 = null;
assertNotEquals("one pair is not equal to null pair of different type", p1, p3);
}
@Test
public void twoPairsOfSameTypes() {
Pair<Integer, Integer> p1 = new Pair<>(1, 2);
Pair<Integer, Integer> p2 = new Pair<>(1, 2);
assertEquals("two pairs of same type with same values are equal", p1, p2);
p2 = new Pair<>(1, 3);
assertNotEquals("two pairs of same type with different values are not equal", p1, p2);
}
@Test
public void twoPairsOfDifferentTypes() {
Pair<Integer, Integer> p1 = new Pair<>(1, 2);
Pair<Integer, String> p2 = new Pair<>(1, "test");
assertNotEquals("two pairs of different types are not equal", p1, p2);
Pair<Integer, Double> p3 = new Pair<>(1, 10.0);
assertNotEquals("two pairs of different compatible types are not equal", p1, p3);
String s = "test";
assertNotEquals("one pair is not equal to a completely different type", p1, s);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class PartitionArrayJTests {
int[] in, out;
int idx;
@Test
public void badInput() {
in = null;
idx = 0;
try{
ArrayUtils.partitionArray(in, idx);
}catch(IllegalArgumentException e){
System.out.println("null array got IllegalArgumentException: " + e.getMessage());
}
in = new int[]{1,2,3};
idx = 4;
try{
ArrayUtils.partitionArray(in, idx);
}catch(IllegalArgumentException e){
System.out.println("Index out of bounds array got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void allEqualsArray() {
in = new int[]{1,1,1,1,1,1,1};
idx = 3;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("all equals array middle index expected same array", in, in);
in = new int[]{1,1,1,1,1,1,1};
idx = 0;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("all equals array first index expected same array", in, in);
in = new int[]{1,1,1,1,1,1,1};
idx = in.length - 1;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("all equals array last index expected same array", in, in);
}
@Test
public void increasingArray() {
in = new int[]{1,2,3,4,5};
idx = 3;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("increasing array middle index expected same array", in, in);
in = new int[]{1,2,3,4,5};
idx = 0;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("increasing array first expected same array", in, in);
in = new int[]{1,2,3,4,5};
idx = in.length - 1;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("increasing array last index expected same array", in, in);
}
@Test
public void decreasingArray() {
in = new int[]{5,4,3,2,1};
out = new int[]{1,2,3,4,5};
idx = 2;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("decreasing array middle index expected reverse array", in, out);
in = new int[]{5,4,3,2,1};
out = new int[]{4,3,2,1,5};
idx = 0;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("decreasing array first index expected shift array", in, out);
in = new int[]{5,4,3,2,1};
out = new int[]{1,3,2,4,5};
idx = in.length - 1;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("decreasing array last index", in, out);
}
@Test
public void randomArray() {
in = new int[]{3,7,4,6,9,8,1,2,0,5};
out = new int[]{3,0,2,1,4,8,9,6,5,7};
idx = 2;
ArrayUtils.partitionArray(in, idx);
assertArrayEquals("random array", in, out);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class PartitionArrayZerosAndNegativesLastJTests {
int[] numbers;
@Test
public void nullEmpty() {
numbers = null;
assertEquals("null input", -1, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
numbers = new int[]{};
assertEquals("empty input", -1, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
}
@Test
public void oneElement() {
numbers = new int[]{1};
assertEquals("one element positive input", 0, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
numbers = new int[]{-1};
assertEquals("one element negative input", -1, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
numbers = new int[]{0};
assertEquals("one element 0 input", -1, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
}
@Test
public void allPositive() {
numbers = new int[]{1, 2, 3};
assertEquals("all positive input", 2, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
}
@Test
public void allNegative() {
numbers = new int[]{-1, -2, -3};
assertEquals("all negative input", -1, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
}
@Test
public void sample() {
numbers = new int[]{-1, 2, -3};
assertEquals("-1, 2, 3 input", 0, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
numbers = new int[]{1, -1, 2, -3, 4, -5};
assertEquals("1, -1, 2, -3, 4, -5 input", 2, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
numbers = new int[]{3, 4, -1, 1};
assertEquals("3, 4, -1, 1 input", 2, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
numbers = new int[]{1, -1, -5, -3, 3, 4, 2, 8};
assertEquals("1, -1, -5, -3, 3, 4, 2, 8 input", 4, ArrayUtils.partitionArrayZerosAndNegativesLast(numbers));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class PlacePostboxJTests {
int[] input;
int place;
@Test
public void nullEmpty(){
input = null;
try{
place = ArrayUtils.getPostboxPlace(input);
} catch(IllegalArgumentException e){
System.out.println("null input got IllegalArgumentException: " + e.getMessage());
}
}
@Test
public void edge(){
input = new int[]{1};
place = 0;
assertEquals("only 1 place expected that", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{0, 0};
place = 1;
assertEquals("all 0 expected last", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{0, 0, 1};
place = 2;
assertEquals("0,0,1 expected 2", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{0, 0, 1, 0, 0};
place = 2;
assertEquals("0,0,1,0,0 expected 2", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{0, 0, 1, 0};
place = 2;
assertEquals("0,0,1,0 expected 2", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{0, 1, 0, 0};
place = 1;
assertEquals("0,1,0,0 expected 1", place, ArrayUtils.getPostboxPlace(input));
}
@Test
public void increasing(){
input = new int[]{1, 2, 3};
place = 2;
assertEquals("increasing 1,2,3 expected 2", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{1, 2, 3, 4};
place = 2;
assertEquals("increasing 1,2,3,4 expected 2", place, ArrayUtils.getPostboxPlace(input));
}
@Test
public void decreasing(){
input = new int[]{3, 2, 1};
place = 1; //0 and 1 are both good solutions
assertEquals("decreasing 3,2,1 expected 1", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{4, 3, 2, 1};
place = 1;
assertEquals("decreasing 4,3,2,1 expected 1", place, ArrayUtils.getPostboxPlace(input));
}
@Test
public void random(){
input = new int[]{3, 2, 0, 5};
place = 3; //1,2,3 are all good solutions
assertEquals("3,2,0,5 expected 1", place, ArrayUtils.getPostboxPlace(input));
input = new int[]{2, 8, 0, 1, 6, 5};
place = 4;
assertEquals("2,8,0,1,6,5 expected 4", place, ArrayUtils.getPostboxPlace(input));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class ProdAllExcludingElementAtIJTests {
int[] numbers, out;
@Test
public void nullEmpty() {
numbers = null;
assertEquals("null input, expected empty output", 0, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers).length);
numbers = new int[]{};
assertEquals("empty input, expected empty output", 0, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers).length);
numbers = new int[]{1};
assertEquals("single element input, expected empty output", 0, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers).length);
//ALLOW ZERO
numbers = null;
assertEquals("null input, expected empty output", 0, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers).length);
numbers = new int[]{};
assertEquals("empty input, expected empty output", 0, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers).length);
numbers = new int[]{1};
assertEquals("single element input, expected empty output", 0, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers).length);
}
@Test(expected = IllegalArgumentException.class)
public void exceptionIfZerosInInput() {
numbers = new int[]{0, 1};
ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers);
}
@Test
public void variantAllowZero() {
numbers = new int[]{0, 0};
out = new int[]{0, 0};
assertArrayEquals("0,0 input expected 0,0 output", out, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers));
numbers = new int[]{1, 2, 0, 4, 5};
out = new int[]{0, 0, 40, 0, 0};
assertArrayEquals("1, 2, 0, 4, 5 input expected 0, 0, 40, 0, 0 output", out, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers));
}
@Test
public void twoElementArray() {
numbers = new int[]{2, 3};
out = new int[]{3, 2};
assertArrayEquals("2,3 input expected 3,2 output", out, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers));
//ALLOW ZERO
numbers = new int[]{2, 3};
out = new int[]{3, 2};
assertArrayEquals("2,3 input expected 3,2 output", out, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers));
}
@Test
public void sameElementArray() {
numbers = new int[]{2, 2, 2};
out = new int[]{4, 4, 4};
assertArrayEquals("2,2,2 input expected 4,4,4 output", out, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers));
//ALLOW ZERO
numbers = new int[]{2, 2, 2};
out = new int[]{4, 4, 4};
assertArrayEquals("2,2,2 input expected 4,4,4 output", out, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers));
}
@Test
public void sampleArray() {
numbers = new int[]{1, 2, 3, 4, 5};
out = new int[]{120, 60, 40, 30, 24};
assertArrayEquals("1, 2, 3, 4, 5 input expected 120, 60, 40, 30, 24 output", out, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers));
numbers = new int[]{3, 2, 1};
out = new int[]{2, 3, 6};
assertArrayEquals("3, 2, 1 input expected 2, 3, 6 output", out, ArrayUtils.prodAllExcludingElementAtINotAllowZero(numbers));
//ALLOW ZERO
numbers = new int[]{1, 2, 3, 4, 5};
out = new int[]{120, 60, 40, 30, 24};
assertArrayEquals("1, 2, 3, 4, 5 input expected 120, 60, 40, 30, 24 output", out, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers));
numbers = new int[]{3, 2, 1};
out = new int[]{2, 3, 6};
assertArrayEquals("3, 2, 1 input expected 2, 3, 6 output", out, ArrayUtils.prodAllExcludingElementAtIAllowZero(numbers));
}
}
package com.blogspot.groglogs.structures;
import java.util.HashSet;
import java.util.Set;
/**
* Represents a quadruple of integers.
* CAUTION: hashcode and equals are beasts. We want logic so that if the four elements are same, no matter the order,
* then the objects are considered same.
*/
public class Quadruple{
public int a, b, c, d;
public Quadruple(int a, int b, int c, int d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
//can cause many collisions like this
@Override
public int hashCode(){
return this.a + this.b + this.c + this.d;
}
//add elements from both objects to two sets, then compare them, if all content is same, then objects are too
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(!(o instanceof Quadruple)){
return false;
}
Quadruple other = (Quadruple)o;
Set<Integer> thisSet = new HashSet<>();
thisSet.add(this.a);
thisSet.add(this.b);
thisSet.add(this.c);
thisSet.add(this.d);
Set<Integer> otherSet = new HashSet<>();
otherSet.add(other.a);
otherSet.add(other.b);
otherSet.add(other.c);
otherSet.add(other.d);
return thisSet.equals(otherSet);
}
}
package com.blogspot.groglogs.structures;
import java.util.Objects;
public class Range {
public int start, end, tot;
public Range(int start, int end){
this.start = start;
this.end = end;
this.tot = 0;
}
public Range(int start, int end, int tot){
this.start = start;
this.end = end;
this.tot = tot;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Range)) return false;
Range range = (Range) o;
return range.start == this.start &&
range.end == this.end &&
range.tot == this.tot;
}
@Override
public int hashCode() {
return Objects.hash(this.start, this.end, this.tot);
}
}
package com.blogspot.groglogs.test.arrayutils;
import org.junit.Test;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import static org.junit.Assert.assertArrayEquals;
public class ReverseWordsJTests {
char[] in, out;
@Test
public void nullEmpty() {
in = null;
out = null;
ArrayUtils.reverseWords(in);
assertArrayEquals("null input expected null", out, in);
in = new char[] {};
out = new char[] {};
ArrayUtils.reverseWords(in);
assertArrayEquals("empty input expected empty", out, in);
}
@Test
public void singleWord() {
in = new char[] {'t'};
out = new char[] {'t'};
ArrayUtils.reverseWords(in);
assertArrayEquals("t input expected t", out, in);
in = new char[] {'t', 'e'};
out = new char[] {'t', 'e'};
ArrayUtils.reverseWords(in);
assertArrayEquals("te input expected te", out, in);
in = new char[] {'t', 'e', 's'};
out = new char[] {'t', 'e', 's'};
ArrayUtils.reverseWords(in);
assertArrayEquals("tes input expected tes", out, in);
in = new char[] {'t', 'e', 's', 't'};
out = new char[] {'t', 'e', 's', 't'};
ArrayUtils.reverseWords(in);
assertArrayEquals("test input expected test", out, in);
}
@Test
public void multipleWordsSameLength() {
in = new char[] {'t', 'e', 's', 't', ' ', 'w', 'o', 'r', 'd'};
out = new char[] {'w', 'o', 'r', 'd', ' ', 't', 'e', 's', 't'};
ArrayUtils.reverseWords(in);
assertArrayEquals("test word input expected word test", out, in);
in = new char[] {'t', 'e', 's', 't', ' ', 'w', 'o', 'r', 'd', ' ', 'm', 'i', 'n', 'e'};
out = new char[] {'m', 'i', 'n', 'e', ' ', 'w', 'o', 'r', 'd', ' ', 't', 'e', 's', 't'};
ArrayUtils.reverseWords(in);
assertArrayEquals("test word mine input expected mine word test", out, in);
}
@Test
public void multipleWordsDifferentLength() {
in = new char[] {'t', 'e', 's', 't', ' ', 'w', 'o', 'r', 'd', 's'};
out = new char[] {'w', 'o', 'r', 'd', 's', ' ', 't', 'e', 's', 't'};
ArrayUtils.reverseWords(in);
assertArrayEquals("test words input expected words test", out, in);
in = new char[] {'t', 'e', 's', 't', ' ', 'w', 'o', 'r', 'd', 's', ' ', 'm', 'y'};
out = new char[] {'m', 'y', ' ', 'w', 'o', 'r', 'd', 's', ' ', 't', 'e', 's', 't'};
ArrayUtils.reverseWords(in);
assertArrayEquals("test words my input expected my words test", out, in);
}
}
package com.blogspot.groglogs.test.arrayutils;
import org.junit.Test;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
public class RPNJTests {
String[] input, converted;
double output, res;
@Test
//null or empty expected exception
public void nullEmpty() {
input = null;
try{
res = ArrayUtils.evaluateRPN(input);
}catch (IllegalArgumentException e){
System.out.println("null input string got IllegalArgumentException: " + e.getMessage());
}
}
@Test
//wrong expression, expected exception
public void wrongExpression() {
input = new String[]{"3", "2", "1"};
try{
res = ArrayUtils.evaluateRPN(input);
}catch (IllegalArgumentException e){
System.out.println("No operands input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"a", "a", "a"};
try{
res = ArrayUtils.evaluateRPN(input);
}catch (IllegalArgumentException e){
System.out.println("Character input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"3", "2", "+", "1"};
try{
res = ArrayUtils.evaluateRPN(input);
}catch (IllegalArgumentException e){
System.out.println("Wrong operands input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"3", "0", "/"};
try{
res = ArrayUtils.evaluateRPN(input);
}catch (ArithmeticException e){
System.out.println("Divide by 0 got ArithmeticException: " + e.getMessage());
}
}
@Test
//multiplication, 3 characters allowed
public void multiplicationCharacters() {
input = new String[]{"3", "2", "*"};
output = 6;
assertEquals("3 * 2 expected 6", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"3", "2", "x"};
output = 6;
assertEquals("3 x 2 expected 6", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"3", "2", "X"};
output = 6;
assertEquals("3 X 2 expected 6", output, ArrayUtils.evaluateRPN(input), 0);
}
@Test
//integer expression, expected integer
public void intExpression() {
input = new String[]{"3", "2", "+"};
output = 5;
assertEquals("3 + 2 expected 5", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"3", "4", "5", "x", "-"};
output = -17;
assertEquals("3 - (4 x 5) expected -17", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"3", "4", "-", "5", "X"};
output = -5;
assertEquals("(3 - 4) x 5 expected -5", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"3", "5", "+", "2", "1", "+", "-"};
output = 5;
assertEquals("(3 + 5) - (2 + 1) expected 5", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"15", "7", "1", "1", "+", "-", "/", "3", "x", "2", "1", "1", "+", "+", "-"};
output = 5;
assertEquals("((15 / (7 - (1 + 1))) x 3) - (2 + (1 + 1)) expected 5", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"-8","23","8","-","9","23","-","-","*"};
output = -232;
assertEquals("-8 x ((23 - 8) - (9 - 23)) expected -232", output, ArrayUtils.evaluateRPN(input), 0);
}
@Test
//double expression, expected double
public void doubleExpression() {
input = new String[]{"3", "2", "/"};
output = 1.5;
assertEquals("3 / 2 expected 1.5", output, ArrayUtils.evaluateRPN(input), 0);
input = new String[]{"3", "2", "/", "1" ,"+"};
output = 2.5;
assertEquals("(3 / 2) + 1 expected 2.5", output, ArrayUtils.evaluateRPN(input), 0);
}
@Test
//null or empty expected exception
public void nullEmptyinPlace() {
input = null;
try{
res = ArrayUtils.evaluateRPNinPlace(input);
}catch (IllegalArgumentException e){
System.out.println("null input string got IllegalArgumentException: " + e.getMessage());
}
}
@Test
//wrong expression, expected exception
public void wrongExpressioninPlace() {
input = new String[]{"3", "2", "1"};
try{
res = ArrayUtils.evaluateRPNinPlace(input);
}catch (IllegalArgumentException e){
System.out.println("No operands input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"a", "a", "a"};
try{
res = ArrayUtils.evaluateRPNinPlace(input);
}catch (IllegalArgumentException e){
System.out.println("Character input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"3", "2", "+", "1"};
try{
res = ArrayUtils.evaluateRPNinPlace(input);
}catch (IllegalArgumentException e){
System.out.println("Wrong operands input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"3", "0", "/"};
try{
res = ArrayUtils.evaluateRPNinPlace(input);
}catch (ArithmeticException e){
System.out.println("Divide by 0 got ArithmeticException: " + e.getMessage());
}
}
@Test
//multiplication, 3 characters allowed
public void multiplicationCharactersinPlace() {
input = new String[]{"3", "2", "*"};
output = 6;
assertEquals("3 * 2 expected 6", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"3", "2", "x"};
output = 6;
assertEquals("3 x 2 expected 6", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"3", "2", "X"};
output = 6;
assertEquals("3 X 2 expected 6", output, ArrayUtils.evaluateRPNinPlace(input), 0);
}
@Test
//integer expression, expected integer
public void intExpressioninPlace() {
input = new String[]{"3", "2", "+"};
output = 5;
assertEquals("3 + 2 expected 5", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"3", "4", "5", "x", "-"};
output = -17;
assertEquals("3 - (4 x 5) expected -17", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"3", "4", "-", "5", "X"};
output = -5;
assertEquals("(3 - 4) x 5 expected -5", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"3", "5", "+", "2", "1", "+", "-"};
output = 5;
assertEquals("(3 + 5) - (2 + 1) expected 5", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"15", "7", "1", "1", "+", "-", "/", "3", "x", "2", "1", "1", "+", "+", "-"};
output = 5;
assertEquals("((15 / (7 - (1 + 1))) x 3) - (2 + (1 + 1)) expected 5", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"-8","23","8","-","9","23","-","-","*"};
output = -232;
assertEquals("-8 x ((23 - 8) - (9 - 23)) expected -232", output, ArrayUtils.evaluateRPNinPlace(input), 0);
}
@Test
//double expression, expected double
public void doubleExpressioninPlace() {
input = new String[]{"3", "2", "/"};
output = 1.5;
assertEquals("3 / 2 expected 1.5", output, ArrayUtils.evaluateRPNinPlace(input), 0);
input = new String[]{"3", "2", "/", "1" ,"+"};
output = 2.5;
assertEquals("(3 / 2) + 1 expected 2.5", output, ArrayUtils.evaluateRPNinPlace(input), 0);
}
@Test
//null or empty expected exception
public void nullEmptyconvertToRPN() {
input = null;
try{
converted = ArrayUtils.convertToRPN(input);
}catch (IllegalArgumentException e){
System.out.println("null input string got IllegalArgumentException: " + e.getMessage());
}
}
@Test
//wrong expression, expected exception
public void wrongExpressionconvertToRPN() {
input = new String[]{"a", "a", "a"};
try{
converted = ArrayUtils.convertToRPN(input);
}catch (IllegalArgumentException e){
System.out.println("Character input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"3", "+", "2", "("};
try{
converted = ArrayUtils.convertToRPN(input);
}catch (IllegalArgumentException e){
System.out.println("Wrong operands input got IllegalArgumentException: " + e.getMessage());
}
input = new String[]{"(", "3", "+", "2", ")", ")"};
try{
converted = ArrayUtils.convertToRPN(input);
}catch (IllegalArgumentException e){
System.out.println("Wrong operands input got IllegalArgumentException: " + e.getMessage());
}
}
@Test
//multiplication, 3 characters allowed
public void multiplicationCharactersconvertToRPN() {
input = new String[]{"3", "*", "2"};
converted = new String[]{"3", "2", "*"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"3", "x", "2"};
converted = new String[]{"3", "2", "x"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"3", "X", "2"};
converted = new String[]{"3", "2", "X"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
}
@Test
//integer expression
public void intExpressionconvertToRPN() {
input = new String[]{"3", "+", "2"};
converted = new String[]{"3", "2", "+"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"3", "-", "4", "x", "5"};
converted = new String[]{"3", "4", "5", "x", "-"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"(", "3", "-", "4", ")", "X", "5"};
converted = new String[]{"3", "4", "-", "5", "X"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"(", "3", "+", "5", ")", "-", "(", "2", "+", "1", ")"};
converted = new String[]{"3", "5", "+", "2", "1", "+", "-"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"(", "15", "/", "(", "7", "-", "(", "1", "+", "1", ")", ")", ")", "x", "3", "-", "(", "2", "+", "(", "1", "+", "1", ")", ")"};
converted = new String[]{"15", "7", "1", "1", "+", "-", "/", "3", "x", "2", "1", "1", "+", "+", "-"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"-8", "*", "(", "(", "23", "-", "8", ")", "-", "(", "9", "-", "23", ")", ")"};
converted = new String[]{"-8","23","8","-","9","23","-","-","*"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
}
@Test
//double expression
public void doubleExpressionconvertToRPN() {
input = new String[]{"3.5", "+", "2.06"};
converted = new String[]{"3.5", "2.06", "+"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
input = new String[]{"3.67", "-", "4.01", "x", "5"};
converted = new String[]{"3.67", "4.01", "5", "x", "-"};
assertArrayEquals(converted, ArrayUtils.convertToRPN(input));
}
@Test
//convert to RPN and evaluate
public void convertToRPNandEvaluate() {
input = new String[]{"(", "15", "/", "(", "7", "-", "(", "1", "+", "1", ")", ")", ")", "x", "3", "-", "(", "2", "+", "(", "1", "+", "1", ")", ")"};
output = 5;
assertEquals("convert and evaluate ((15 / (7 - (1 + 1))) x 3) - (2 + (1 + 1)) expected 5", output, ArrayUtils.evaluateRPNinPlace(ArrayUtils.convertToRPN(input)), 0);
input = new String[]{"-8", "*", "(", "(", "23", "-", "8", ")", "-", "(", "9", "-", "23", ")", ")"};
output = -232;
assertEquals("convert and evaluate -8 x ((23 - 8) - (9 - 23)) expected -232", output, ArrayUtils.evaluateRPNinPlace(ArrayUtils.convertToRPN(input)), 0);
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class StartIndexForSumNeverBelowZeroJTests {
int[] gasAtCity, gasForNextCity;
int expected;
@Test
public void nullEmpty() {
gasAtCity = null;
gasForNextCity = null;
expected = -1;
assertEquals("null", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{};
gasForNextCity = new int[]{};
expected = -1;
assertEquals("empty", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void differentLength() {
gasAtCity = new int[]{1};
gasForNextCity = new int[]{1,2};
expected = -1;
assertEquals("different length", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void oneCity() {
gasAtCity = new int[]{1};
gasForNextCity = new int[]{1};
expected = 0;
assertEquals("one city 0 sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{1};
gasForNextCity = new int[]{2};
expected = 0;
assertEquals("one city negative sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{2};
gasForNextCity = new int[]{1};
expected = 0;
assertEquals("one city positive sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void twoCities() {
gasAtCity = new int[]{1,1};
gasForNextCity = new int[]{1,1};
expected = 0;
assertEquals("two cities 0 sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{1,1};
gasForNextCity = new int[]{2,2};
expected = -1;
assertEquals("two cities negative sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{1,2};
gasForNextCity = new int[]{2,1};
expected = 1;
assertEquals("two cities round 0 sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{2,2};
gasForNextCity = new int[]{1,1};
expected = 0;
assertEquals("two cities positive sum", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void increasing() {
gasAtCity = new int[]{1,2,3,4,5};
gasForNextCity = new int[]{1,2,3,4,5};
expected = 0;
assertEquals("1,2,3,4,5", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{1,2,3,4,5};
gasForNextCity = new int[]{4,5,1,2,3};
expected = 2;
assertEquals("1,2,3,4,5 - 4,5,1,2,3", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void decreasing() {
gasAtCity = new int[]{5,4,3,2,1};
gasForNextCity = new int[]{5,4,3,2,1};
expected = 0;
assertEquals("5,4,3,2,1", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{5,4,3,2,1};
gasForNextCity = new int[]{4,3,2,1,5};
expected = 0;
assertEquals("5,4,3,2,1 - 4,3,2,1,5", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void impossible() {
gasAtCity = new int[]{1,2,3,4,5};
gasForNextCity = new int[]{2,3,4,5,6};
expected = -1;
assertEquals("1,2,3,4,5 - 2,3,4,5,6", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
gasAtCity = new int[]{1,1,1,1,1};
gasForNextCity = new int[]{1,1,2,1,1};
expected = -1;
assertEquals("1,1,1,1,1 - 1,1,2,1,1", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
@Test
public void sample() {
gasAtCity = new int[]{1,2,3,4,5};
gasForNextCity = new int[]{3,4,5,1,2};
expected = 3;
assertEquals("1,2,3,4,5 - 3,4,5,1,2", expected, ArrayUtils.startIndexForSumNeverBelowZero(gasAtCity, gasForNextCity));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class SubarrayOfSumKJTests {
int[] in;
int k;
@Test
public void nullEmpty() {
in = null;
k = 1;
assertFalse("null", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{};
k = 1;
assertFalse("empty", ArrayUtils.subarrayOfSumK(k, in));
}
@Test
public void oneElement() {
in = new int[]{1};
k = 1;
assertFalse("1", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{2};
k = 1;
assertFalse("2", ArrayUtils.subarrayOfSumK(k, in));
}
@Test
public void twoElement() {
in = new int[]{1, 2};
k = 1;
assertFalse("1", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{1, 2};
k = 3;
assertTrue("3", ArrayUtils.subarrayOfSumK(k, in));
}
@Test
public void ascending() {
in = new int[]{1, 2, 3, 4};
k = 1;
assertFalse("1", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{1, 2, 3, 4};
k = 5;
assertTrue("5", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{1, 2, 3, 4};
k = 10;
assertTrue("10", ArrayUtils.subarrayOfSumK(k, in));
}
@Test
public void descending() {
in = new int[]{4,3,2,1};
k = 1;
assertFalse("1", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{4,3,2,1};
k = 5;
assertTrue("5", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{4,3,2,1};
k = 3;
assertTrue("3", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{4,3,2,1};
k = 10;
assertTrue("10", ArrayUtils.subarrayOfSumK(k, in));
}
@Test
public void sample() {
in = new int[]{5,0,0,0};
k = 1;
assertFalse("1", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{5,0,0,0};
k = 5;
assertTrue("5", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{5,0,0,0};
k = 0;
assertTrue("0", ArrayUtils.subarrayOfSumK(k, in));
in = new int[]{2,-1,-2,5};
k = -3;
assertTrue("-3", ArrayUtils.subarrayOfSumK(k, in));
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class ThreeSumsJTests {
int[] numbers;
int target;
List<int[]> out, expected;
@Test
public void nullEmpty() {
numbers = null;
target = 0;
assertEquals("null input", 0, ArrayUtils.threeSums(target, numbers).size());
numbers = new int[]{1, 2};
target = 0;
assertEquals("2 elements in input", 0, ArrayUtils.threeSums(target, numbers).size());
}
@Test
public void threeElementsInInput() {
numbers = new int[]{0, 0, 0};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("3 elements all zeros match target 0 expected 1 result", 1, out.size());
expected = new ArrayList<>();
expected.add(new int[]{0, 0, 0});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{1, 1, 1};
target = 3;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("3 elements all same match target expected 1 result", 1, out.size());
expected = new ArrayList<>();
expected.add(new int[]{1, 1, 1});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{1, 1, 1};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("3 elements all same NOT match target expected no result", 0, out.size());
}
//this really depends on the definition of "duplicate"
@Test
public void duplicates() {
numbers = new int[]{1, 1, 1, 1, 1, 1};
target = 3;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("6 elements all same match target expected 1 results", 1, out.size());
expected = new ArrayList<>();
expected.add(new int[]{1, 1, 1});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
}
@Test
public void ascendingInput() {
numbers = new int[]{0, 1, 2, 3, 4, 5};
target = 3;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("ascending input single match expected 1 result", 1, out.size());
expected = new ArrayList<>();
expected.add(new int[]{0, 1, 2});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{-2, -1, 0, 1, 2, 5};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("ascending input multiple matches expected 2 results", 2, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-2, 0, 2});
expected.add(new int[]{-1, 0, 1});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{-2, -1, -1, 0, 1, 1, 1, 2, 7};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("ascending input multiple matches with duplicates expected 4 results", 4, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-2, 0, 2});
expected.add(new int[]{-2, 1, 1});
expected.add(new int[]{-1, -1, 2});
expected.add(new int[]{-1, 0, 1});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
}
@Test
public void descendingInput() {
numbers = new int[]{5, 4, 3, 2, 1, 0};
target = 3;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("descending input single match expected 1 result", 1, out.size());
expected = new ArrayList<>();
expected.add(new int[]{0, 1, 2});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{5, 2, 1, 0, -1, -2};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("descending input multiple matches expected 2 results", 2, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-2, 0, 2});
expected.add(new int[]{-1, 0, 1});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{7, 2, 1, 1, 1, 1, 0, -1, -1, -2};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("ascending input multiple matches with duplicates expected 4 results", 4, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-2, 0, 2});
expected.add(new int[]{-2, 1, 1});
expected.add(new int[]{-1, -1, 2});
expected.add(new int[]{-1, 0, 1});
for(int i = 0; i < out.size(); i++){
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
}
@Test
public void sampleInput() {
numbers = new int[]{-1, 0, 1, 2, -1, -4};
target = -6;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("sample input expected 1 results", 1, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-4, -1, -1});
for (int i = 0; i < out.size(); i++) {
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{-1, 0, 1, 2, -1, -4};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("sample input expected 2 results", 2, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-1, -1, 2});
expected.add(new int[]{-1, 0, 1});
for (int i = 0; i < out.size(); i++) {
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{-5, -3, -1, 1, 2, 3, 4, 5};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("sample input expected 4 results", 4, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-5, 1, 4});
expected.add(new int[]{-5, 2, 3});
expected.add(new int[]{-3, -1, 4});
expected.add(new int[]{-3, 1, 2});
for (int i = 0; i < out.size(); i++) {
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
numbers = new int[]{-4, -1, -1, 0, 2, 2};
target = 0;
out = ArrayUtils.threeSums(target, numbers);
assertEquals("sample input expected 3 results", 2, out.size());
expected = new ArrayList<>();
expected.add(new int[]{-4, 2, 2});
expected.add(new int[]{-1, -1, 2});
for (int i = 0; i < out.size(); i++) {
int[] outResult = out.get(i);
int[] expectedResult = expected.get(i);
assertArrayEquals("result is correct", expectedResult, outResult);
}
}
}
package com.blogspot.groglogs.test.arrayutils;
import com.blogspot.groglogs.arrayutils.ArrayUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class WitnessOfTheTallPeopleJtests {
int[] people;
int expected;
@Test
public void nullEmpty() {
people = null;
expected = 0;
assertEquals("null", expected, ArrayUtils.witnessOfTheTallPeople(people));
people = new int[]{};
expected = 0;
assertEquals("empty", expected, ArrayUtils.witnessOfTheTallPeople(people));
}
@Test(expected = IllegalArgumentException.class)
public void negativeHeight() {
people = new int[]{-1};
ArrayUtils.witnessOfTheTallPeople(people);
}
@Test
public void onePerson() {
people = new int[]{1};
expected = 1;
assertEquals("one person", expected, ArrayUtils.witnessOfTheTallPeople(people));
}
@Test
public void increasing() {
people = new int[]{1,2,3,4,5};
expected = 1;
assertEquals("increasing height", expected, ArrayUtils.witnessOfTheTallPeople(people));
}
@Test
public void decreasing() {
people = new int[]{5,4,3,2,1};
expected = 5;
assertEquals("decreasing height", expected, ArrayUtils.witnessOfTheTallPeople(people));
}
@Test
public void triangles() {
people = new int[]{1,2,3,2,1};
expected = 3;
assertEquals("pyramid height", expected, ArrayUtils.witnessOfTheTallPeople(people));
people = new int[]{3,2,1,2,3};
expected = 1;
assertEquals("inverted pyramid height", expected, ArrayUtils.witnessOfTheTallPeople(people));
}
@Test
public void sample() {
people = new int[]{3, 6, 3, 4, 1};
expected = 3;
assertEquals("3, 6, 3, 4, 1", expected, ArrayUtils.witnessOfTheTallPeople(people));
}
}
@steghio
Copy link
Author

steghio commented Oct 31, 2017

Full description at:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment