Terminated due to timeout for my hacker rank solution - java

Hello all please check the problemHackerRank Problem Statement
This is my solution for the above problem(link)
static int migratoryBirds(List<Integer> arr) {
int ar[]=new int[arr.size()];
for(int i=0;i<arr.size();i++){
ar[i] = Collections.frequency(arr,arr.get(i));
// ar[i] = obj.occuranceOfElement(arr,arr.get(i));
}
int maxAt = 0;
for (int i = 0; i < ar.length; i++) {
maxAt = ar[i] > ar[maxAt] ? i : maxAt;
}
return arr.get(maxAt);
}
my code is unable to handle when the array size is bigger,example 17623 elements in array.
Terminated due to timeout
The problem is in the second for loop which iterates over the array and gives me the index of the largest number in the array.Is there any other way that I could increase the performance.

Your problem is in this part:
for(int i = 0; i < arr.size(); i++)
ar[i] = Collections.frequency(arr, arr.get(i));
This is O(N²): Collections.frequency() iterates over whole list to calculate frequency for only one element. Manually, you can iterate over the list to calculate frequencey for all elements.
Moreover, ther're only 5 birds, so you need only 5 length array.
static int migratoryBirds(int[] arr) {
int max = 1;
int[] freq = new int[6];
for (int val : arr)
freq[val]++;
for (int i = 2; i < freq.length; i++)
max = freq[i] > freq[max] ? i : max;
return max;
}

Your problem is the call to Colletions.frequency, which is an O(N) operation. When you call it from inside a loop it becomes O(N²) and that consumes all your time.
Also, are you sure which implmentation of List you receive? You call list.get(i) which might also be O(N) if the implementation is a LinkedList.
The target of this exercise is to calculate the frequency of each value in one pass over the input. You need a place where you store and increase the number of occurrences for each value and you need to store the largest value of the input.
You have also skipped over a crucial part of the specification. The input has limits which makes solving the problem easier than you now think.

Here's another one:
static int migratoryBirds(List<Integer> arr) {
int freq[]=new int[6];
for(int i=0;i<arr.size();i++){
++freq[arr.get(i)];
}
int maxAt = 1;
for (int i = 2; i < freq.length; i++) {
if (freq[i] > freq[maxAt]) {
maxAt = i;
}
}
return maxAt;
}

We can determine the type number of the most common bird in one loop. This has the time complexity O(n).
static int migratoryBirds(int[] arr) {
int highestFrequency = 0;
int highestFrequencyBirdType = 0;
int[] frequencies = new int[5]; // there are 5 bird types
for (int birdType : arr) {
int frequency = ++frequencies[birdType - 1];
if (frequency > highestFrequency) {
highestFrequency = frequency;
highestFrequencyBirdType = birdType;
} else if (frequency == highestFrequency && birdType < highestFrequencyBirdType) {
highestFrequencyBirdType = birdType;
}
}
return highestFrequencyBirdType;
}
For each element in the array arr we update the overall highestFrequency and are storing the corresponding value representing the highestFrequencyBirdType . If two different bird types have the highest frequency the lower type (with the smallest ID number) is set.

Related

How to optimize the sorting algorithm?

