Filling HashSet in a codeFights task - java

The goal of my daily task is to count how many groups of integers(from 1 to 10000, 10001 to 20000 etc. till 1000000) in a giving array (int [] a) are represented( as for example, a[0] =2, so it belongs to group 1).
My view on that is like:
static int countingGroups(int[] a)
{
Set<Integer> setOfGroupsExposed = new HashSet<>();
// Creating set for counting groups
List<Integer>list = IntStream.iterate(1,i->i+(1*10000)).limit(101).boxed()
.collect(Collectors.toList());
// list.forEach(System.out::println);
int res = setOfGroupsExposed.size();
for (int i = 0; i <a.length ; i++) {
for (int j = 0; j <list.size() ; j++) {
if(a[i]>=list.get(j)&& a[i]<list.get(j++))
setOfGroupsExposed.add(j);
// System.out.println(setOfGroupsExposed); - prints just empty brackets
}
}
return res;
}
And I can't understand what's wrong with my loops over here, and why Set is always empty.

You should change
if (a[i]>=list.get(j)&& a[i]<list.get(j++))
to
if (a[i]>=list.get(j)&& a[i]<list.get(j+1))
otherwise you increment j twice in each iteration, and besides, j++ returns the original value of j, so your condition has the same effect as if (a[i]>=list.get(j)&& a[i]<list.get(j)).

Related

Count the number of all possible distinct 3-digit numbers given a String array

Below is my code for counting the number of distinct 3 digit strings which works correctly, HOWEVER, I would like to optimize this code to where I can improve the time complexity. Can someone help me w/ this?
input: [1,2,1,4]
output: 12
Thanks.
static int countUnique(String [] arr)
{
Set<String> s = new TreeSet<>();
for (int i = 0; i<arr.length; i++)
{
for (int j = 0; j<arr.length; j++)
{
for (int k = 0; k<arr.length; k++)
{
if (i!=j && j != k && i!=k)
s.add((arr[i] +""+arr[j]+""+arr[k]));
}
}
}
return s.size();
}
Here's an O(n) solution:
Iterate over each distinct available digit in turn.
(A) Add 1 if there are three instances of it, accounting for one string of three of this digit.
(B) If there are two instances of it, add 3 times the number of digits already iterated over, accounting for 3 choose 2 ways to arrange two instances of this digit with one other digit already iterated over.
(C) Add the number of ways we can pick two of the digits seen so far, accounting for arranging just one instance of this digit with each of those.
(D) Finally, add to our record of the counts of ways to arrange two digits: if there are two instances of this digit, add 3 choose 2 = 3, accounting for just arranging two instances of this digit. Also add (2 * 3 choose 2 = 6) times the number of digits already iterated over, accounting for the number of ways to arrange this digit with another already seen.
For example:
1 2 1 4
1 -> D applies, add 3 to the two-digit-arrangements count
11x, 1x1, x11
2 -> C applies, add 3 to result
112, 121, 211
D applies, add 6 to the two-digit-arrangements count (total 9)
12x, 1x2, x12, 21x, 2x1, x21
4 -> C applies, add 9 to result
Result 12
JavaScript code with random tests, comparing with your brute force approach:
function f(A){
const counts = {};
for (let a of A)
counts[a] = counts[a] ? -~counts[a] : 1;
let result = 0;
let numTwoDigitArrangements = 0;
let numSeen = 0;
for (let d of Object.keys(counts)){
if (counts[d] > 2)
result += 1;
if (counts[d] > 1)
result += 3 * numSeen;
result += numTwoDigitArrangements;
if (counts[d] > 1)
numTwoDigitArrangements += 3;
numTwoDigitArrangements += 6 * numSeen;
numSeen = numSeen + 1;
}
return result;
}
function bruteForce(arr){
const s = new Set();
for (let i=0; i<arr.length; i++){
for (let j=0; j<arr.length; j++){
for (let k=0; k<arr.length; k++){
if (i != j && j != k && i != k)
s.add((arr[i] + "" + arr[j]+ "" + arr[k]));
}
}
}
return s.size;
}
// Random tests
var numTests = 500;
var maxLength = 25;
for (let i=0; i<numTests; i++){
const n = Math.ceil(Math.random() * maxLength);
const A = new Array(n);
for (let j=0; j<n; j++)
A[j] = Math.floor(Math.random() * 10);
const _f = f(A);
const _bruteForce = bruteForce(A);
if (_f != _bruteForce){
console.log('Mismatch found:');
console.log('' + A);
console.log(`f: ${ _f }`);
console.log(`brute force: ${ _bruteForce }`);
}
}
console.log('Done testing.');
Another way to solve this is by Backtracking algorithm. Any combination or permutation kind of problem can be solved using Backtracking.
Here is some information on Backtracking algorithm - https://en.wikipedia.org/wiki/Backtracking
Note: This is not most optimized solution nor O(n) solution. This solution is O(n! * n). But there are many opportunities to make it more optimized.
Java code using Backtracking:
int countUniqueOpt(String[] arr) {
//Set to avoid duplicates
Set<String> resultList = new HashSet<>();
backtracking(arr, 3, resultList, new ArrayList<>());
return resultList.size();
}
void backtracking(String[] arr, int k, Set<String> resultList, List<Integer> indexList) {
if (indexList.size() == k) {
String tempString = arr[indexList.get(0)] + arr[indexList.get(1)] + arr[indexList.get(2)];
resultList.add(tempString);
} else {
for (int i = 0; i < arr.length; i++) {
if (!indexList.contains(i)) {
indexList.add(i);
backtracking(arr, k, resultList, indexList);
indexList.remove(indexList.size() - 1);
}
}
}
}

