The problem I am facing is this one:
I have an array of doubles from which I want to keep the top k greater values.
I have seen some implementations involving Arrays.sort. For example in this example with relative issue it is suggested to use this approach.
Since I am only interested in the first k elements I have also experimented with MinMaxPriorityQueue. I have created a MinMaxPriorityQueue with a maximumSize:
Of course there is again autoboxing.
Builder<Comparable> builder = MinMaxPriorityQueue.maximumSize(maximumSize);
MinMaxPriorityQueue<Double> top2 = builder.create();
The problem is that the order is the ascending one that it's the opposite of the one I want. So I cannot use it this way.
To state the problem's real parameters my arrays is about 50 elements long and I am interested in up to the top k = 5 elements.
So is there any way to bypass this problem using the second approach? Should I stay with the first one even though I don't really need all elements sorted? Do you know if there is any significant difference in speed performance (I will have to use this in a lot of situations so that's where the speed is needed)? Is there any other solution I could use?
As for the performance, I know I can theoretically check it myself but I am a bit out of time and if someone have any solution I am happy to hear it (or read it anyway).
If you only have like 50 elements, as noted in my comment, just sort it and take the last k elements. It's 2 lines only:
public static double[] largests(double[] arr, int k) {
Arrays.sort(arr);
return Arrays.copyOfRange(arr, arr.length - k, arr.length);
}
This modifies (sorts) the original array. If you want your original array unmodified, you only need +1 line:
public static double[] largests2(double[] arr, int k) {
arr = Arrays.copyOf(arr, arr.length);
Arrays.sort(arr);
return Arrays.copyOfRange(arr, arr.length - k, arr.length);
}
You can use System.arraycopy on a sorted array:
double[] getMaxElements(double[] input, int k) {
double[] temp = Arrays.copyOf(input, input.length);
Arrays.sort(temp); // Sort a copy to keep input as it is since Arrays.sort works in-place.
return Arrays.copyOfRange(temp, temp.length - k, temp.length); // Fetch largest elements
}
For 50 elements, it is much faster to sort an array than to mess with generics and comparables.
I will write up an additional "fast" algorithm...
double[] getMaxElements2(double[] input, int k) {
double[] res = new double[k];
for (int i = 0; i < k; i++) res[i] = Double.NEGATIVE_INFINITY; // Make them as small as possible.
for (int j = 0; j < input.length; j++) // Look at every element
if (res[0] < input[j]) { // Keep the current element
res[0] = input[j];
Arrays.sort(res); // Keep the lowest kept element at res[0]
}
return res;
}
This is O(N*k*log(k)) while the first one is O(N*log(N)).
Related
I have an ArrayList of int.
The main program - calls a method to get a list of the sum of all (n member) combination of the members of the list. Where n can be anywhere between 2 - 6. E.g. Original List is {1,2,3,4,5}; Then the output should be {6, 7, 8, 8, 9, 10, 9, 10, 11, 12} where n = 3;
I am looking for the optimum way to do this. Right now, the way I have written the program (which is working) is without recursion. I have methods for all numbers i.e.
MyMethod2 -- gives me all the sum of 2 member combinations of MyArrayList
MyMethod3 -- gives me all the sum of 3 member combinations of MyArrayList
MyMethod4 -- gives me all the sum of 4 member combinations of MyArrayList
......
So, you can see that there is a lot of duplicate set of codes.
Also the way the program has currently been written (e.g. My Method3):
MyMethod3
ArrayList<Integer> sum = new ArrayList<Integer>();
for (i = 0; i < MyArrayList.size(); i++){
for (j = i + 1; j < MyArrayList.size(); j++){
for (k = j + 1; k < MyArrayList.size(); k++){
int total = MyArrayList.get(i) + MyArrayList.get(j) + MyArrayList.get(k);
sum.add(total);
}
}
}
return sum;
The MyMethod for n = 6, can become pretty long. Also "n" can change in the future.
Is there a better way to do this using recursion to minimize duplicate code, and using the number n as a variablefor the method call. Is there a standard package in Java that can help with recursion.
Adding the Code based on #Maertin suggestion - which worked for me
ArrayList<Integer> myArray = new ArrayList<Integer>();
myArray.add(5);
myArray.add(6);
myArray.add(4);
myArray.add(2);
myArray.add(1);
ArrayList<Integer> finalSumArray = combineTwoArrayList(3, myArray, myArray);
public static ArrayList<Integer> combineTwoArrayList(int n, ArrayList<Integer> origArray, ArrayList<Integer> finalSumArray) {
if (n == 1) return finalSumArray;
ArrayList<Integer> finalSumArray = new ArrayList<Integer>();
for (int i = 0; i < sumArray.size() - 1; i++){
for (int j = i + 1; j < origArray.size(); j++){
finalSumArray.add(sumArray.get(i) + origArray.get(j));
}
}
--n;
return combineTwoArrayList(n, origArray, finalSumArray);
}
You are correct in wanting to do this via recursion, because now, instead of having three separate methods, you could have one method with a parameter n for n-member combinations.
public int nCombinationSum( int n, int i, ArrayList<Integer> arr, ArrayList<Integer> sumArr) {
/* Gets n-combination sums and puts into sumArr
Input: n consecutive element combination, current index i in arr, and output ArrayList
Output: Gets n consecutive element combinations in arr from index i to index (i + n) and puts into sumArr
*/
//**BASE CASE**
//if index out of arr bounds
if( i + n > arr.size() )
return 0;
//**RECURSIVE CASE**
else {
//sum of values in arr from i to (i + n)
int currComboSum = 0;
for( int j = 0; j < n; j++ )
currComboSum += arr.get(j);
//adding sum to next element in resultant array
sumArr.add( currComboSum );
return nCombinationSum( n, i + 1, arr, sumArr );
}
}
USAGE
In your main method, you can call nCombinationSum and provide it with the kind of combination (n), starting index (in your case, 0), and arrayList (arr), and the arrayList you want to append the sums in (sumArr).
This also has the potential added benefit of allowing you to add any n-combination sum starting from a certain index. If you would like, you could add an end index as well, but this is fairly extended as it is.
EDIT: Please edit your question to reflect that you want the result to be an arrayList of sums, rather than the total sum. It is not clear right now.
Basically, what you want to do with recursion, in general, is to set a base case and a recursive case.
Your base case would be if your index is out of bounds, because you're going to call all elements from index i to i + n.
For the recursive case, use the algorithm below to account for each element in arr, then just keep returning the function with the next index value, and the function will continue running until it is out of bounds.
Algorithm
Getting sum of n-combination elements
Appending that sum into resultant array sumArr
Feel free to refer to the code above for reference.
You can use recursion. Basically, you should have only two for loops. (which is the code for two member combinations). When you compute 'total', pass each 'total' value to an ArrayList MyArrayList2. Now for MyMethod3, you use the elements of MyArrayList2 and the original ArrayList and find new 'total' values again and pass that to MyArrayList3. For MyMethod4, you use the elements of MyArrayList3 and the original ArrayList and find new 'total' values again and pass that to MyArrayList4.... ....
I have an array with positive integers in random order. A number x
from the list is given ,we need to find any two numbers in the list
having sum equal to x.Running time must be less than n^2.
{edit}
What I did is that , I put all the numbers less than half of x in one array and greater than half of x in another array and all greater than x are discarded and then the idea is that the required two numbers must from the two arrays (not from a single array) and by iterating I can get the two numbers.
Now for the worst case I am little confuse is that approach is good? or if anyone guide me something more better than this also can we achieve log n or n *log n ?
Your solution is both wrong, and in O(n^2).
It is wrong since consider x=5 and arr=[1,2,3,5] - the two numbers needed are from one array, not from both.
What if arr=[3,3,6], x=6, you will place both 3s in one list (not greater than x/2 for example), and will fail to find 3+3=6.
Your algorithm runs in O(n^2), because assume exactly half of the elements are greater than x1, and half are smaller than x. Then, the number of combinations you have to check are (n/2*n/2) /2 = n^2/8
To solve it in O(nlogn), think what happens if you sort the data, given a number arr[i], can you find efficiently if there is a number x-arr[i] in the now sorted array?
You can even enhance the above to O(n) average case by placing the elements in a hash-set, and now, given an number y, can you find efficiently if x-y is also in the set?
EDIT:
Stroked out parts are not relevant anymore since OP editted the question, added a new cause of concern instead.
(1) than x/2 in the editted question.
Here is O(n) solution for finding the first pair of indices of array which sums to the expected target. The solution will stop when it finds the first 2 indices that sum to target, if you need all the pairs that add up to target then instead of using int[] result, you can use ArrayList or even a Map, process the complete array and return it with all the pairs of indices. There is an obvious assumption that the Map's hashcode function is really good and there are not much collisions so that map operations perform in O(1) time.
import java.util.*;
public class Solution {
public static void main(String[] args) {
int[] array = new int[] {1,2,4,7,12,67,12,5,9,1,10};
System.out.println(Arrays.toString(sum(array, 68)));
}
public static int[] sum(int[] array, int target) {
int[] result = new int[2];
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// n iterations
for (int index = 0; index < array.length; index++) {
// constant
if (map.containsKey(target - array[index])) {
result[1] = index;
// constant
result[0] = map.get(target - array[index]);
return result;
}
// constant
map.put(array[index], index);
}
return result;
}
}
Here you go,
Sort the array using merge sort (Time complexity: n logn). Take two pointers/counters, say i & j, one starts from index 0 and another from n-1 (assuming n size of array is n).
if array[i]+array[j]=sum
return;
else if (array[i]+array[j]<sum) i++;
else j--;
Do it until i>j.
Overall time complexity: n logn
/* Time Complexity = O(n)-since HashMap operations take O(1) time*/
public static ArrayList<Integer> twoSum(int[] arr , int target){
if (arr == null){
throw new IllegalArgumentException();
}
ArrayList<Integer> targetHolder = new ArrayList<>();
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0 ; i < arr.length ; i++){
if (map.containsKey(arr[i])){
int index = map.get(arr[i]);
targetHolder.add(index+1);
targetHolder.add(i+1);
}
else{
map.put(target-arr[i], i);
}
}
return targetHolder;
}
public static void main(String[] args) {
int[] A = {1,2,3,4,5,6};
System.out.println(twoSum(A, 6));
}
}
public void function(int[] array, int sum){
for(int i = 0; i < array.length/2; i ++){
for(int j = array.length-1;; j--){
if(array[i]+array[j] < sum) break;
if(array[i]+array[j] == sum) System.out.println(array[i]+" + "+array[j]+" = "+sum);
}
}
}
I have an array int[] a= {5,3,1,2} and I want to make a method that picks out the "k" smallest numbers and return an array with the k smallest integers in ascending order. But when I run this code I get the output: [1,3].
I know the code skips some numbers somehow, but I cant twist my brain to fix it.
Any ideas?
EDIT: Without sorting the original array.
public static int[] nrSmallest(int[] a, int k) {
if(k <1 || k>a.length)
throw new IllegalArgumentException("must be at least 1");
int[] values= Arrays.copyOf(a, k);
Arrays.sort(values);
int counter= 0;
for(int i= k; i < a.length; i++) {
if(a[i]< values[counter]) {
for(int j= k-1; j> counter; j--) {
values[j]= values[j-1];
}
values[counter]= a[i];
}
if(counter< k) counter++;
}
return values;
}
EDIT: Joop Eggen solved this for me. Scroll down to see answer. Thanks!
As already pointed out in the comments, simply return a part of the sorted array.
public static int[] nrSmallest(int[] a, int k) {
// check parameters..
// copy all so we don't sort a
int[] sorted = Arrays.copyOf(a, a.length);
Arrays.sort(sorted);
return Arrays.copyOf(sorted, Math.min(k, sorted.length));
}
If you can't modify the original array, this is typically done with some type of priority queue, often a binary heap.
The method that you use in your example is O(n^2), and uses O(k) extra space. Sorting the original array and selecting the top k items is O(n log n). If you copy the array and then sort it, it uses O(n) extra space.
Using a heap is O(n log k), and requires O(k) extra space.
There is an O(n) solution that involves manipulating the original array (or making a copy of the array and manipulating it). See Quickselect.
My own testing shows that Quickselect is faster in the general case, but Heap select is faster when the number of items to be selected (k) is less than 1% of the total items (n). See my blog post, When theory meets practice. That comes in quite handy when selecting, say, the top 100 items from a list of two million.
(Corrected) To keep your code:
for (int i= k; i < a.length; i++) {
if (a[i] < values[counter]) { // Found small value
// Insert sorted
for (int j = k-1; j >= 0; j--) {
if (j == 0 || a[i] > values[j-1]) { // Insert pos
// Move greater ones up.
for (int m = k - 1; m > j; m--) {
values[m] = values[m - 1];
}
values[j] = a[i]; // Store
break; // Done
}
}
}
}
int[] values= Arrays.copyOf(a, k); this line is wrong. you are copying only k elements. but you are suppose to copy all elements and then sort the array.
First sort the array and then return the sorted part of the array upto k.
public static int[] nrSmallest(int[] a, int k) {
if(k <1 || k>a.length)
throw new IllegalArgumentException("must be at least 1");
Arrays.sort(a);
return Arrays.copyOf(a,k);
}
You could use the "pivoting" idea of quicksort,
The pivot denotes the "rank" of that number in the array, so your end goal would be having a pivot at index "k", which will result in a subarray less than the Kth element, in other words first K smallest numbers (not exactly sorted).
I want to insert an elements inside an array. I have one array1 of size 5(where all teh indexs are full).I have created one more array2 of size 6 and inserted all the elemnts of array1 inside array2. Now I want to insert an element in 3rd postion of array2 such that elements present in 3rd position will move to 4th, 4th will move to 5th and 5th will move to 6.
I have developed a program below. But getting string index out of bound exception. Please help me to fix this issue.
System.arraycopy(...) method is your helper. The rest you will do by yourself.
Sadly array is not the best for this task. I would recommend you to use List (ArrayList) in case if you don't have to bind yourself to arrays. And also you have the method toArray().
Here is a tutorial, but you will find numerous others: http://www.java-samples.com/showtutorial.php?tutorialid=234
I whipped you up something quick.
public int[] insert(final int[] array, final int index, final int...nums){
if(nums.length == 0){
return array;
}
int[] newArray = new int[array.length + nums.length];
int i = 0;
for(; i < index; i++){
newArray[i] = array[i];
}
for(int j = 0; j < nums.length; j++){
newArray[i + j] = nums[j];
}
for(; i < array.length; i++){
newArray[i + nums.length] = array[i];
}
return newArray;
}
I try to avoid System.arraycopy personally, as here you can see a better flow easier. It is more beneficial if you can see how it works, better than relying on someone else's code.
If You need insertions in the middle of array and change it's size dynamically - it's better to use LinkedList collection.
Say I have an array of 10 elements. Another part of my program determines I must remove the item at index 4.
What is the most efficient method to remove the item and shorten the array?
I wrote the following method, however it does not seem to work properly. Am I missing something, for example if the index to remove is 0? The method is called by sending an array and the index to be removed.
I realize there are Array Lists and other types of lists. However this is an assignment for a programming course and MUST use ARRAYs.
//Removes the index from the array and returns the array.
NumberTile[] removeAndTrim(NumberTile[] array, int index){
NumberTile[] save = array;
array = new NumberTile[save.length-1];
for (int i=0; i<index; i++){
array[i]=save[i];
}//end for loop
for (int j=index; j<save.length-1; j++){
array[j]=save[(j+1)];
}
return array;
}//end removeAndTrim
public NumberTile[] removeAndTrim(NumberTile[] a, int index){
NumberTile[] result = new NumberTile[a.length-1];
for (int i = 0; i < result.length; i++){
result[i] = a[((i < index) ? i : i + 1)];
}
return result;
}
Your most efficient way would be one loop / traversal and one array creation.
(Without using arraycopy that is).
Note: This doesn't alter the values of the parameter array at all, just returns a new one.
Your method is the most efficient possible assuming this is an exercise where you are not allowed to use libraries, utility classes like arraylist or System.arraycopy. Reasoning:
You can't avoid constructing a new array since a) you need one that is one element shorter and b) Java arrays are fixed size so you can't change the existing one
You need to copy length-1 elements in order to populate the new array. Doing this in a tight loop is the fastest you can do in pure Java.
As a style point, you should probably call the new array "result" or somthing similar and avoid the fiddling around with trying to save the array. This is pointless - you can't alter the input parameter.
Note that your function needs to be used as follows:
NumberTile[] newArray=removeAndTrim(oldArray,index);
NumberTile[] removeAndTrim(NumberTile[] array, int removeIndex) {
NumberTile[] newArray = new NumberTile[array.length - 1];
for (int i = 0; i < removeIndex; i++) {
newArray[i] = array[i];
}
for (int i = removeIndex + 1; i < array.length - 1; i++) {
newArray[i] = array[i + 1];
}
return newArray;
}