I need to sort an array. I write code, i use insertion sort, but for big n this code work so slow. How optimize my code. May be there is another algorithm.
public void insertionSort(ArrayList<Integer> arrayList) {
int n = arrayList.size();
int in, out;
for(out = 1; out < n; out++)
{
int temp = arrayList.get(out);
in = out;
while (in > 0 && arrayList.get(in-1) > temp)
{
arrayList.set(in, arrayList.get(in-1));
in--;
}
arrayList.set(in,temp);
}
print(arrayList);
}
You can use counting sort instead of insertion sort. Because counting sort takes a linear time, but insertion sort at worst takes О(n^2)
Here is example of using counting sort:
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Main {
public static void print(int []a){
System.out.println(Arrays.toString(a));
}
public static void countingSort(int []a, int []b, int n, int k){
int []c = new int [k];
for(int i=0; i<k; i++)
c[i] = 0;
for(int j=0; j<n; j++){
c[a[j]] = c[a[j]]+1;
}
for(int i=1; i<k; i++){
c[i] = c[i]+c[i-1];
}
for(int j=n-1; j>=0; j--){
c[a[j]] = c[a[j]]-1;
b[c[a[j]]] = a[j];
}
for(int i=0; i<n; i++)
a[i] = b[i];
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Random ran = new Random();
int n = Integer.parseInt(in.nextLine());
int []a = new int[n];
int []b = new int[n];
int k = 5; // max value on the array
for(int i=0; i<n; i++)
a[i] = ran.nextInt(k);
print(a);
countingSort(a,b,n,k);
print(a);
}
}
You should look into QuickSort or MergeSort if you want faster sorting algorithms. Unlike InsertionSort (and SelectionSort), they are recursive, but still fairly easy to implement. You can find many examples if you look around on the internet.
As Anna stated above, counting sort can be a really good algorithm, considering you don't have a really large data set and the data is not sparse.
For example, an array of size 10k with 100 elements duplicated will have much better space efficiency than an array of size 10k with all unique elements and spread in a sparse fashion.
For example, the following array -> [5,5,4,...,2,2,1,1,5,6,7,8] will need a space of an array of size 8 (1 being the minimum and 8 being the maximum) while,
This array -> [5,100,6004,3248,45890,2384,128,8659,...,3892,128] will need a space of an array at least of size 45886 (5 being the minimum and 45890 being the maximum).
So, I'll suggest you use this algorithm when you know that the data set you have is evenly distributed within an acceptable range which won't make your program run out of memory. Otherwise you can go with something like quicksort or mergesort. That gets the work done just fine.
That being said, Anna's implementation of counting sort seemed a little over coded to me personally, so here's me sharing my implementation.
public int[] countSort(int[] nums) {
int min = nums[0], max = nums[0], counterLength, start = 0;
int[] counter;
// To dynamically allocate size to the counter.
// Also an essential step if there are negative elements in the input array.
// You can actively avoid this step if you know:
// 1. That the elements are not going to be negative.
// 2. The upper bound of the elements in the array.
for (int i : nums) {
if (i > max)
max = i;
else if (i < min)
min = i;
}
counterLength = max - min + 1;
counter = new int[counterLength];
for (int i : nums)
counter[i - min]++;
for (int i = 0; i < counterLength; i++) {
if (counter[i] > 0) {
int end = start + counter[i];
Arrays.fill(nums, start, end, i + min);
start = end;
}
}
return nums;
}

CodeFights - issues with time limit when writing FirstDuplicate method

