Java Algorithm: Segregate Odd Even Numbers (time-space complexity) - java

I am writing a method that segregates the array of integers so that all the even integers precede all the odd integers in the array. It must take linear time in the size of the array O(n) and operate in place with only a constant amount of extra space.
Input: {2, 4, 7, 6, 1, 3, 5, 4}
Output: 2, 4, 6, 4, 7, 1, 3, 5
Input: {5, 12, 3, 21, 8, 7, 19, 102, 201}
Output: 12, 8, 102, 5, 3, 21, 7, 19, 201
These were my solutions:
private static void segregateArray1(final int[] arr) {
if (arr != null) {
int leftIdx = 0;
int rightIdx = arr.length - 1;
while (leftIdx < rightIdx) {
if (arr[leftIdx] % 2 != 0 && arr[rightIdx] % 2 == 0) {
// swap immediately
int temp = arr[leftIdx];
arr[leftIdx] = arr[rightIdx];
arr[rightIdx] = temp;
leftIdx++;
rightIdx--;
} else {
if (arr[leftIdx] % 2 == 0) {
leftIdx++;
}
if (arr[rightIdx] % 2 == 1) {
rightIdx--;
}
}
}
}
}
Method 1 takes O(n) and does not take up extra space. However, it does not maintain order.
private static int[] segregateArray2(final int[] arr) {
List<Integer> evenArr = new ArrayList<Integer>();
List<Integer> oddArr = new ArrayList<Integer>();
for (int i : arr) {
if (i % 2 == 0) {
evenArr.add(i);
} else {
oddArr.add(i);
}
}
evenArr.addAll(oddArr);
return ArrayUtils.toPrimitive(evenArr.toArray(new Integer[0]));
}
Method 2 creates ArrayList. I am unsure if this is also O(n).
To test:
public static void main(String[] args) {
int[] arr = {2, 4, 7, 6, 1, 3, 5, 4};
segregateArray1(arr);
System.out.println(Arrays.toString(arr));
int[] arr = {2, 4, 7, 6, 1, 3, 5, 4};
// creates another array segragatedArr!
int[] segragatedArr = segregateArray2(arr);
System.out.println(Arrays.toString(segragatedArr));
}
I am not sure if there is a neater solution/simplicity which satisfies time-space complexity (O(n) and space constraint).

The simplest way to do this and keep the same time complexity and also that the size of the output array is the same size as the input array is to do a modulus check on each value and if it is positive that placed to to the front of the array and if negative then to the back. Please keep in mind that you will need two variables to know the next available locations for the positive and negative numbers

ArrayList numberList = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
numberList.stream().filter(i -> i % 2 == 0).forEach(System.out::println);

Related

Google Foobar challenge Minion Work Assignment Java test cases not passing

