A interviewer wrecked my life with this sort question today.
You have an array of a million integers and you need to sort them by remainder.
1. You don't know what integer they are going to divide by.
2. You can't use classes such as a Comparator to help.
3. Loop as little as possible.
4. Keep in mind to conserve memory.
For instance
int[] ints = {5434, 3454, 2, 0, 356, 896, 7324, 888, 99, 78365, 111};
int divider = 27;
Would be
int[] int2 = {0, 2, 111, 356, 896, 5434, 7324, 78365, 99, 888, 3454};
The method I came up with loops = divider / 2.
It works but if the divider is 250 then it loops 125 times.
public void sortByMod(int[] millionInts, int divideBy) {
long time = System.nanoTime();
int[] b = new int[millionInts.length];
int remainder, remainderMin = 0, remainderMax = divideBy, positionMin = 0, positionMax = millionInts.length - 1;
for (int i = 0; i < millionInts.length;) {
for (int j = 0; j < millionInts.length; j++) {
remainder = millionInts[j] % divideBy;
if (remainder == remainderMin) {
b[positionMin] = millionInts[j];
positionMin++;
i++;
} else if (remainder == remainderMax) {
b[positionMax] = millionInts[j];
positionMax--;
i++;
}
}
remainderMax--;
remainderMin++;
}
System.out.println("time = " + (System.nanoTime() - time));
System.out.println("loopcount = " + remainderMin);
}
I wrote another method that can do it in 2 loops but its confusing to read.
It violates the memory constraint but is extremely fast.
public void sortByModPro(int[] millionInts, int divideBy) {
int[] range = new int[divideBy];
int[] remainders = new int[millionInts.length];
int[] newArray = new int[millionInts.length];
long times = System.nanoTime();
for (int i = 0; i < millionInts.length; i++) {
remainders[i] = millionInts[i] % divideBy;
range[millionInts[i] % divideBy]++;
}
for (int i = range.length - 1, past = millionInts.length; i >= 0; i--) {
range[i] = past - range[i];
past = range[i];
}
for (int i = 0; i < millionInts.length; i++) {
newArray[range[remainders[i]]] = millionInts[i];
range[remainders[i]]++;
}
System.out.println("time = " + (System.nanoTime() - times));
}
How would you do this with 1 loop?
Speed > Memory
You can loop only once by using a bunch of buckets, one for each remainder. Simply dump the numbers in the buckets based on their remainder and then merge the buckets. Of course this violates the memory constraint.
Use your array to hold the buckets
The problem with the buckets is that you need to at least add a reference to each item in the array. What you could do to avoid that is partition the array into buckets and maintain a reference to the start and end index of each bucket. Of course this uses some memory, but the divideBy parameter should be rather small, right?
So, here's some pseudocode:
// init each bucket with 0 elements
for (remainder=0; remainder<divideBy; remainder++) {
buckets = {
start : 0, // startIndex in the array
end: 0, // the index after the last item actually placed in the bucket
count: 0 // how many items should be in the bucket
}
}
// count how many elements fit in each bucket
for (i=0; i<N; i++) {
buckets[array[i]%divideBy].count++;
}
// init the start and end points of each bucket
elementsCounted=0;
for (remainder=0; remainder<divideBy; remainder++) {
buckets[remainder].start = elementsCounted;
buckets[remainder].end = elementsCounted;
elementsCounted += buckets[remainder].count;
}
// at this point each bucket starts where it should in the array, but has no elements
// loop through the array and place items in the right bucket by swapping them
for (i=0; i<N; i++) {
remainder = array[i]%divideBy;
if (i < buckets[remainder].start || i >= buckets[remainder].end) {
// array[i] is in the wrong bucket, swap it at the end of the right bucket
swap(array[i], array[buckets[remainder].end]);
buckets[remainder].end++;
i--;
}
}
// everything is in the right place
You will note that there is an i-- in the final for, so it technically could go on forever. That is not the case, i will stay in place only if the array[i] is not in the right place. Each iteration will either place an element in the correct position or advance to the next position if the element is not in the correct position. All in all, it will iterate at most 2N times.
Total time complexity: O(3N+divideBy) = O(N + divideBy)
Total extra space used: divideBy*sizeof(bucket) = divideBy*12
Related
I need to run an array that's sorted backwards (I.E. 100, 99, 98, 97 . . . . 3, 2, 1, 0, highest to lowest) through a bucket sort that will sort it lowest to highest. The code that generates the array looks like this:
int n = 100;//Decides how large the arrays fed to the sorts are, minimum value of 100
int k = n - 1;
int howMany = 10;//Decides how many times the sorts are timed whenever the program is run
int[] baseArray = new int[n];
//Loops entire thing as many times as howMany dictates, will refer to it as PRIME LOOP
for (int m = 0; m < howMany; m++) {
for (int i = 0; i < n; i++) //Generates array that's sorted backwards
{
baseArray[i] = k;
k--;
}
int[] bucketArray = new int[n];
for (int i = 0; i < n; i++) {
bucketArray[i] = baseArray[i];
}
bucketSort(bucketArray); //Sends the array to bucket sort (This is line 218)**************
}
Here's the actual bucket sort:
//Bucket Sort
public static void bucketSort(int[] input) {
// get hash codes
final int[] code = hash(input);
// create and initialize buckets to ArrayList: O(n)
List<Integer>[] buckets = new List[code[1]];
for (int i = 0; i < code[1]; i++) {
buckets[i] = new ArrayList();
}
// distribute data into buckets: O(n)
for (int i : input) {
buckets[hash(i, code)].add(i); //This is line 349*******************************************
}
// sort each bucket O(n)
for (List bucket : buckets) {
Collections.sort(bucket);
}
int ndx = 0;
// merge the buckets: O(n)
for (int b = 0; b < buckets.length; b++) {
for (int v : buckets[b]) {
input[ndx++] = v;
}
}
}
private static int[] hash(int[] input) {
int m = input[0];
for (int i = 1; i < input.length; i++) {
if (m < input[i]) {
m = input[i];
}
}
return new int[] { m, (int) Math.sqrt(input.length) };
}
private static int hash(int i, int[] code) {
return (int) ((double) i / code[0] * (code[1] - 1));
}
The first time the code goes through the for-loop (prime loop) bucket sort spits out the array with it properly sorted lowest to highest. However, without fail the second time it goes through the prime loop it gives me an ArrayIndexOutOfBoundsException, specifically,
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 18 out of bounds for length 10
at SeniorResearch.Final_Project_Driver.bucketSort(Final_Project_Driver.java:349)
at SeniorResearch.Final_Project_Driver.main(Final_Project_Driver.java:218)
(I marked out the mentioned lines above)
Can anyone help me figure out why this is happening? What's changing from PRIME LOOP 1 to PRIME LOOP 2 that's causing an ArrayIndexOutOfBoundsException in the bucket sort and how do I fix it?
In your bucketSort method at you think you are using final int[] code = hash(input); to calculate the number of buckets, but in reality, you are calculating the hash of your array.
So what you have to do is calculate the number of different hash codes for your array elements.
Use the method which calculates the hash of a single integer, then count how many different hashes you've got, then add each integer into the "hash-bucket", and so on...
My question is if I input two maxvalue numbers in array, ex. 100, 10, 100. How can i get the output to print both index numbers?
The output expected would be 100 on index 1 and index 3
the index has 1 added as I want the index to start at one not zero
Add this to initialization.
HashSet<Integer> maxIndices = new HashSet<Integer>();
Make a second pass through the mileage array and add any max values to the HashSet.
The other option is to use a HashMap where the first integer is the mileage and the second value is the positive integer of how many have been found.
Because it makes only one pass, it may be faster even though you are counting every mileage, not just the one that ends up being the largest. Whether it is will be, of course, dependent upon the data, the compiler, and environmental conditions at the time of execution.
For your return value, you'll either need a custom POJO Java Bean or you can use Pair<> or Tuple<>. (See Using Pairs or 2-tuples in Java.)
Just simple Java using a series of for loops. The below method will return a 2D int array with each row consisting of two columns, the highest array value and its' respective array index number.
public int[][] getAllMaxValues(int[] allValues) {
int maxVal = 0;
int counter = 0;
// Get the Max value in array...
for (int i = 0; i < allValues.length; i++) {
if (allValues[i] > maxVal) {
maxVal = allValues[i];
}
}
// How many of the same max values are there?
for (int i = 0; i < allValues.length; i++) {
if (allValues[i] == maxVal) {
counter++;
}
}
// Place all the max values and their respective
// indexes into a 2D int array...
int[][] result = new int[counter][2];
counter = 0;
for (int i = 0; i < allValues.length; i++) {
if (allValues[i] == maxVal) {
result[counter][0] = maxVal;
result[counter][1] = i;
counter++;
}
}
// Return the 2D Array.
return result;
}
How you might use this method:
int[] allMiles = {100, 10, 100, 60, 20, 100, 34, 66, 74};
int[][] a = getAllMaxValues(allMiles);
for (int i = 0; i < a.length; i++) {
System.out.println("Array max value of " + a[i][0] +
" is located at index: " + a[i][1]);
}
Console window would display:
Array max value of 100 is located at index: 0
Array max value of 100 is located at index: 2
Array max value of 100 is located at index: 5
Written this code, would like to get better approach using any algorithm to find missing numbers from an sorted or unsorted array. If its an unsorted array, i would sort and execute the following.
private static void identifyMissingValues(Integer[] ar) {
for(int i = 0; i < (ar.length - 1); i++) {
int next = ar[i + 1];
int current = ar[i];
if((next - current) > 1) {
System.out.println("Missing Value : " + (current + 1));
}
}
}
Any code faster or better than this, please suggest.
Any code faster or better than this, please suggest.
No there is no such thing - you cannot improve on an O(n) algorithm if every element must be visited.
Use BitSet instead of sorting.
int[] ar = {7, 2, 6, 8, 10, 4, 3, 2};
int min = IntStream.of(ar).min().getAsInt();
BitSet b = new BitSet();
for (int i : ar)
b.set(i - min);
int i = 0;
while ((i = b.nextClearBit(i + 1)) < b.length())
System.out.println(i + min);
result
5
9
Sorting the array would take O(n*log(n)).
You can do better if you add all the elements of the array to a HashSet (O(n)) running time, and then check for each number between 0 and ar.length - 1 (or whatever the relevant range is) whether the HashSet contains that number. This would take O(n) time.
Your approach is good, but I added something more for more than one numbers are missing..
eg : ar={1,2,4,6,10} // Sorted Array
private static void identifyMissingValues(Integer[] ar) {
for (int i = 0; i < (ar.length - 1); i++) {
int next = ar[i + 1];
int current = ar[i];
if ((next - current) > 1) {
for (int ind = 1; ind < next - current; ind++)
System.out.println("Missing Value : " + (current + ind));
}
}
}
Output is,
Missing Value : 3
Missing Value : 5
Missing Value : 7
Missing Value : 8
Missing Value : 9
Can I know the Input and Expected output number series ?
According to your code i feel the series should be a difference of 1,If i'm not wrong.
So you have an array of n elements which starts with an integer i and contains all integers from i to i+n is that right? Eg:
arr = [1,2,3,4,5]
So, the sum of all numbers in the array should be the sum of numbers from i to i+n.
Eg: sum(arr) = 1+2+3+4+5 = 15
The formula for the sum of numbers 1 to n is n(n+1)/2
So you can have a for loop as:
int counter = 0;
for(Integer i : integers)
counter += i
To get the sum of numbers in your array.
If your array starts at one, you check whether the counter variable equals n(n+1)/2, where n is the length of your array.
If your array doesn't start at one, for example arr = [78, 79, 80, 81] then you need to tweak this approach a little, but I'm sure you can figure it.
You can do:
Set<Integer> mySet = new TreeSet<Integer>(Arrays.asList(ar));
int min = mySet.first();
for (int i = 0; i < mySet.size(); i++) {
int number = min + i;
if (!mySet.contains(number)) {
System.out.println ("Missing: " + number);
i--;
}
}
Integer [] list = new Integer[]{1, 12, 85, 6, 10};
Integer previous = null;
Arrays.sort(list);
System.out.println(list);
for(int index = 0; index < list.length; index++){
if(previous == null){
previous = (Integer) list[index];
continue;
}
Integer next = previous + 1;
if(((Integer) list[index] - previous) > 1){
System.out.println("Missing value " + next);
index--;
}
previous = next;
}
this is the question, and yes it is homework, so I don't necessarily want anyone to "do it" for me; I just need suggestions: Maximum sum: Design a linear algorithm that finds a contiguous subsequence of at most M in a sequence of N long integers that has the highest sum among all such subsequences. Implement your algorithm, and confirm that the order of growth of its running time is linear.
I think that the best way to design this program would be to use nested for loops, but because the algorithm must be linear, I cannot do that. So, I decided to approach the problem by making separate for loops (instead of nested ones).
However, I'm really not sure where to start. The values will range from -99 to 99 (as per the range of my random number generating program).
This is what I have so far (not much):
public class MaxSum {
public static void main(String[] args){
int M = Integer.parseInt(args[0]);
int N = StdIn.readInt();
long[] a = new long[N];
for (int i = 0; i < N; i++) {
a[i] = StdIn.readLong();}}}
if M were a constant, this wouldn't be so difficult. For example, if M==3:
public class MaxSum2 {
public static void main(String[] args){
int N = StdIn.readInt(); //read size for array
long[] a = new long[N]; //create array of size N
for (int i = 0; i < N; i++) { //go through values of array
a[i] = StdIn.readLong();} //read in values and assign them to
//array indices
long p = a[0] + a[1] + a[2]; //start off with first 3 indices
for (int i =0; i<N-4; i++)
{if ((a[i]+a[i+1]+a[1+2])>=p) {p=(a[i]+a[i+1]+a[1+2]);}}
//if sum of values is greater than p, p becomes that sum
for (int i =0; i<N-4; i++) //prints the subsequence that equals p
{if ((a[i]+a[i+1]+a[1+2])==p) {StdOut.println((a[i]+a[i+1]+a[1+2]));}}}}
If I must, I think MaxSum2 will be acceptable for my lab report (sadly, they don't expect much). However, I'd really like to make a general program, one that takes into consideration the possibility that, say, there could be only one positive value for the array, meaning that adding the others to it would only reduce it's value; Or if M were to equal 5, but the highest sum is a subsequence of the length 3, then I would want it to print that smaller subsequence that has the actual maximum sum.
I also think as a novice programmer, this is something I Should learn to do. Oh and although it will probably be acceptable, I don't think I'm supposed to use stacks or queues because we haven't actually covered that in class yet.
Here is my version, adapted from Petar Minchev's code and with an important addition that allows this program to work for an array of numbers with all negative values.
public class MaxSum4 {
public static void main(String[] args)
{Stopwatch banana = new Stopwatch(); //stopwatch object for runtime data.
long sum = 0;
int currentStart = 0;
long bestSum = 0;
int bestStart = 0;
int bestEnd = 0;
int M = Integer.parseInt(args[0]); // read in highest possible length of
//subsequence from command line argument.
int N = StdIn.readInt(); //read in length of array
long[] a = new long[N];
for (int i = 0; i < N; i++) {//read in values from standard input
a[i] = StdIn.readLong();}//and assign those values to array
long negBuff = a[0];
for (int i = 0; i < N; i++) { //go through values of array to find
//largest sum (bestSum)
sum += a[i]; //and updates values. note bestSum, bestStart,
// and bestEnd updated
if (sum > bestSum) { //only when sum>bestSum
bestSum = sum;
bestStart = currentStart;
bestEnd = i; }
if (sum < 0) { //in case sum<0, skip to next iteration, reseting sum=0
sum = 0; //and update currentStart
currentStart = i + 1;
continue; }
if (i - currentStart + 1 == M) { //checks if sequence length becomes equal
//to M.
do { //updates sum and currentStart
sum -= a[currentStart];
currentStart++;
} while ((sum < 0 || a[currentStart] < 0) && (currentStart <= i));
//if sum or a[currentStart]
} //is less than 0 and currentStart<=i,
} //update sum and currentStart again
if(bestSum==0){ //checks to see if bestSum==0, which is the case if
//all values are negative
for (int i=0;i<N;i++){ //goes through values of array
//to find largest value
if (a[i] >= negBuff) {negBuff=a[i];
bestSum=negBuff; bestStart=i; bestEnd=i;}}}
//updates bestSum, bestStart, and bestEnd
StdOut.print("best subsequence is from
a[" + bestStart + "] to a[" + bestEnd + "]: ");
for (int i = bestStart; i<=bestEnd; i++)
{
StdOut.print(a[i]+ " "); //prints sequence
}
StdOut.println();
StdOut.println(banana.elapsedTime());}}//prints elapsed time
also, did this little trace for Petar's code:
trace for a small array
M=2
array: length 5
index value
0 -2
1 2
2 3
3 10
4 1
for the for-loop central to program:
i = 0 sum = 0 + -2 = -2
sum>bestSum? no
sum<0? yes so sum=0, currentStart = 0(i)+1 = 1,
and continue loop with next value of i
i = 1 sum = 0 + 2 = 2
sum>bestSum? yes so bestSum=2 and bestStart=currentStart=1 and bestEnd=1=1
sum<0? no
1(i)-1(currentStart)+1==M? 1-1+1=1 so no
i = 2 sum = 2+3 = 5
sum>bestSum? yes so bestSum=5, bestStart=currentStart=1, and bestEnd=2
sum<0? no
2(i)-1(currentStart)+1=M? 2-1+1=2 so yes:
sum = sum-a[1(curentstart)] =5-2=3. currentStart++=2.
(sum<0 || a[currentStart]<0)? no
i = 3 sum=3+10=13
sum>bestSum? yes so bestSum=13 and bestStart=currentStart=2 and bestEnd=3
sum<0? no
3(i)-2(currentStart)+1=M? 3-2+1=2 so yes:
sum = sum-a[1(curentstart)] =13-3=10. currentStart++=3.
(sum<0 || a[currentStart]<0)? no
i = 4 sum=10+1=11
sum>bestSum? no
sum<0? no
4(i)-3(currentStart)+1==M? yes but changes to sum and currentStart now are
irrelevent as loop terminates
Thanks again! Just wanted to post a final answer and I was slightly proud for catching the all negative thing.
Each element is looked at most twice (one time in the outer loop, and one time in the while loop).
O(2N) = O(N)
Explanation: each element is added to the current sum. When the sum goes below zero, it is reset to zero. When we hit M length sequence, we try to remove elements from the beginning, until the sum is > 0 and there are no negative elements in the beginning of it.
By the way, when all elements are < 0 inside the array, you should take only the largest negative number. This is a special edge case which I haven't written below.
Beware of bugs in the below code - it only illustrates the idea. I haven't run it.
int sum = 0;
int currentStart = 0;
int bestSum = 0;
int bestStart = 0;
int bestEnd = 0;
for (int i = 0; i < N; i++) {
sum += a[i];
if (sum > bestSum) {
bestSum = sum;
bestStart = currentStart;
bestEnd = i;
}
if (sum < 0) {
sum = 0;
currentStart = i + 1;
continue;
}
//Our sequence length has become equal to M
if (i - currentStart + 1 == M) {
do {
sum -= a[currentStart];
currentStart++;
} while ((sum < 0 || a[currentStart] < 0) && (currentStart <= i));
}
}
I think what you are looking for is discussed in detail here
Find the subsequence with largest sum of elements in an array
I have explained 2 different solutions to resolve this problem with O(N) - linear time.
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;