Code not giving Desired output (Migratory Birds - HackerRank) - java

The Question is here
https://www.hackerrank.com/challenges/migratory-birds/problem
I have tried out the following code which does not work with a particular test case . The test case is here
https://hr-testcases-us-east-1.s3.amazonaws.com/33294/input04.txt?AWSAccessKeyId=AKIAJ4WZFDFQTZRGO3QA&Expires=1580486031&Signature=2XwAz3pdGmZVcNC1YpHk7buPl5U%3D&response-content-type=text%2Fplain
My code is
static int migratoryBirds(List<Integer> arr) {
List<Integer> typesofbirds = new ArrayList<>();
List<Integer> noofbirds = new ArrayList<>();
for(int i=0;i<arr.size();i++){
if(i==0){
typesofbirds.add(arr.get(i));
}
else{
for(int j=0;j<typesofbirds.size();j++){
if(j==typesofbirds.size()-1 && typesofbirds.get(j) != arr.get(i)){
typesofbirds.add(arr.get(i));
}
else{
if(typesofbirds.get(j) == arr.get(i)){
break;
}
}
}
}
}
System.out.println(typesofbirds);
for(int i=0;i<typesofbirds.size();i++){
int count=0;
for(int j=0;j<arr.size();j++){
if(typesofbirds.get(i) == arr.get(j)){
count++;
}
}
noofbirds.add(count);
}
System.out.println(noofbirds);
int maximumbirdsindex=0;
for(int i=1;i<noofbirds.size();i++){
if(noofbirds.get(i)>noofbirds.get(maximumbirdsindex)){
maximumbirdsindex=i;
}
}
return typesofbirds.get(maximumbirdsindex);
}
The array typesofbirds contains the different type of birds id order wise
The array noofbirds contains the no of birds corresponding to each bird type order wise
maximumbirdsindex contains the index of the bird corresponding to the array typesofbirds
first for loop fills the array typesofbirds the second loop fills the noofbirds array and the third loop simply calculates the index of the maximum birds id

You need to do two things: Count the number of birds for each type, and then find the maximum. While you are trying to do that, you seem to be running into an issue of getting lost in your code by making it over-complicating it. Think about this: how many birds you could count and know which number that is all in the same data structure?
Just 1 array could accomplish this really well for you. You increment the correct index of the array each time you count, and then by going in order you can determine which bird is the highest number of sightings, and that index is the correct number.
If you want to try and debug it on your own with that thought process then that's great. If you want to see a working implementation it is below(feel free not to check it out until after you have done it yourself).
static int migratoryBirds(List<Integer> arr) {
int[] birdCountArr = new int[6]; // bird numbers are guaranteed to be between [1,5], we ignore index 0.
for(int n : arr) {
birdCountArr[n]++;
}
int high = 0;
int highBirdNum = 0;
for(int i = 1; i < birdCountArr.length; i++) {
if(birdCountArr[i] > high) {
high = birdCountArr[i];
highBirdNum = i;
}
}
return highBirdNum;
}
EDIT: A little more explanation to follow up on your question. In the first loop we are simply going through the list of ints we are given and putting them into the array we made based on the index. If the bird is "4", we put it into index 4. So let's say we have this input:
1 1 2 1 3 4 4 5 2
We are going to get each number from this list and put it into the array based on index. So our array would look like this:
[0, 3, 2, 1, 2, 1]
We have 0 birds of number 0(since that's not valid), 3 birds of number 1, 2 birds of number 2, 1 bird of number 3, 2 birds of number 4, and 1 bird of number 5. The correct answer in this case would be 1, since that is the bird with the highest number.
Essentially, index = bird number, which index is highest, if there are multiple indexes with the same number, lowest index wins :)

My js solution
function migratoryBirds(arr) {
let spotted = new Array(5).fill(0);
for (let bird of arr) ++spotted[bird - 1];
return spotted.indexOf(Math.max(...spotted)) + 1;
}

Related

Count the number of times going through a list from beginning