I have tried solving this problem using Java and for some reason only 2 out of 9 test cases pass but locally all my test cases pass. I am 99% positive that there is some issue with Google Foobar Java test cases for this challenge. Did someone encounter this and if yes, what did you do to solve it?
Question was...
Write a function called solution(data, n) that takes in a list of
less than 100 integers and a number n, and returns that same list
but with all of the numbers that occur more than n times removed
entirely.
The returned list should retain the same ordering as the original
list - you don't want to mix up those carefully-planned shift
rotations! For instance, if data was [5, 10, 15, 10, 7] and n was 1,
solution(data, n) would return the list [5, 15, 7] because 10 occurs
twice, and thus was removed from the list entirely.
-- Java cases --
Input:
Solution.solution({1, 2, 3}, 0)
Output:
Input:
Solution.solution({1, 2, 2, 3, 3, 3, 4, 5, 5}, 1)
Output:
1,4
There are 6 more test cases that are hidden.
Below is the solution I created.
public class MinionShift {
public static int[] solution(int[] data, int n) {
if(n<1)
return new int[0];
if(data.length < 1)
return data;
Map<Integer, Integer> map = new HashMap<>();
for(int d: data) {
map.put(d, map.getOrDefault(d, 0) + 1);
}
return Arrays.stream(data).filter(c->map.containsKey(c) && !(map.get(c)>n)).toArray();
}
}
Test cases that I have tried...
[{1, 2, 3}, 0]
[{1, 2, 2, 3, 3, 3, 4, 5, 5}, 1]
[{1, 2, 2, 3, 3, 3, 4, 5, 5}, 10]
[{1, 2, 2, 3, 3, 3, 4, 5, 5}, -1]
[{}, 5]
[{1, 1, 1, 1, 1}, 5]
[{101, 102, 103, 104, 105}, 5]
Edit...
I tried a Java stream based solution as follows but unfortunately the challenge went away as I submitted a Python solution. But I am posting it here anyways.
public class MinionShift {
public static int[] solution(int[] data, int n) {
if(n<1)
return new int[0];
if(data.length < 1)
return data;
return Arrays.stream(data).filter(d->Arrays.stream(data).filter(i->i==d).count()<=n).toArray();
}
}
This is how I got it to work, you have to work backwards with the array. You need to create another array to check for duplicates to remember them.
public static int[] solution(int[] data, int n){
if(n < 1){
return new int[0];
}
if(data.length < 1){
return data;
}
List<Integer> a = Arrays.stream(data).boxed().collect(Collectors.toList());
for(int i = a.size()-1; i > -1; i--){
ArrayList<Integer> t = new ArrayList<>();
for(int j = 0; j < a.size(); j++){
if(a.get(j) == a.get(i)){
t.add(j);
}
}
if(t.size() > n){
for(int j = t.size()-1; j > -1; j--){
a.remove((int) t.get(j));
}
i -= t.size()-1;
}
}
data = new int[a.size()];
int c = 0;
for(int d : a){
data[c] = d;
c++;
}
return data;
}

Finding the number of fails between 3 sets of array

This is a simple coding questions that is asking to sum the marks for each students. If the total marks reaches the pass mark then reach to the next student. If they did not reach the pass marks then it will return as failed.
class StudentMarks {
public int getCountFailures() {
int[] student1 = {3, 2, 6, 4, 3, 6, 6, 7, 3, 2};
int[] student2 = {8, 7, 8, 9, 10, 7, 6, 8, 9, 6};
int[] student3 = {2, 5, 3, 1, 4, 3, 3, 2, 5, 6};
int[][] allStudents = {student1, student2, student3};
int numberFails = 0;
int passMark = 50;
// YOUR CODE GOES HERE
return numberFails;
}
}
Test code
int numberFails = marks.getCountFailures();
System.out.println("Number of fails = " + numberFails);
How would I implement a way to sum the marks for each of the students and return the number of fails.
The expected outcome
Number of fails = 2
Seems as a homework question, so this is not a complete answer but would help you come up with one on your own.
// will return the count of the arrays - the sum of the elements
// of which are equal to or cross the threshold.
long count =
Arrays.stream(allStudents)
.peek(a -> System.out.println(Arrays.toString(a)))
.filter(a -> thresholdSum(a) )
.count();
System.out.print(count);
This is the helper method.
public static boolean thresholdSum(int[] a) {
int threshold = 50;
int sum =0;
for (int i : a) {
sum += i;
if (sum >= threshold) {
return true;
}
}
return false;
}

How can I pick out the odd numbers and even numbers from a given array and then store them in another array?