Changing 2D ArrayList code to 2D array code

I found this code online and it works well to permute through the given array and return all possible combinations of the numbers given. Does anyone know how to change this code to incorporate a 2D array instead?
public static ArrayList<ArrayList<Integer>> permute(int[] numbers) {
ArrayList<ArrayList<Integer>> permutations = new ArrayList<ArrayList<Integer>>();
permutations.add(new ArrayList<Integer>());
for ( int i = 0; i < numbers.length; i++ ) {
ArrayList<ArrayList<Integer>> current = new ArrayList<ArrayList<Integer>>();
for ( ArrayList<Integer> p : permutations ) {
for ( int j = 0, n = p.size() + 1; j < n; j++ ) {
ArrayList<Integer> temp = new ArrayList<Integer>(p);
temp.add(j, numbers[i]);
current.add(temp);
}
}
permutations = new ArrayList<ArrayList<Integer>>(current);
}
return permutations;
}
This is what I have attempted:
public static int[][] permute(int[] numbers){
int[][] permutations = new int[24][4];
permutations[0] = new int[4];
for ( int i = 0; i < numbers.length; i++ ) {
int[][] current = new int[24][4];
for ( int[] permutation : permutations ) {
for ( int j = 0; j < permutation.length; j++ ) {
permutation[j] = numbers[i];
int[] temp = new int[4];
current[i] = temp;
}
}
permutations = current;
}
return permutations;
}
However this returns all zeroes. I chose 24 and 4 because that is the size of the 2D array that I need.
Thanks
It’s not really that easy. The original code exploits the more dynamic behaviour of ArrayList, so a bit of hand coding will be necessary. There are many correct thoughts in your code. I tried to write an explanation of the issues I saw, but it became too long, so I decided to modify your code instead.
The original temp.add(j, numbers[i]); is the hardest part to do with arrays since it invloves pushing the elements to the right of position j one position to the right. In my version I create a temp array just once in the middle loop and shuffle one element at a time in the innermost loop.
public static int[][] permute(int[] numbers) {
// Follow the original here and create an array of just 1 array of length 0
int[][] permutations = new int[1][0];
for (int i = 0; i < numbers.length; i++) {
// insert numbers[i] into each possible position in each array already in permutations.
// create array with enough room: when before we had permutations.length arrays, we will now need:
int[][] current = new int[(permutations[0].length + 1) * permutations.length][];
int count = 0; // number of new permutations in current
for (int[] permutation : permutations) {
// insert numbers[i] into each of the permutation.length + 1 possible positions of permutation.
// to avoid too much shuffling, create a temp array
// and use it for all new permutations made from permutation.
int[] temp = Arrays.copyOf(permutation, permutation.length + 1);
for (int j = permutation.length; j > 0; j--) {
temp[j] = numbers[i];
// remember to make a copy of the temp array
current[count] = temp.clone();
count++;
// move element to make room for numbers[i] at next position to the left
temp[j] = temp[j - 1];
}
temp[0] = numbers[i];
current[count] = temp.clone();
count++;
}
assert count == current.length : "" + count + " != " + current.length;
permutations = current;
}
return permutations;
}
My trick with the temp array means I don’t get the permutations in the same order as in the origianl code. If this is a requirement, you may copy permutation into temp starting at index 1 and shuffle the opposite way in the loop. System.arraycopy() may do the initial copying.
The problem here is that you really need to implement properly the array version of the ArrayList.add(int,value) command. Which is to say you do an System.arraycopy() and push all the values after j, down one and then insert the value at j. You currently set the value. But, that overwrites the value of permutation[j], which should actually have been moved to permutations[j+1] already.
So where you do:
permutation[j] = numbers[i];
It should be:
System.arraycopy(permutation,j, permutations, j+1, permutations.length -j);
permutation[j] = numbers[i];
As the ArrayList.add(int,value) does that. You basically wrongly implemented it as .set().
Though personally I would scrap the code and go with something to dynamically make those values on the fly. A few more values and you're talking something prohibitive with regard to memory. It isn't hard to find the nth index of a permutation. Even without allocating any memory at all. (though you need a copy of the array if you're going to fiddle with such things without incurring oddities).
public static int[] permute(int[] values, long index) {
int[] returnvalues = Arrays.copyOf(values,values.length);
if (permutation(returnvalues, index)) return returnvalues;
else return null;
}
public static boolean permutation(int[] values, long index) {
return permutation(values, values.length, index);
}
private static boolean permutation(int[] values, int n, long index) {
if ((index == 0) || (n == 0)) return (index == 0);
int v = n-(int)(index % n);
int temp = values[n];
values[n] = values[v];
values[v] = temp;
return permutation(values,n-1,index/n);
}

How to concatenate array with multiple numbers

I need to print [1,2,3,4] when I get array like [1,1,2,2,3,3,4].
Other examples:
input=[1,1,1,2] output should be=[1,2]
input=[1,1,1,1] output should be=[1]
int count=1;
//This for counts only the different numbers of the array input.
for(int i=0; i<array.length-1;i++){
if(array[i+1]!=array[i]){
count++;
}
}
//New array only for the needed numbers.
Integer [] res = new Integer[count];
res[0] = array[0];
for(int i = 1;i<count;i++){
if(array[i]!=array[i+1]){
res[i]=array[i+1];
}
}
With input [1,1,2,2,3,3,4] I get [1, 2, null, 3].
Should be [1,2,3,4].
One problem is that you increment the loop's counter even when array[i]==array[i+1], which results in the output array having null values.
Another problem is that you don't iterate over all the elements of the input array in the second loop.
Both problems can be solved if you use two indices, one for the input array (the loop's variable) and another for the current position in the output array :
int count=1;
for(int i=0; i<array.length-1;i++){
if(array[i+1]!=array[i]){
count++;
}
}
Integer [] res = new Integer[count];
res[0] = array[0];
int resIndex = 1;
for(int i = 1; i < array.length - 1; i++){
if(array[i] != array[i+1]) {
res[resIndex] = array[i+1];
resIndex++;
}
}
EDIT :
As fabian suggested, changing the second loop to
for(int i = 1 ; i < array.length - 1 && resIndex < count; i++)
can make it slightly faster if the last unique number of the input array repeats multiple times.

Predicament with arrays and expansion

So I know that the java convention is to use ArrayList<> when it comes to expansions and many other applications. The typical array cannot expand. My java course is elementary so we are still reviewing over arrays right now. As much as I want to use an arraylist I cant. How do I make it to where I store only elements that satisfy the condition in my counter array?
public int[] above100Degrees()
{
int[] blazing = new int[temps.length];
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
blazing[i] = temps[i];
}
}
return blazing;
}
Output
The temperature above 100 degrees is: 0 0 0 0 0 0 0 103 108 109
Just count how many elements match your filter first, then create the array, then populate it. It means you'll need to go through the array twice, but there are no really nice alternatives unless you want to end up creating multiple arrays. So something like:
public int[] above100Degrees() {
// First work out how many items match your filter
int count = 0;
// Are you allowed to use the enhanced for loop? It's not necessary, but it
// makes things simpler.
for (int temp : temps) {
if (temp > 100) {
count++;
}
}
// Create an array of the right size...
int[] ret = new int[count];
// ... and populate it.
int index = 0;
for (int temp : temps) {
if (temp > 100) {
ret[index++] = temp;
}
}
return ret;
}
I would use a loop to find how many are above 100 before assigning the array.
public int[] above100Degrees()
{
int newArrayLength=0;
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
newArrayLength++;
}
}
int[] blazing = new int[newArrayLength];
int positionInNewArray = 0;
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
blazing[positionInNewArray] = temps[i];
positionInNewArray++;
}
}
return blazing;
}
You could do a ternary operation
resultString = "The temperature above 100 degrees is: ";
for(int i = 0; i < blazing.length; i++){
resultString += blazing[i] != 0 ? blazing[i] : "";
}
Note: This would require more memory than JonSkeets answer, but could potentially be more efficient. If your expect your array length to get very large, go with JonSkeet's answer. In other words, this won't scale well.
One way is to count things before setting up the array. Another way is to set up the array first and keep track of the count, then create a new array:
public int[] above100Degrees()
{
int[] blazing = new int[temps.length];
int count = 0;
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
blazing[count++] = temps[i];
}
}
// At this point, `count` is the number of elements you're going to return;
// and the first `count` elements of `blazing` hold those elements, while the
// remaining elements of `blazing` are garbage
int[] result = new int[count];
for ( int i = 0; i < count; i++ )
result[i] = blazing[i];
return result;
}
This approach would be better if the condition you're testing for takes a lot of time to calculate (as opposed to temps[i] > 100, which hardly takes any time). You could use Arrays.copy to create the result array, but if you can't use ArrayList you probably can't use Arrays.copy either.
Your code is keeping the cells in blazing array for i <= 100; You need to ignore these and start populating from i = 101.