I am trying to solve problem A (called Task Management) in the following website: http://codeforces.com/gym/101439/attachments/download/5742/2017-yandexalgorithm-qualification-round-en.pdf
Basically, we are given a unsorted list of integers from 1 to n and we want to visit integers in order(i.e from 1,2,3,4,5,.... n). How many times do we have to go to the beginning of the list until we have visited all integers from 1 to n in increasing order.
Let's say we have a list like: 3 2 1. during the first run through the list we visit only the number 1, during the second run through the list, we visit only the number 2, and during the third run we finally visit the number 3. So we have to go through the list 3 times.
Here is my code:
import java.util.Scanner;
import java.util.ArrayList;
class TaskManagement{
// arr: array of tasks
static int countNumberOfLoops(ArrayList<Integer> arr){
int targetTask = 1;
// Last task to close
int finalTask = arr.size();
int index=0;
int count =0;
while(targetTask != finalTask+1){
if(index%arr.size()==0) count++;
if(arr.get(index%arr.size())==targetTask) targetTask++;
index++;
}
System.out.println(count);
return count;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
// make a static array of size n
ArrayList<Integer> arr = new ArrayList<Integer>();
for (int i=0; i<n; i++) {
int item = scan.nextInt();
arr.add(item);
}
countNumberOfLoops(arr);
}
}
The problem is: my code is not efficient enough, O(n^2) and for a very large data set, it will be slow.
Is there any way I can implement the code in a more efficient way?
Loop through all the numbers and store the index of their occurrence in a hash table or a normal array since numbers are between 1-n.
For example if, the numbers were 3, 4, 5, 2, 1
which would result in hash like this. (let's call this Index)
{
1 -> 4,
2 -> 3,
3 -> 0,
4 -> 1,
5 -> 2
}
Loop from 1 to n-1 and find the index for ith and (i+1)th element.
loopCount = 0;
loopCount = 0;
for (int i=1; i<n; i++) {
if (Index[i] > Index[i+1]) {
loopCount++;
}
}
Time complexity O(n)
An editorial has been posted here:
Consider a solution with the complexity O(n^2). Use a variable to track the
last closed task and go through the list of tasks from the beginning
to the end. Increase the counter when passing through the element
corresponding to the next task. The constraints are quite large, so
this solution doesn't fit the timelimit.
What is the case when we can
not find any suitable tasks up to the end of the list? There is only
one case: the task number (x + 1) is closer to the beginning of the
list then the closed task number x. Therefore, to solve the problem we
can determine position of each task in the task list and count the
number of distinct numbers x such that the position of task number
(x + 1) is less than the position of task number x.
Don't forget to
consider the first pass through the task list. The final complexity of
the solution is O(n).

Getting indexes of maximum number in array

I have an array containing numbers which are ranks.
Something like this :
0 4 2 0 1 0 4 2 0 4 0 2
Here 0 corresponds to the lowest rank and max number corresponds to highest rank. There may be multiple indexes containing highest rank.
I want to find index of all those highest rank in array. I have achieved with following code:
import java.util.*;
class Index{
public static void main(String[] args){
int[] data = {0,4,2,0,1,0,4,2,0,4,0,2};
int max = Arrays.stream(data).max().getAsInt();
ArrayList<Integer> indexes = new ArrayList<Integer>();
for(int i=0;i<12;i++){
if(data[i]==max){
indexes.add(i);
}
}
for(int j=0;j<indexes.size();j++){
System.out.print(indexes.get(j)+" ");
}
System.out.println();
}
}
I have got result as : 1 6 9
Is there any better way than this ?
Because, In my case there may be an array containing millions of elements due to which I have some issue regarding performance.
So,
Any suggestion is appreciated.
One approach would be to simply make a single pass along the array and keep track of all indices of the highest number. If the current entry be less than the highest number seen so far, then no-op. If the current entry be the same as the highest number seen, then add that index. Otherwise, we have seen a new highest number and we should throw out our old list of highest numbers and start a new one.
int[] data = {0,4,2,0,1,0,4,2,0,4,0,2};
int max = Integer.MIN_VALUE;
List<Integer> vals = new ArrayList<>();
for (int i=0; i < data.length; ++i) {
if (data[i] == max) {
vals.add(i);
}
else if (data[i] > max) {
vals.clear();
vals.add(i);
max = data[i];
}
}
You are on the Stream- way... I would suggest you to stay there :)
int[] data = { 0, 4, 2, 0, -1, 0, 4, 2, 0, 4, 0, 2 };
int max = Arrays.stream(data).max().getAsInt();
int[] indices = IntStream.range(0, data.length).filter(i -> data[i] == max).toArray();
As i see your program goes through the array 2 times.You can try this:
Run through the array finding the max of this array.When you find a max just save every other element that is equal to the current max and their values.This way you only go through the array only once.
Here is an example: Let's say you have the following array {1,3,5,3,4,5} and you go through it.You will first save the 1 as max then the 3 then the 5 which is the max of this array.After saving 5 you won't save 3 or 4 but you will save 5 as it is equal to the max.Hope i helped.