I'm trying to solve the problem below from CodeFights. I left my answer in Java after the question. The code works for all the problems, except the last one. Time limit exception is reported. What could I do to make it run below 3000ms (CodeFights requirement)?
Note: Write a solution with O(n) time complexity and O(1) additional space complexity, since this is what you would be asked to do during a real interview.
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.
Input/Output
[time limit] 3000ms (java)
[input] array.integer a
Guaranteed constraints:
1 ≤ a.length ≤ 105,
1 ≤ a[i] ≤ a.length.
[output] integer
The element in a that occurs in the array more than once and has the minimal index for its second occurrence. If there are no such elements, return -1.
int storedLeastValue = -1;
int indexDistances = Integer.MAX_VALUE;
int indexPosition = Integer.MAX_VALUE;
for (int i = 0; i < a.length; i++)
{
int tempValue = a[i];
for (int j = i+1; j < a.length; j++) {
if(tempValue == a[j])
{
if(Math.abs(i-j) < indexDistances &&
j < indexPosition)
{
storedLeastValue = tempValue;
indexDistances = Math.abs(i-j);
indexPosition = j;
break;
}
}
}
}
return storedLeastValue;
Your solution has two nested for loops which implies O(n^2) while the question explicitly asks for O(n). Since you also have a space restriction you can't use an additional Set (which can provide a simple solution as well).
This question is good for people that have strong algorithms/graph theory background. The solution is sophisticated and includes finding an entry point for a cycle in a directed graph. If you're not familiar with these terms I'd recommend that you'll leave it and move to other questions.
Check this one, it's also O(n) , but without additional array.
int firstDuplicate(int[] a) {
if (a.length <= 1) return -1;
for (int i = 0; i < a.length; i++) {
int pos = Math.abs(a[i]) - 1;
if (a[pos] < 0) return pos + 1;
else a[pos] = -a[pos];
}
return -1;
}
The accepted answer does not work with the task.
It would work if the input array would indeed contain no bigger value than its length.
But it does, eg.: [5,5].
So, we have to define which number is the biggest.
int firstDuplicate(int[] a) {
int size = 0;
for(int i = 0; i < a.length; i++) {
if(a[i] > size) {
size = a[i];
}
}
int[] t = new int[size+1];
for(int i = 0; i < a.length; i++) {
if(t[a[i]] == 0) {
t[a[i]]++;
} else {
return a[i];
}
}
return -1;
}
What about this:
public static void main(String args[]) {
int [] a = new int[] {2, 3, 3, 1, 5, 2};
// Each element of cntarray will hold the number of occurrences of each potential number in the input (cntarray[n] = occurrences of n)
// Default initialization to zero's
int [] cntarray = new int[a.length + 1]; // need +1 in order to prevent index out of bounds errors, cntarray[0] is just an empty element
int min = -1;
for (int i=0;i < a.length ;i++) {
if (cntarray[a[i]] == 0) {
cntarray[a[i]]++;
} else {
min = a[i];
// no need to go further
break;
}
}
System.out.println(min);
}
You can store array values in hashSet. Check if value is already present in hashSet if not present then add it in hashSet else that will be your answer. Below is code which passes all test cases:-
int firstDuplicate(int[] a) {
HashSet<Integer> hashSet = new HashSet<>();
for(int i=0; i<a.length;i++){
if (! hashSet.contains(a[i])) {
hashSet.add(a[i]);
} else {
return a[i];
}
}
return -1;
}
My simple solution with a HashMap
int solution(int[] a) {
HashMap<Integer, Integer> countMap = new HashMap<Integer, Integer>();
int min = -1;
for (int i=0; i < a.length; i++) {
if (!(countMap.containsKey(a[i]))) {
countMap.put(a[i],1);
}
else {
return a[i];
}
}
return min;
}
Solution is very simple:
Create a hashset
keep iterating over the array
if element is already not in the set, add it.
else element will be in the set, then it mean this is minimal index of first/second the duplicate
int solution(int[] a) {
HashSet<Integer> set = new HashSet<>();
for(int i=0; i<a.length; i++){
if(set.contains(a[i])){
// as soon as minimal index duplicate found where first one was already in the set, return it
return a[i];
}
set.add(a[i]);
}
return -1;
}
A good answer for this exercise can be found here - https://forum.thecoders.org/t/an-interesting-coding-problem-in-codefights/163 - Everything is done in-place, and it has O(1) solution.

Generate random numbers without duplicates using arrays only