How can I pick out the odd numbers and even numbers from a given array and then store them in another array? The flow is: the odd numbers will go to the odd[] array while the even numbers will go to the even[] array??
Here's my code, I'm not sure if this is correct since it somewhat stores and prints a mix of zeros and even numbers, no presence of odd numbers.....
int[] num = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int[] odd = new int[10];
int[] even = new int[10];
for (int i = 0; i < num.length; i++) { // For odd numbers
if (num[i] % 2 != 0) {
num[i] = odd[i];
}
System.out.println(num[i] + " ");
}
for (int j = 0; j < num.length; j++) { // For even numbers
if (num[j] % 2 == 0) {
num[j] = even[j];
}
System.out.println(num[j] + " ");
}
You can do all that in one loop - that would be way faster. To know the correct position to put the number in, add extra counter for each array.
Your kind of approach
int[] num = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int[] odd = new int[10];
int[] even = new int[10];
int oddPos = 0;
int evenPos = 0;
for (int i = 0; i < num.length; i++) {
if (num[i] % 2 == 0) {
even[evenPos] = num[i];
evenPos++;
} else {
odd[oddPos] = num[i];
oddPos++;
}
}
However this would not be the best solution as you (in most cases) cannot determine the length of odd and even arrays beforehand. Then you should use either arraylists or count the values of each or something else.
More dynamic approach
As stated before - you need to determine the size of the arrays at first
int[] num = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int oddCount = 0, evenCount = 0;
int oddPos = 0, evenPos = 0;
//get the count of each type
for (int i = 0; i < num.length; i++) {
if (num[i] % 2 == 0)
oddCount++;
else
evenCount++;
}
//define arrays in correct sizes
int[] odd = new int[oddCount];
int[] even = new int[evenCount];
//put values in arrays
for (int i = 0; i < num.length; i++) {
if (num[i] % 2 == 0) {
even[evenPos] = num[i];
evenPos++;
} else {
odd[oddPos] = num[i];
oddPos++;
}
}
the approach for detecting odd and even numbers is correct, But I think the problem with the code you wrote is that the length of odd and even arrays, isn't determinant. so for this matter, I suggest using ArrayList<Integer>, let's say you get the array in a function input, and want arrays in the output (I'll mix the arrays in the output for better performance. but separating the functions for each list extracting is also ok depending on what you're going to do with them).
Solution
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
public static Integer[][] separateOddnEven(int[] input) {
Integer[][] output = new Integer[2][];
List<Integer> odds = new ArrayList<>();
List<Integer> evens = new ArrayList<>();
for (int i = 0; i < input.length; ++i) {
int temp = input[i];
if (temp % 2 == 0)
evens.add(temp);
else
odds.add(temp);
}
// alternative is to use these Arraylists directly
output[0] = new Integer[odds.size()];
output[1] = new Integer[evens.size()];
output[0] = odds.toArray(output[0]);
output[1] = evens.toArray(output[1]);
return output; // index 0 has odd numbers and index 1 has even numbers.
}
public static void main(String[] args) {
int[] input = {0, 21, 24, 22, 14, 15, 16, 18};
Integer[][] output = separateOddnEven(input);
System.out.println("odd numbers :");
System.out.println(Arrays.toString(output[0]));
System.out.println("even numbers :");
System.out.println(Arrays.toString(output[1]));
}
}
output :
odd numbers :
[21, 15]
even numbers :
[0, 24, 22, 14, 16, 18]
You can collect a 2d array with two rows: even and odd as follows:
int[] num = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
// a 2d array of two rows: even and odd
int[][] arr = new int[2][];
// process a 1d array and fill a 2d array
Arrays.stream(num).boxed()
// Map<Integer,List<Integer>>
.collect(Collectors.toMap(
// key: 0 - even, 1 - odd
n -> n % 2,
// value - a list of one
// element, i.e. number
n -> new ArrayList<>(List.of(n)),
// merge duplicates
(list1, list2) -> {
list1.addAll(list2);
return list1;
}))
// fill the rows of a 2d array: even and odd
.forEach((key, value) -> arr[key] = value.stream()
.mapToInt(Integer::intValue).toArray());
// output
System.out.println("Even: " + Arrays.toString(arr[0]));
// Even: [2, 4, 6, 8, 10, 12, 14, 16]
System.out.println("Odd: " + Arrays.toString(arr[1]));
// Odd: [1, 3, 5, 7, 9, 11, 13, 15]
in lambda (3 lines)
int[] nums = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
separate even and odd nums with partitioningBy:
Map<Boolean, List<Integer>> map = IntStream.of(nums)
.boxed().collect(partitioningBy(n -> (n & 1) == 0));
…and transform the resulting List<Integer> for even and odd to int[]:
int[] even = map.get(true).stream().mapToInt(i -> i).toArray();
int[] odd = map.get(false).stream().mapToInt(i -> i).toArray();
System.out.println("even numbers: " + Arrays.toString(even));
System.out.println("odd numbers: " + Arrays.toString(odd));
even numbers: [2, 4, 6, 8, 10, 12, 14, 16]
odd numbers: [1, 3, 5, 7, 9, 11, 13, 15]