Why does this merge sort give incorrect results?

My assignment is to merge two arrays using int arrays that the user fills and we have to assume that there will be a maximum of 10000 inputs from the user, and the user inputs a negative number to stop. Then sort the array from least to greatest and print it out. Initially i thought that this would be quite easy but when i finished, i began getting outputs such as:
Enter the values for the first array, up to 10000 values, enter a negative number to quit: 1
3
5
-1
Enter the values for the second array, up to 10000 values, enter a negative number to quit
2
4
6
-1
First Array:
1
3
5
Second Array:
2
4
6
Merged Array:
6 1 2 3 4 5
as you can see, the six is out of place and i have no idea how to fix it. Here is the source code, i have included copious comments because I really want you guys to help me out to the best of your abilities. IF it's possible to use the same exact technique without implement new techniques and methods into the code please do so. I know there are methods in java that can do all of this in one line but it's for an assignment at a more basic level.
import java.util.Scanner;
public class Merge
{
public static void main(String [] args)
{
Scanner scan = new Scanner(System.in);
int [] first = new int[10000]; //first array, assume 10k inputs max
int [] second = new int[10000]; //first array, assume 10k inputs max
boolean legal = true; //WILL IMPLIMENT LATER
int end = 0; // set how many elements to put in my "both" array
int end2 = 0;// set how many elements to put in my "both" array
System.out.print("Enter the values for the first array, up to 10000 values, enter a negative number to quit");
//get values
for(int i = 0; i<first.length; i++)
{
first[i] = scan.nextInt(); //fill first with user input
if(first[i] <0) //if negative number, stop loop
{
end = i; //get position of end of user input
break;
}
}
System.out.println("Enter the values for the second array, up to 10000 values, enter a negative number to quit");
for(int i = 0; i<second.length; i++) //exact same as the first get values loop
{
second[i] = scan.nextInt();
if(second[i] <0)
{
end2 = i;
break;
}
}
System.out.print("First Array:\n");
for(int i = 0; i<first.length; i++) //print first array
{
if(i == end) //this prevents from printing thousands of zeros, only prints values that user inputed
break;
System.out.println(first[i] + " ");
}
System.out.print("Second Array:\n");
for(int i = 0; i<second.length; i++) //same as printing first array
{
if(i == end2)
break;
System.out.println(second[i] + " ");
}
int [] both = new int[(end)+(end2)]; //instanciate an int array to hold only inputted values from first[] and second[]
int [] bothF = new int[(end)+(end2)]; //this is for my simple sorter algotithm loop
for(int i = 0; i<both.length; i++) //fill both with the first array that was filled
{
both[i] = first[i];
}
int temp = end; // see below
for(int i = 0;i<both.length; i++) //fill array with the second array that was filled(starting from the end of the first array so that the first set is not overwritten
{
if(temp<both.length){ //this prevents an out of bounds
both[temp] = second[i];
temp++;}
}
//simple sorting algorithm
for(int d = both.length -1;d>=0;d--)
{
for(int i = 0; i<both.length; i++)
{
if(both[d]<both[i])
{
bothF[d] = both[d];
both[d] = both[i];
both[i] = bothF[d];
}
}
}
System.out.println("Merged Array:"); //print the results
for(int i = 0; i<both.length; i++)
{
System.out.print(both[i] + " ");
}
//System.out.println("ERROR: Array not in correct order");
}
Your sorting algorithm is faulty.
It's similar to selection sort, in that you take two elements and swap them if they're out of place. However, you don't stop the comparisons when you should: when the index d is less than the index i, the comparison-and-swap based on arr[d] > arr[i] is no longer valid.
The inner loop should terminate with i=d.
The logic of your sort goes something like this:
On the d-th loop, the elements at d+1 and to the right are correctly sorted (the larger numbers). This is true at the beginning, because there are 0 elements correctly sorted to the right of the right-most element.
On each of the outer loops (with the d counter), compare the d-th largest element slot with every unsorted element, and swap if the other element is larger.
This is sufficient to sort the array, but if you begin to compare the d-th largest element slot with already-sorted elements to its right, you'll end up with a larger number in the slot than should be. Therefore, the inner loop should terminate when it reaches d.
Sure, you can do it like this
for (int i = 0; i < end; i++) {
both[i] = first[i];
}
for (int i = 0; i < end2; i++) {
both[i + end] = second[i];
}
// simple sorting algorithm
for (int d = both.length - 1; d >= 0; d--) {
for (int i = 0; i < d; i++) {
if (both[i] > both[d]) {
int t = both[d];
both[d] = both[i];
both[i] = t;
}
}
}
Output(s) -
Enter the values for the first array, up to 10000 values, enter a negative number to quit3
5
-1
Enter the values for the second array, up to 10000 values, enter a negative number to quit
2
4
6
-1
First Array:
3
5
Second Array:
2
4
6
-1
Merged Array:
2 3 4 5 6
First I will start with some recommendations:
1.Give end1 and end2 the initial value as the array lengths.
The printing part - instead of breaking the loop - loop till i == end(if its not changed by the first part it will stay the array length).
One suggestion is to use a "while" statement on the user input to do the reading part (it seems cleaner then breaking the loop- but its OK to do it like you have done too).
Try to use more functions.
now to the main thing- why not to insert the numbers from both arrays to the join array keeping them sorted?
Guiding:
Keep a marker for each array.
Iterate over the new join array If arr1[marker1]> arr2[marker2]
insert arr2[marker2] to the joint array in the current position.
and add 1 to marker2. and the opposite.
(don't forget to choose what happens if the are equal).
This can be achieved because the arrays were sorted in the first place.
Have fun practicing!
I guess you have sort of a reverse "selection sort"-algorithm going on there. I made an class that run your code and printed out the output after every swap. Here is the code which is the same as you got in your application with the addition of print.
for(int d = both.length -1;d>=0;d--)
{
for(int i = 0; i<both.length; i++)
{
if(both[d]<both[i])
{
int temp = both[d];
both[d] = both[i];
both[i] = temp;
printArray(both);
}
}
}
and when we run this on an example array we get this output
[9, 8, 7, 6]=
-> 6879
-> 6789
-> 6798
-> 6978
-> 9678
The algorithm actually had the correct answer after two swaps but then it started shuffling them into wrong order. The issue is the inner for loops end parameter. When you have run the outer loop once, you can be certain that the biggest number is in the end. 'd' is here 3 and it will swap out a bigger number every time it encounters it. the if clause comparisions in the first loop is 6-9 (swap), 9-8, 9-7, 9-9. All good so far.
Potential problems comes in the second iteration with 'd' as 2. Array is now [6,8,7,9] and comparisons are 7-6, 7-8 (swap with result [6,7,8,9]), 8-8, 8-9 (swap!!) resulting in [6,7,9,8]. the last swap was the problematic one. We knew that the biggest number was already in the last spot, but we still compare against it. with every gotrough of the whole inner loop it will always find the biggest number (and all other bigger than both[d] that is already in place) and swap it to some wrong position.
As we know that the biggest number will be last after one iteration of the outer loop, we shouldn't compare against it in the second iteration. You sort of lock the 9 in the array and only try to sort the rest, being in this case [6,8,7] where d = 3, value 7. hence, your inner loop for(int i = 0; i<both.length; i++) becomes for(int i = 0; i<=d; i++). As an added bonus, you know that in the last iteration i==d, and thus the code inside it, if(both[d]<both[i]) will never be true, and you can further enhance the loop into for(int i = 0; i<d; i++).
In your algorithm you always do four comparisons in the inner loop over four iterations of the outer loop, which means there is a total of 16 comparisons. if we use the i<d we'll just do three comparisons in the inner loop on the first iteration of the outer loop, then two, then one. This brings it to a total of six comparisons.
Sorry if too rambling, just wanted to be thorough.

searching multiple number in given array

have following problem. suppose i have an array number[n] , i want search multiple number , for example i want to search 12, 45 ,1 ,6,8,5, and if every number present array then i can get favorable result. so there is one way , i just pick one element like 7
if it is present in array number[n], then can get inside the loop , and again initialize another loop and check that if second number is in the number[n] , and so on, so here i need same number of loop as the number of searching numbers. so is there is another way to deal with such problem. because it will running time will be polynomial.
here is my code:
import java.util.Scanner;
class Number {
boolean check(int[] num)
{
for (int i = 0; i < 5; i++) {
if (num[i] == 7) {
for (j = 0; j < 5; j++) {
if (num[j] == 8) {
for (int k = 0; k < 5; k++) {
if (num[k] == 9) {
return true;
}
else
continue;
}
} else
continue;
}
} else
return false;
}
}
public static void main(string [] args)
{
Number obj1 = new Number();
Scanner input = new Scanner(System.in);
int [] num =new int[5];
for(int i=0;i<5;i++)
num[i] =input.nextInt();
boolean get ;
get = obj1.check(num []);
System.out.print(response);
}
}
You could do something like this.
public static boolean allFoundIn( int[] toSearch, int... numbers )
Set numbersSet = new HashSet(Arrays.asList(numbers));
numbersSet.removeAll(Arrays.asList(toSearch));
return numbersSet.isEmpty();
}
Then in your main, just call
allFoundIn(num, 7, 8, 9);
which will return true if 7, 8 and 9 are all found in the array num.
If you want a sub-polynomial solution then there are a few possibilities.
1) Sort both lists, then loop like so (pseudocode)
toFind = <first element of listToFind>
for i in listToSearch:
if i == toFind:
if toFind is last element of listToFind:
return true
toFind = next element of listToFind
else if i > toFind:
return false
2) Put all the elements of the list to search in a HashSet. Then loop over the elements you want to find and see if it's in the HashSet. If they all are then they're all in the list. If not then they're not all in the list. HashSet has fast lookup, so it will likely be better than polynomial time.
and since I was already beaten to the punch for 2, I'll stop thinking of alternatives and post.
Yes, you can dramatically reduce the number of passes. Firstly though, don't hard code you search numbers like that with a separate loop for each. Create one array to store the numbers being searched for and one containing the numbers being searched. Sort each in the same direction, eg ascending order. Create two ints to act as counters, one for each array. Now use a while loop to compare the numbers in each array at the positions the counters are at.
How you advance the counters depends on how the numbers compare. If the number in the array of ones being searched for is larger than the one being searched, you advance the one being searched. If the other way around you advance the one being searched and if equal you advance both and record the match. Keep going until the end of one array is reached.
Using this method you only traverse the arrays a maximum of one time. I'd write example code but I'm typing on my phone!
This solution is not the fastest since it does a binary search for every number. Additionally, it has to be sorted first. It would be better to put all your source numbers into a hash set, like in David Wallace's solution. Then each search time is constant instead of depending on the size of your source array.
boolean check(int[] num) {
int[] toSearch = new int[] { 12, 45, 1, 6, 8, 5 };
for (int search : toSearch) {
if (Arrays.binarySearch(num, search) == -1) {
return false;
}
}
return true;
}
If you want to use a hash set, you could do it like this:
boolean check(Integer[] num) {
HashSet<Integer> numSet = new HashSet<>(Arrays.asList(num));
int[] toSearch = new int[] { 12, 45, 1, 6, 8, 5 };
for (int search : toSearch) {
if (!numSet.Contains(search)) {
return false;
}
}
return true;
}