I want to fill an array of size X with random integers from 0 to X with no duplicates. The catch is I must only use arrays to store the collections of int, no ArrayLists. How do I go about implementing this?
I don't understand why I can't seem to get this. But this is my most recent bit of code that fills the list but allows for duplicates.
System.out.print("Zero up to but excluding ");
int limit = scanner.nextInt();
// create index the size of the limit
int [] index = new int[limit];
for(int fill=0;fill<limit;fill+=1){
index[fill] = (limit);
}
int randomNumber = 0;
Random rand = new Random();
int [] randoms = new int[limit];
boolean flag = true;
// CODE TO NOT PRINT DOUBLES
for (int z=0;z<limit;z+=1){
randomNumber = rand.nextInt(limit);
int i=0;
while (i<limit){
if (index[i] == randomNumber){
flag = true;
}
else {
flag = false;
break;
}
i+=1;
}
if (flag == false){
randoms[z] = randomNumber;
index[z] = randomNumber;
}
}
System.out.println("Randoms: "+java.util.Arrays.toString(randoms));
Here's one way to do it:
Create an array of length N
Fill it from 0 to N-1
Run a for loop and swap randomly 2 indices
Code:
// Step 1
int N = 10;
int[] array = new int[N];
// Step 2
for(int i=0; i < N; i++)
array[i] = i;
// Step 3
for(int i=0; i < N; i++) {
int randIndex = (int) (Math.random() * N);
int tmp = array[i];
array[i] = array[randIndex];
array[randIndex] = tmp;
}
Why not rephrase the problem to shuffling an array of integers. First fill the array monotonically with the numbers 0 to X. Then use the Random() function to select one of the X numbers to exchange with the number in position 0. Repeat as many times as you may like. Done.
Here is your bug:
while (i<limit){
if (index[i] == randomNumber){
flag = true;
}
else {flag = false;break;} <--- rest of the array is skipped
i+=1;
}
after you generated a new number, you start to check for equality , however once you find that randomNumber!=index[i] (else statement) you break out of the while. look this: actual array is 3,4,5,1 your new number is 5, you compare it to 3 just to find out that they different so flag is set to false and break out happens.
Consider using another array filled with elements in order from 0 to X. Then, with this array, shuffle the elements around. How do you go about this? Use a loop to traverse through every single element of the array, and for each iteration, choose a random number from 0 to array.length - 1 and switch the elements at the index you're currently on and the random index. This is how it would look like,
In your main, you would have an array initialized by doing this,
int[] arr = new int[10];//10 can be interchangeable with any other number
for(int i = 0; i < arr.length; i++){
arr[i] = i;
}
shuffleArray(arr);
And the shuffle method would look like this,
public int[] shuffleArray(int[] arr){
Random rand = new Random();
for(int i = 0; i < arr.length; i++){
int r = rand.nextInt(arr.length);//generate a random number from 0 to X
int k = arr[i];
arr[i] = arr[r];
arr[r] = k;
}
}

Where does this program get its numbers from, and why is this caused by increasing 1 array size? (Java)