Making a copy of the second half of an array

I want to make a function that takes as parameters an array and a boolean. The boolean tells the function if the rest of the division of the array is to be included. It then returns a new array which is the copy of the second half of the first:
secondHalf({1, 2, 3, 4, 5}, true) → {3, 4, 5}
secondHalf({1, 2, 3, 4, 5}, false) → {4, 5}
For this assignment, I'm not supposed to use any other classes.
Here's what I've attempted:
static int[] secondHalf(int[] vector, boolean include) {
int size = vector.length/2;
if(vector.length%2 == 0)
include = false;
if(include)
size ++;
int[] vector_2 = new int[size];
int i = 0;
while(i < size){
if(include)
vector_2[i] = vector[i+size-1];
vector_2[i] = vector[i+size+1];
i++;
}
return vector_2;
To find the size of vector_2, I've decided to use compound assignment operators. So the first part of this solution checks for the required condition and assigns a value to size in a single statement.
Since we know how many times to iterate over the loop, I think a for loop would be more appropriate than a while loop.
The loop retrieves all the values in vector from the middle of the array to the end of the array and places each value into vector_2.
static int[] secondHalf(int[] vector, boolean include) {
int size = vector.length/2 + (include && vector.length%2 != 0 ? 1 : 0);
int[] vector_2 = new int[size];
for(int i = 0; i < size; i++)
vector_2[i] = vector[vector.length - size + i];
return vector_2;
}
People have hinted at System#arraycopy, but with Arrays.copyOfRange there is an even simpler method, where you only have to define the proper start index and directly receive the copy.
The start index is array.length / 2 by default. Iff the include flag is true, then you have to add the remainder of dividing the array length by 2 to that.
An MCVE:
import java.util.Arrays;
public class ArrayPartCopy
{
public static void main(String[] args)
{
int array0[] = { 1, 2, 3, 4, 5 };
System.out.println("For " + Arrays.toString(array0));
System.out.println(Arrays.toString(secondHalf(array0, true)));
System.out.println(Arrays.toString(secondHalf(array0, false)));
int array1[] = { 1, 2, 3, 4 };
System.out.println("For " + Arrays.toString(array1));
System.out.println(Arrays.toString(secondHalf(array1, true)));
System.out.println(Arrays.toString(secondHalf(array1, false)));
}
static int[] secondHalf(int[] array, boolean include)
{
int start = array.length / 2;
if (include)
{
start += array.length % 2;
}
return Arrays.copyOfRange(array, start, array.length);
}
}
The output is
For [1, 2, 3, 4, 5]
[4, 5]
[3, 4, 5]
For [1, 2, 3, 4]
[3, 4]
[3, 4]

Find first duplicate element with lowest second occurrence index

I'm trying to solve a problem on CodeFights called firstDuplicate, that states -
Given an array a that contains only numbers in the range from 1 to
a.length, find the first duplicate number for which the second
occurrence has the minimal index. In other words, if there are more
than 1 duplicated numbers, return the number for which the second
occurrence has a smaller index than the second occurrence of the other
number does. If there are no such elements, return -1.
Example
For a = [2, 3, 3, 1, 5, 2], the output should be firstDuplicate(a) =
3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3
has a smaller index than than second occurrence of 2 does, so the
answer is 3.
For a = [2, 4, 3, 5, 1], the output should be firstDuplicate(a) = -1.
My solution -
public class FirstDuplicate {
private static HashMap<Integer, Integer> counts = new HashMap<>();
private static void findSecondIndexFrom(int[] num, int n, int i) {
// given an array, a starting index and a number, find second occurrence of that number beginning from next index
for(int x = i; x < num.length; x++) {
if(num[x] == n) {
// second occurrence found - place in map and terminate
counts.put(n, x);
return;
}
}
}
private static int firstDuplicate(int[] a) {
// for each element in loop, if it's not already in hashmap
// find it's second occurrence in array and place number and index in map
for(int i = 0; i < a.length; i++) {
if(!counts.containsKey(a[i])) {
findSecondIndexFrom(a, a[i], i+1);
}
}
System.out.println(counts);
// if map is empty - no duplicate elements, return -1
if(counts.size() == 0) {
return -1;
}
// else - get array of values from map, sort it, find lowest value and return corresponding key
ArrayList<Integer> values = new ArrayList<>(counts.values());
Collections.sort(values);
int lowest = values.get(0);
//System.out.println(lowest);
for(Map.Entry<Integer, Integer> entries: counts.entrySet()) {
if(entries.getValue() == lowest) {
return entries.getKey();
}
}
return -1;
}
public static void main(String[] args) {
// int[] a = new int[]{2, 3, 3, 1, 5, 2};
//int[] a = new int[]{2, 4, 3, 5, 1};
//int[] a = new int[]{8, 4, 6, 2, 6, 4, 7, 9, 5, 8};
//int[] a = new int[]{1, 1, 2, 2, 1};
int[] a = new int[]{10, 6, 8, 4, 9, 1, 7, 2, 5, 3};
System.out.println(firstDuplicate(a));
}
}
This solution passes only for about 4 of the 11 test cases on CodeFights. However, I manually executed each one of the test cases in my IDE, and each one produces the right result.
I can't figure out why this won't work in CodeFights. Does it have something to do with the use of the static HashMap?
Edited: Since adding and checking if element is present in Set can be done in one step, code can be simplified to:
public static int findDuplicateWithLowestIndex(int... a){
Set<Integer> set = new HashSet<>();
for(int num : a){
if(!set.add(num)){
return num;
}
}
return -1;
}
You're completly right Patrick.
Use this solution: here duplicateIndex should be very large number.
package sample;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Duplicate {
public static Integer secondIndex(Integer[] arr) {
List<Integer> arrlist = new ArrayList<>(Arrays.asList(arr));
int duplicateIndex = 999;
int ele = 0;
for (int i = 0; i < arrlist.size(); i++) {
int secondIndex = getSecondIndex(arrlist, arrlist.get(i));
if (secondIndex >= 0 && duplicateIndex > secondIndex) {
duplicateIndex = secondIndex;
ele = arrlist.get(i);
}
}
return duplicateIndex == 999 ? -1 : ele;
}
public static int getSecondIndex(List<Integer> arr, int ele) {
List<Integer> var0 = new ArrayList<>(arr);
var0.set(var0.indexOf(ele), -1);
return var0.indexOf(ele);
}
public static void main(String[] str) {
// Integer[] arr = new Integer[] { 2, 3, 3, 1, 5, 2 };
// Integer[] arr = new Integer[] { 2, 4, 3, 5, 1 };
// Integer[] arr = new Integer[] { 8, 4, 6, 2, 6, 4, 7, 9, 5, 8 };
// Integer[] arr = new Integer[]{1, 1, 2, 2, 1};
Integer[] arr = new Integer[] { 10, 6, 8, 4, 9, 1, 7, 2, 5, 3 };
System.out.println(secondIndex(arr));
}
}
Solution in Javascript
function solution(a) {
const duplicates = [];
for (const i of a) {
if (duplicates.includes(i))
return i;
else
duplicates.push(i);
}
return -1;
}
console.log(solution([2, 1, 3, 5, 3, 2])); // 3
console.log(solution([2, 2])); // 2
console.log(solution([2, 4, 3, 5, 1])); // -1

Categories