Randomly "shaking" an array to assign new random spots

What Im trying to do is take my array coins[]. And basically rearrange each coin to a different position. This is what i have so far. When I do this though, nothing happens. Meaning all the values stay the same. Except for the last one. That one changes.
public void shake()
{
for (int i = 0; i < coins.length; i++)
{
int index = Coin.RANDOM.nextInt(coins.length);
Coin temp = coins[index];
coins[index] = coins[i];
coins[i] = temp;
System.out.print(coins[i] + ", ");
}
}
I instantiate random like this:
public static long SEED = System.currentTimeMillis();
public static Random RANDOM = new Random(SEED);
Please notice that this line
System.out.print(coins[swap] + ", ");
displays the already moved (swapped) coin. Maybe you were thinking about displaying the new coin at i index: coins[i] (which wouldn't be correct anyway, as the already displayed coin still can be swapped in the next iterations). Probably it's better to create a second for loop to display final coin values.
But this isn't only problem here. To randomly shuffle an array you should use Fisher-Yates algorithm which is slightly different than your method. You can find Java implementation of this algorithm on SO.
If you had a List<Coin> instead of Coin[] (list instead of array) you could use the Collections.shuffle method and be sure that the algorithm is correct and you'll always get random result.
As you are using swap as index with which you will be swapping the current value you can edit your Random number generator to generate random numbers between certain range (say 0 - coins.length) and then you can change your implementation to something like this
public void shake()
{
Coin temp;
for (int i = 0; i < coins.length; i++)
{
//int swap = Coin.RANDOM.nextInt(coins.length);
temp = coins[swap];
coins[swap] = coins[i];
coins[i] = temp;
System.out.print(coins[i] + ", ");
}
}
For the commented line in your code check THIS to update your random number generator to generate numbers between two values. Then each time you generate swap(index) between i+1 - coins.length and continue this till you fully exhaust the array. This ensures that you don't make a swap at the index the value for which you have already displayed. But I am not completely confident that this would indeed be a random shuffle as in the beginning of the loop you have more choices for the swap index then you would have sometime later in the loop and the shake is not completely random. This solution is only in case you want to strictly implement your own shake method without using the Collections.shuffle as #Tomek mentioned.
why don't you using Collections? its so simple to assign random indexes to each value in array or ArrayList.
Collections.shuffle(coins);//if coins is array
Collections.shuffle(Arrays.asList(coins));//if coins is an ArrayList
You might use Knuth's shuffling algorithm which rearranges the array so that a result is a uniformly random permutation. Algorithm is simple but works like a charm:
Iterate over array and in iteration i pick random integer swap between 0 and i
Swap array[i] and array[swap]
Note that in your implementation random is generated between 0 and 11, which doesn't seem to produce good shuffling.
Here is a code example with shuffling for array of integers:
import java.util.Random;
public class Test {
public static long SEED = System.currentTimeMillis();
public static Random RANDOM = new Random(SEED);
public static void shuffle(int[] numbers)
{
for (int i = 0; i < numbers.length; i++)
{
int swap = RANDOM.nextInt(i + 1);
int temp = numbers[swap];
numbers[swap] = numbers[i];
numbers[i] = temp;
}
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + ", ");
}
}
public static void main(String[] args) {
shuffle(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
}
}
Output for test run is:
5, 11, 6, 1, 3, 10, 9, 2, 4, 7, 8,
Use this method and pass your array in parameter
Collections.shuffle(arrayList);
This method return void so it will not give you a new list but as we know that array is passed as a reference type in Java so it will shuffle your array and save shuffled values in it. That's why you don't need any return type.
You can now use arraylist which is shuffled.
Sourse: https://stackoverflow.com/a/16112539/4291272

Categories

Resources