This program simply is supposed to eliminate duplicates from an array. However, the second for loop in the eliminate method was throwing an out of bounds exception. I was looking and couldnt see how that could be, so I figured I would increase the array size by 1 so that I would get it to work with the only downside being an extra 0 tacked onto the end.
To my surprise, when I increased tracker[]'s size from 10 to 11, the program prints out every number from 0 to 9 even if I dont imput most of those numbers. Where do those numbers come from, and why am I having this problem?
import java.util.*;
class nodupes
{
public static void main(String[] args)
{
int[] dataset = new int[10];
//getting the numbers
for (int i = 0; i <= 9 ; i++)
{
Scanner input = new Scanner(System.in);
System.out.println("Enter a one digit number");
dataset[i] = input.nextInt();
}
int[] answer = (eliminateduplicates(dataset));
System.out.println(Arrays.toString(answer));
}
public static int[] eliminateduplicates(int[] numbers)
{
boolean[] tracker = new boolean[11];
int arraysize = 1;
for(int k = 0; k <= 9; k++)
{
if(tracker[numbers[k]] == false)
{
arraysize++;
tracker[numbers[k]] = true;
}
}
int[] singles = new int[arraysize];
for(int l = 0; l <= arraysize; l++)
{
if(tracker[l] == true)
{
singles[l] = l;
}
}
return singles;
}
}
The exception was occuring at this part
if(tracker[l] == true)
but only when trackers size was 10. At 11 it just prints [0,1,2,3,4,5,6,7,8,9]
EDIT: The arraysize = 1 was a hold over from debugging, originally it was at 0
EDIT: Fixed it up, but now there is a 0 at the end, even though the array should be getting completely filled.
public static int[] eliminateduplicates(int[] numbers)
{
boolean[] tracker = new boolean[10];
int arraysize = 0;
for(int k = 0; k < numbers.length; k++)
{
if(tracker[numbers[k]] == false)
{
arraysize++;
tracker[numbers[k]] = true;
}
}
int[] singles = new int[arraysize];
int counter = 0;
for(int l = 0; l < arraysize; l++)
{
if(tracker[l] == true)
{
singles[counter] = l;
counter++;
}
}
return singles;
}
Since arrays start at 0, your arraysize will be one larger than the number of unique numbers, so your final loop goes through one too many times. In other words "l" (letter l -- try using a different variable name) will get to 11 if you have 10 unique numbers and tracker only has item 0-10, thus an out of bounds exception. Try changing the declaration to
int arraysize = 0;
Once again defeated by <=
for(int l = 0; l <= arraysize; l++)
An array size of 10 means 0-9, this loop will go 0-10
For where the numbers are coming from,
singles[l] = l;
is assigning the count values into singles fields, so singles[1] is assigned 1, etc.
Edit like 20 because I should really be asleep. Realizing I probably just did your homework for you so I removed the code.
arraySize should start at 0, because you start with no numbers and begin to add to this size as you find duplicates. Assuming there was only 1 number repeated ten times, you would've created an array of size 2 to store 1 number. int arraysize = 0;
Your first for loop should loop through numbers, so it makes sense to use the length of numbers in the loop constraint. for( int i = 0; i < numbers.length; i ++)
For the second for loop: you need to traverse the entire tracker array, so might as well use the length for that (tracker.length). Fewer magic numbers is always a good thing. You also need another variables to keep track of your place in the singles array. If numbers was an array of 10 9s, then only tracker[9] would be true, but this should be placed in singles[0]. Again, bad job from me of explaining but it's hard without diagrams.
Derp derp, I feel like being nice/going to bed, so voila, the code I used (it worked the one time I tried to test it):
public static int[] eliminateduplicates(int[] numbers)
{
boolean[] tracker = new boolean[10];
int arraysize = 0;
for(int k = 0; k < numbers.length; k++)
{
if(tracker[numbers[k]] == false)
{
arraysize++;
tracker[numbers[k]] = true;
}
}
int[] singles = new int[arraysize];
for(int l = 0, count = 0; l < tracker.length; l++)
{
if(tracker[l] == true)
{
singles[count++] = l;
}
}
return singles;
}
I feel you are doing too much of processing for getting a no duplicate, if you dont have the restriction of not using Collections then you can try this
public class NoDupes {
public static void main(String[] args) {
Integer[] dataset = new Integer[10];
for (int i = 0; i < 10; i++) {
Scanner input = new Scanner(System.in);
System.out.println("Enter a one digit number");
dataset[i] = input.nextInt();
}
Integer[] arr = eliminateduplicates(dataset);
for (Integer integer : arr) {
System.out.println(integer);
}
}
public static Integer[] eliminateduplicates(Integer[] numbers) {
return new HashSet<Integer>(Arrays.asList(numbers)).toArray(new Integer[]{});
}
}
To answer your question your final loop is going one index more than the size.
The range of valid indexes in an array in Java is [0, SIZE), ie. from 0 up to arraysize-1.
The reason you're getting the exception is because in your loop you're iterating from 0 to arraysize inclusively, 1 index too far:
for(int l = 0; l <= arraysize; l++)
Therefore when you get to if(tracker[l] == true) in the last iteration, l will equal arraysize and tracker[l] will be outside the bounds of the array. You can easily fix this by changing <= to < in your for loop condition.
The reason that the problem goes away when the size of your array is changed from 10 to 11 has to do with arraysize being incremented up to 10 in the for loop above the one causing the problems. This time, singles[10] is a valid element in the array since the range of indexes in your array is now [0, 11).
EDIT: Actually arraysize has the potential to be incremented to 11, I thought it was initialised to 0 in which case it would only get to 10. Either way the above is still valid; the last index you try and access in your array must be 1 less than the length of your array in order to avoid the exception you're getting, since arrays are zero-based. So yeah, long story short, <= should be <.