Iterating over a sorted array and storing the count of distinct integers

Can somebody PLEASE answer my specific question, I cannot use material not covered in class yet and must do it this way.
I'm trying to iterate over a sorted array and if the previous number == the current number it stores the count in possiton n of a new array; when the previous number != the current number, it then moves to n+1 on the new array and starts counting again.
I'm debugging it now but having trouble working out what it isn't work. Any help is much appreciated.
// Get the count of instances.
int[] countOfNumbers = new int[50]; // Array to store count
int sizeOfArray = 0; // Last position of array filled
int instanceCounter = 1; // Counts number of instances
int previousNumber = 0; // Number stored at [k-1]
for (int k=1; k < finalArrayOfNumbers.length; k++) {
previousNumber = finalArrayOfNumbers[k-0];
if (previousNumber == finalArrayOfNumbers[k]) {
instanceCounter++;
countOfNumbers[sizeOfArray] = instanceCounter;
}
instanceCounter = 1;
sizeOfArray++;
countOfNumbers[sizeOfArray] = instanceCounter;
Don't worry about mapping or anything, I just need to know how If I have an array of:
[20, 20, 40, 40, 50]
I can get back
[2, 2, 1]
There's lots of neat tools in the Java API so you can avoid doing a lot of this yourself:
List<Integer> list = Arrays.asList(20, 20, 40, 40, 50);
Map<Integer, Integer> freq = new LinkedHashMap<>();
for (int i: list) {
freq.put(i, Collections.frequency(list, i));
}
System.out.println(freq.values());
That'll print [2, 2, 1] like you wanted.
Alternatively if you'd like a list of only the distinct values in the list, you can use an implementation of Set.
But since you're restricted because this is a class assignment, you could do something like this instead:
int[] a = { 20, 20, 40, 40, 50 };
int[] freq = new int[a.length];
// count frequencies
for (int i = 1, j = 0, count = 1; i <= a.length; i++, count++) {
if (i == a.length || a[i] != a[i - 1]) {
freq[j++] = count;
count = 0;
}
}
// print
for (int i = 0; i < freq.length && freq[i] != 0; i++) {
System.out.println(freq[i]);
}
And the output is still the same.
I put comments in the two places you were off, here's your fixed code.
for (int k = 1; k < finalArrayOfNumbers.length; k++) {
previousNumber = finalArrayOfNumbers[k - 1]; // changed 0 to 1
if (previousNumber == finalArrayOfNumbers[k]) {
instanceCounter++;
countOfNumbers[sizeOfArray] = instanceCounter;
} else { // put this last bit in an else block
instanceCounter = 1;
sizeOfArray++;
countOfNumbers[sizeOfArray] = instanceCounter;
}
}
I'm debugging it now but having trouble working out what it isn't work. Any help is much appreciated.
Here's a clue for you:
previousNumber = finalArrayOfNumbers[k-0];
if (previousNumber == finalArrayOfNumbers[k]) {
Clue: 'k - 0' has the same value as 'k' in the above.
Clue 2: If your intention is that previousNumber contains the number you are currently counting, then it needs to be initialized outside of the loop, and updates when the current number changes.
Clue 3: You should not increment sizeOfArray on every loop iteration ...
Based on your Question, I'd say that your thinking about / understanding of the code that you have written is woolly. And this is why you are having difficulty debugging it.
In order to debug a piece of code effectively, you first need a mental model of how it ought to work. Then you use the debugger to watch what is happening at key points to confirm that the program is behaving as you expect it to.
(If you come into the debugging process without a mental model, all you see is statements executing, variables changing, etcetera ... with nothing to tell you if the right thing is happening. It is like watching the flashing lights on a computer in an old movie ... not enlightening.)
I would opt for a hashmap where the key is the number and its value the count. This way you have a unique number and count. Your solution runs into a problem where you don't really know at index i, what count that number belongs to, unless your list has no duplicates and is in order with no gaps, like 1, 2, 3, 4, 5 as opposed to the case of 1, 1, 1, 1, 5, 5, 5, 5
HashMap<Integer, Integer> occurances = new HashMap>Integer, Integer>();
int[] someSortedArray = new int[10];
//fill up a sorted array
for(int index = 0; index < someSortedArray.length; index++)
{
someSortedArray[index] = index+1;
}
int current = someSortedArray[0];
int count = 1;
for(int index = 1; index < someSortedArray.length; index++)
{
if(someSortedArray[index] != current)
{
occurances.put(current, count);
current = someSortedArray[index];
count = 1;
}else
{
count++;
}
}
System.out.println(occurances);
I think this should do it (haven't compiled).
You where not increasing sizeOfArray anywhere in your for loop.
// Get the count of instances.
int[] countOfNumbers = new int[50]; // Array to store count
int sizeOfArray = 0; // Last position of array filled
int instanceCounter = 1; // Counts number of instances
int previousNumber = finalArrayOfNumbers[0]; // Number stored at [k-1]
for (int k=1; k < finalArrayOfNumbers.length; k++) {
if (previousNumber == finalArrayOfNumbers[k]) {
instanceCounter++;
}
else
{
countOfNumbers[sizeOfArray] = instanceCounter;
instanceCounter = 1;
sizeOfArray++;
previousNumber = finalArrayOfNumbers[k]
}
}
countOfNumbers[sizeOfArray] = instanceCounter;

Categories

Resources