Generating Random Permutation Uniformly in Java

Anyone know of a fast/the fastest way to generate a random permutation of a list of integers in Java. For example if I want a random permutation of length five an answer would be 1 5 4 2 3, where each of the 5! possibilities is equally likely.
My thoughts on how to tackle this are to run a method which generates random real numbers in an array of desired length and then sorts them returning the index i.e. 0.712 0.314 0.42 0.69 0.1 would return a permutation of 5 2 3 4 1. I think this is possible to run in O(n^2) and at the moment my code is running in approximately O(n^3) and is a large proportion of the running time of my program at the moment. Theoretically this seems OK but I'm not sure about it in practice.
Have you tried the following?
Collections.shuffle(list)
This iterates through each element, swapping that element with a random remaining element. This has a O(n) time complexity.
If the purpose is just to generate a random permutation, I don't really understand the need for sorting. The following code runs in linear time as far as I can tell
public static int[] getRandomPermutation (int length){
// initialize array and fill it with {0,1,2...}
int[] array = new int[length];
for(int i = 0; i < array.length; i++)
array[i] = i;
for(int i = 0; i < length; i++){
// randomly chosen position in array whose element
// will be swapped with the element in position i
// note that when i = 0, any position can chosen (0 thru length-1)
// when i = 1, only positions 1 through length -1
// NOTE: r is an instance of java.util.Random
int ran = i + r.nextInt (length-i);
// perform swap
int temp = array[i];
array[i] = array[ran];
array[ran] = temp;
}
return array;
}
And here is some code to test it:
public static void testGetRandomPermutation () {
int length =4; // length of arrays to construct
// This code tests the DISTRIBUTIONAL PROPERTIES
ArrayList<Integer> counts = new ArrayList <Integer> (); // filled with Integer
ArrayList<int[]> arrays = new ArrayList <int[]> (); // filled with int[]
int T = 1000000; // number of trials
for (int t = 0; t < T; t++) {
int[] perm = getRandomPermutation(length);
// System.out.println (getString (perm));
boolean matchFound = false;
for(int j = 0; j < arrays.size(); j++) {
if(equals(perm,arrays.get(j))) {
//System.out.println ("match found!");
matchFound = true;
// increment value of count in corresponding position of count list
counts.set(j, Integer.valueOf(counts.get(j).intValue()+1));
break;
}
}
if (!matchFound) {
arrays.add(perm);
counts.add(Integer.valueOf(1));
}
}
for(int i = 0; i < arrays.size(); i++){
System.out.println (getString (arrays.get (i)));
System.out.println ("frequency: " + counts.get (i).intValue ());
}
// Now let's test the speed
T = 500000; // trials per array length n
// n will the the length of the arrays
double[] times = new double[97];
for(int n = 3; n < 100; n++){
long beginTime = System.currentTimeMillis();
for(int t = 0; t < T; t++){
int[] perm = getRandomPermutation(n);
}
long endTime = System.currentTimeMillis();
times[n-3] = (double)(endTime-beginTime);
System.out.println("time to make "+T+" random permutations of length "+n+" : "+ (endTime-beginTime));
}
// Plotter.plot(new double[][]{times});
}
There is an O(n) Shuffle method that is easy to implement.
Just generate random number between 0 and n! - 1 and use
the algorithm I provided elsewhere (to generate permutation by its rank).

Categories