Problem in implementing build-heap method for min-heap - java

I am trying to make min-heap algorithm and i am able to get insert,delete,pop ,trickleUp and trickleDown methods to work ,but i am having problems with the constructor which is to make use of build-heap algorithm.
The method is simple as it just wants to build a new heap from an array .Further,i am going to implement heap sort algorithm which requires heapify() to maintain the heap property.But,i am stuck if the constructor and so not sure if my heapify() is correct or not.Once constructor works,heap sort will become easy to implement.
/** Construct a new heap from an array of unsorted data.
*
* Complexity: O(n)
* #param data Array to populate the heap from
* #param capacity Capacity of the heap (must be >= data.length)
* #param comparator Comparator to use when comparing elements
*/
MinHeap(T[] data, int capacity) {
for(int i = data.length/2 + 1; i >= 0;i--){
heapify(data,i);
}
}
public void heapify(Comparable[] data,int i){
int smallest = i;
if(size >left(i) && data[left(i)].compareTo(data[i])< 0 )
{
smallest = left(i);
}
else{
smallest = i;
}
if(size>right(i) && data[right(i)].compareTo(data[i])< 0)
{
smallest = right(i);
}
if(smallest !=i)
{
Comparable temp = data[i];
data[i] = data[smallest];
data[smallest] = temp;
heapify(data,smallest);
}
}
////// Test File
#Test public void testArrayConstruction() {
System.out.println("array construction");
for(int i = 0; i < 100; ++i) {
Integer[] data = randomArray(i);
MinHeap h = new MinHeap(data, i*2);
assertEquals(h.capacity(), i * 2);
assertEquals(h.size(), i);
// Collections has min/max, but of course those don't work on arrays.
Integer smallest = data[0];
for(Integer x : data)
if(x < smallest)
smallest = x;
checkHeapOrder(h);
assertEquals(h.top(), smallest);
}
Integer[] randomArray(int size) {
Integer[] arr = new Integer[size];
for (int i = 0; i < size; ++i) {
arr[i] = Math.round((float) Math.random() * Integer.MAX_VALUE);
}
return arr;
}
void checkHeapOrder(MinHeap h) {
assertTrue(h != null);
for(int i = 1; i < h.size() / 2; ++i)
assertTrue("Heap order property is broken at element at position "
+ i,
h.data[i].compareTo(h.data[i*2]) < 0 &&
h.data[i].compareTo(h.data[i*2 + 1]) < 0);
}
This is the line in the test file where the problem occurs : integer smallest = data[0]; Here,the method is unable to initialize smallest to 0th element of array.I tried tweaking with the program,but every time got the same error.I believe that the test file can also be incorrect,so just want to make sure.
testArrayConstruction caused an Error: 0
java.lang.ArrayIndexOutOfBoundsException

You didn't include the code for randomArray() ...
Assuming that this method makes randomized array with size equal to the method's argument, then your call to
Integer[] data = randomArray(0);
makes an array of size 0 .
You can't read the element at index 0 because an array of size 0 has no elements.

Related

Adding elements to the beginning of an array of ints

Working on an addBefore() method that adds a new element to the beginning of an array of ints and then causes the existing elements to increase their index by one.
This is what is showing in the console when trying to run --
java.lang.RuntimeException: Index 1 should have value 11 but instead has 0
at IntArrayListTest.main(IntArrayListTest.java:67)
Below is the code I have so far.
public class IntArrayList {
private int[] a;
private int length;
private int index;
private int count;
public IntArrayList() {
length = 0;
a = new int[4];
}
public int get(int i) {
if (i < 0 || i >= length) {
throw new ArrayIndexOutOfBoundsException(i);
}
return a[i];
}
public int size() {
return length;
}
public void set(int i, int x) {
if (i < 0 || i >= a.length) {
throw new ArrayIndexOutOfBoundsException(i);
}
a[i] = x;
}
public void add(int x) {
if (length >= a.length) {
int[] b = new int[a.length * 2];
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
a = b;
//count += 1;
}
a[length] = x;
count++;
length = length + 1;
}
public void addBefore(int x) {
int[] b = new int[a.length*2];
for (int i = 0; i < a.length; i++) {
b[i+a.length] = a[i];
}
a = b;
a[index] = x;
length ++;
}
}
Whether you add first or last, you need to only grow the array size if it is already full.
The count field seems to be exactly the same as length, and index seems unused and meaningless as a field, so remove them both.
To rearrange values in an array, use this method:
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
You two "add" methods should then be:
public class IntArrayList {
private int[] a; // Underlying array
private int length; // Number of added elements in a
// other code
public void add(int x) {
if (length == a.length) {
int[] b = new int[a.length * 2];
System.arraycopy(a, 0, b, 0, length);
a = b;
}
a[length++] = x;
}
public void addBefore(int x) {
if (length < a.length) {
System.arraycopy(a, 0, a, 1, length);
} else {
int[] b = new int[a.length * 2];
System.arraycopy(a, 0, b, 1, length);
a = b;
}
a[0] = x;
length++;
}
}
If the answer requires you to do the looping yourself then something like this should work fine (one of a few ways to do this, but is O(n)) :
public void addBefore(int x) {
if(length + 1 >= a.length){
int[] b = new int[a.length*2];
b[0] = x;
for (int i = 0; i < length; i++) {
b[i + 1] = a[i];
}
a = b;
} else {
for (int i = length; i >= 0 ; i--) {
a[i + 1] = a[i];
}
a[0] = x;
}
length++;
}
I noticed this started running a "speed test" - not sure how useful a test like that is, as it would be based on cpu performance, rather than testing complexity of the algorithm ..
you had three problems with your solution:
you increased the length of a every time the method was called. this would quickly create an OutOfMemoryException
when you copied values from a to b, you did b[i+a.length] = a[i]; which means the values would be copied to the middle of b instead of shift just one place
at the end, you put the new value in the end of the array instead of at the beginning.
all this I was able to see because I used a debugger on your code. You need to start using this tool if you want to be able to detect and fix problems in your code.
so fixed solution would do this:
check if a is full (just like it is done with add() method) and if so, create b, and copy everything to it and so on)
move all values one place ahead. the easiest way to do it is to loop backwards from length to 0
assign new value at the beginning of the array
here is a working solution:
public void addBefore(int x) {
// increase length if a is full
if (length >= a.length) {
int[] b = new int[a.length * 2];
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
a = b;
}
// shift all values one cell ahead
for (int i = length; i > 0; i--) {
a[i] = a[i-1];
}
// add new value as first cell
a[0] = x;
length ++;
}
}
You can use the existing Java methods from the Colt library. Here is a small example that uses a Python syntax (to make the example code small I use Jython):
from cern.colt.list import IntArrayList
a=IntArrayList()
a.add(1); a.add(2) # add two integer numbers
print "size=",a.size(),a
a.beforeInsert(0, 10) # add 10 before index 0
print "size=",a.size(),a
You can use DataMelt program to run this code. The output of the above code is:
size= 2 [1, 2]
size= 3 [10, 1, 2]
As you can see, 10 is inserted before 1 (and the size is increased)
Feel free to change the codding to Java, i.e. importing this class as
import cern.colt.list.IntArrayList
IntArrayList a= new IntArrayList()
You could use an ArrayList instead and then covert it to an Integer[] Array which could simplify your code. Here is an example below:
First create the ArrayList:
ArrayList<Integer> myNums = new ArrayList<Integer>();
Next you can add the values that you want to it, but I chose to just add the numbers 2-5, to illustrate that we can make the number 1 the first index and automatically increment each value by one index. That can simplify your addBefore() method to something such as this:
public static void addBefore(ArrayList<Integer> aList) {
int myInt = 1;
aList.add(0, myInt);
}
Since your ArrayList has ONE memory location in Java, altering the Array within a method will work (this would also work for a regular Array). We can then add any value to the beginning of the ArrayList. You can pass an Integer to this method as the second argument (int x), if you want, but I simply created the myInt primitive to simplify the code. I know that in your code you had the (int x) parameter, and you can add that to this method. You can use the ArrayList.add() method to add the int to index 0 of the Array which will increment each Array element by 1 position. Next you will need to call the method:
addBefore(myNums);//You can add the int x parameter and pass that as an arg if you want here
Next we can use the ArrayList.toArray() method in order to covert the ArrayList to an Integer Array. Here is an example below:
Integer[] integerHolder = new Integer[myNums.size()];
Integer[] numsArray = (Integer[])myNums.toArray(integerHolder);
System.out.println(Arrays.toString(numsArray));
First we create an ArrayHolder that will be the same size as your ArrayList, and then we create the Array that will store the elements of the ArrayList. We cast the myNums.toArray() to an Integer Array. The results will be as follows. The number 1 will be at index 0 and the rest of your elements will have incremented by 1 index:
[1, 2, 3, 4, 5]
You could do the entire process within the addBefore() method by converting the Array to an ArrayList within the method and adding (int x) to the 0 index of the ArrayList before converting it back into an Array. Since an ArrayList can only take a wrapper class object you'll simply need to convert the int primitive Array into the type Integer for this to work, but it simplifies your addBefore() method.

How to decrease the size of an array after removing some elements from it

I'm working on a project and this is one of the task:
create a method called "remove()" which can remove an element from an array.
After removing it, if the number of elements are less than 1/4 of the array, the size of the array needs to decrease by half.
For example:
I have an size 100 array with 25 elements in it. After removing one element, I will have 24 elements, and the size of my array will be 50.
Here is my code:
//First create a method decrese
private decrease() {
if (numElement < (1 / 4) * (Array.length)) {
Array[] newarray = new Array[(Array.length) / 2];
for (int i = 0; i < numElement; i++)
newarray[i] = Array[i];
Array = newarray;
}
//Then create my Remove method
public void remove(ToRemove){
if (numElement > 0) { //First check if my array is empty
for (int i = 0; i < numElement; i++) {
if (Array[i].equals(ToRemove)) {
Array[i] = Array[numElement - 1];
Array[numElement - 1] = null;
numElement--;
decrease();
}
}
//if the Array is empty, also decrease the size
decrease();
}
After some test run, my remove works fine,the Array length would never decrese no matter what size I put in.
Can some one help me.
Thanks
Also, you should just use if (numLength < (Array.length / 4)) rather then (1/4) * (Array.length); Don't do any weird casting or something like that. By default, java integer division will floor the result if that's the behavior you expect.
Also, you should be able to just use some Arrays.copyOfRange and System.arraycopy to achieve your copying needs.
https://docs.oracle.com/javase/7/docs/api/java/lang/System.html
https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html
Here's a simple code snippet that basically implement removing elements from arrays.
import java.lang.reflect.Array;
import java.util.Arrays;
public class MySpecialArray<T> {
T[] buf;
int size;
Class<T> type;
public MySpecialArray(Class<T> type, int initialBufSize) {
this.size = 0;
this.type = type;
buf = (T[]) Array.newInstance(type, initialBufSize);
}
/**
* Like arraylist add, it will basically add freely until it reaches the max length of the buffer.
* Then it has to expand the buffer. It uses buf.length * 2 + 1 to account for when an initialBufSize of 0 is
* supplied.
* #param elem
*/
public void add(T elem) {
if (this.size == this.buf.length) {
int newSize = this.buf.length * 2 + 1;
buf = Arrays.copyOf(buf, newSize);
}
this.buf[this.size] = elem;
this.size += 1;
}
public void add(T...elements) {
for(T elem : elements) {
this.add(elem);
}
}
/**
* Remove all occurrences of an element. Also reduce the max buf_size of the array if my utilized size is less than a fourth of my max buf size.
* #param removeMe element to remove all occurrences of
* #return
*/
public void remove(T removeMe) {
boolean found = false;
for(int i = 0; i < this.size; i++) {
if (buf[i].equals(removeMe)) {
System.arraycopy(buf, i+1, buf, i, this.size - i);
this.size -= 1;
if (this.size < this.buf.length / 4) {
this.buf = Arrays.copyOf(buf, this.buf.length / 2);
}
}
}
}
/**
* Remove the last element
* #return
*/
public T remove() {
if (this.size == 0) {
throw new RuntimeException("Cannot remove from empty buffer");
}
T removed = this.buf[this.size -1];
this.size -= 1;
if (this.size <= this.buf.length / 4) {
int newSize = this.buf.length / 2;
this.buf = Arrays.copyOf(this.buf, newSize);
}
return removed;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < this.size; i++) {
sb.append(this.buf[i].toString()).append(",");
}
return sb.toString();
}
public static void main(String...args) {
MySpecialArray<Integer> arr = new MySpecialArray(Integer.class, 50);
arr.add(10, 2, 4, 3, 5, 11, 9, 3, 8, 16);
System.out.println("===Pre removed===");
System.out.println(arr.buf.length);
System.out.println(arr.size);
System.out.println(arr);
arr.remove(3);
System.out.println("===After removing 3===");
System.out.println(arr.buf.length);
System.out.println(arr.size);
System.out.println(arr);
}
}
This sample, when just running it, will print out
===Pre removed===
50
10
10,2,4,3,5,11,9,3,8,16,
===After removing 3===
25
8
10,2,4,5,11,9,8,16,
Simple answer is "You can not"
Java Array data structures are of fixed size and you can't change the size of Same Array Object "once it is created".
If you need to change size you either need to copy its contents to a new Array Object or use different data structure like ArrayList which does this internally.

Calculate BIg-O for 3 random permutation algorithms

I am trying to calculate the Big-O time complexity for these 3 algorithms, but seems like I have a lack of knowledge on this topic.
1st:
private void firstAlgorithm(int size) {
int[] array = new int[size];
int i=0; int flag=0;
while(i<size) {
int num=(int)(Math.random()*(size));
if (num==0 && flag==0) {
flag=1;
array[i]=0;
i++;
} else if(num==0 && flag==1) {
continue;
} else if(!checkVal(num, array)) {
array[i]=num;
i++;
}
}
}
private static boolean checkVal(int val, int[] arr) {
int i = 0;
for (int num:arr) {
if (num==val) {
return true;
}
}
return false;
}
2nd:
private void secondAlgorithm(int size) {
int i = 0;
int[] array = new int[size];
boolean[] booleanArray = new boolean[size];
while (i < array.length) {
int num = (int) (Math.random() * array.length);
if (!booleanArray[num]) {
booleanArray[num] = true;
array[i] = num;
i++;
}
}
}
3rd:
private void thirdAlgorithm(int size) {
int[] array = new int[size];
for (int i = 0; i < array.length; i++) {
int num = (int) (Math.random() * (i - 1));
if (i > 0) {
array = swap(array, i, num);
}
}
}
private static int[] swap(int[] arr, int a, int b) {
int i = arr[a];
arr[a] = arr[b];
arr[b] = i;
return arr;
}
Would be nice, if you could explain your results.
In my opinion, 1st - O(n^2) because of two loops, 2nd don't know, 3rd O(n)
THank you
I assume that in all your algorithms, where you are generating a random number, you meant to take the remainder of the generated number, not multiplying it with another value (example for the first algorithm: Math.random() % size). If this is not the case, then any of the above algorithms have a small chance of not finishing in a reasonable amount of time.
The first algorithm generates and fills an array of size integers. The rule is that the array must contain only one value of 0 and only distinct values. Checking if the array already contains a newly generated value is done in O(m) where m is the number of elements already inserted in the array. You might do this check for each of the size elements which are to be inserted and m can get as large as size, so an upper bound of the running-time is O(size^2).
The second algorithm also generates and fills an array with random numbers, but this time the numbers need not be distinct, so no need to run an additional O(m) check each iteration. The overall complexity is given by the size of the array: O(size).
The third algorithm generates and fills an array with random numbers and at each iteration it swaps some elements based on the given index, which is a constant time operation. Also, reassigning the reference of the array to itself is a constant time operation (O(1)). It results that the running-time is bounded by O(size).

How can I find two elements in an array that sum to k

Suppose you are given an array of unsorted integers as
A = {3,4,5,1,4,2}
Input : 6
Output : {5,1}, {4,2}
How can I do this in O(n) or O(log n). Any suggestions will be appreciated.
Update:
Can we write something more efficient than this?
for(int i=0;i<array.length-1;i++)
{
if(array[i]+array[i+1]==6)
System.out.println("{"+array[i]+","+array[i+1]+"}");
}
If the numbers stored in the input array are only positive then I'd create another array K of k+1 ArrayList elements. Where k is the number you need them to add up to.
Only two numbers less than k can add up to k (assuming we deal with positive ints} or in special case {0,k}.
Then I would iterate through all elements of input array and for each int m that is less or equal to k I'd take its index and add that index to the array of ArrayList K at index m.
Then I would iterate through first half of the array K and for each index i that has some ints stored in it I would find complementary index [k-i] and see if there are any values in it. If there are then those are your pairs.
And btw this is O(n).
public static void findElemtsThatSumTo( int data[], int k){
List arrayK[]= new List[k+1];
for(int i=0; i<arrayK.length; i++)
arrayK[i]= new ArrayList<Integer>();
for(int i=0; i<data.length; i++){
if(data[i]<=k)
arrayK[data[i]].add(i);
}
for(int i=0; i<arrayK.length/2; i++){
if(!arrayK[i].isEmpty() && !arrayK[k-i].isEmpty())
{
for(Object index: arrayK[i])
for(Object otherIndex: arrayK[k-i])
System.out.println("Numbers at indeces ["+index.toString()+", "+otherIndex.toString()+"] add up to "+k+".");
}
}
}
As with your other question, O(log n) is impossible, since you have to examine the entire array. But O(n) is more or less possible.
If your range of possible integers is relatively small — that is, if it's within a constant factor of n — then you can write:
final boolean[] seen = new boolean[max - min + 1];
for(final int a : A)
{
if(seen[input - a - min])
System.out.println("{" + (input - a) + "," + a + "}");
seen[a - min] = true;
}
If not, you can do the same thing, but using a HashSet<Integer> instead of an array:
final Set<Integer> seen = new HashSet<Integer>();
for(final int a : A)
{
if(seen.contains(input - a))
System.out.println("{" + (input - a) + "," + a + "}");
seen.add(a);
}
but that will not have guaranteed O(n) time.
public static void main(String[] args) {
// TODO Auto-generated method stub
int arr[]={4,2,6,8,9,3,1};
int sum=10;
int arr1[]=new int[sum];
for(int x=0;x<arr.length;x++)
{
arr1[arr[x]]++;
}
for(int y=0;y<arr.length;y++)
{
if(arr1[sum-arr[y]]==1)
{
System.out.println(arr[y]+","+(sum-arr[y]));
}
}
}
My little answer about this problem for O(n) complexity with O(n) additional memory. This code snippet returns all unique indices pair of elements with sum K.
/**
* Returns indices of all complementary pairs in given {#code arr} with factor {#code k}. Two elements {#code arr[i]} and {#code arr[j]} are
* complementary if {#code arr[i] + arr[j] = k}.
* Method returns set of pairs in format {#literal [i,j]}. Two pairs {#literal [i,j]} and {#literal [j,i]} are treated as same, and only one pair
* is returned.
* Method support negative numbers in the {#code arr}, as wel as negative {#code k}.
* <p>
* Complexity of this method is <t>O(n)</t>, requires <t>O(n)</t> additional memory.
*
* #param arr source array
* #param k factor number
* #return not {#code null} set of all complementary pairs in format {#literal [i,j]}
*/
public static Set<String> getComplementaryPairs(int[] arr, int k) {
if (arr == null || arr.length == 0)
return Collections.emptySet();
Map<Integer, Set<Integer>> indices = new TreeMap<>();
for (int i = 0; i < arr.length; i++) {
if (!indices.containsKey(arr[i]))
indices.put(arr[i], new TreeSet<>());
indices.get(arr[i]).add(i);
}
Set<String> res = new LinkedHashSet<>();
for (Map.Entry<Integer, Set<Integer>> entry : indices.entrySet()) {
int x = entry.getKey();
int y = k - x;
if (x == y) {
int size = entry.getValue().size();
if (size < 2)
continue;
Integer[] ind = entry.getValue().toArray(new Integer[size]);
for (int j = 0; j < size - 1; j++)
for (int m = j + 1; m < size; m++)
res.add(String.format("[%d,%d]", ind[j], ind[m]));
} else if (x < y && indices.containsKey(y))
for (int j : entry.getValue())
for (int m : indices.get(y))
res.add(String.format("[%d,%d]", j, m));
}
return res;
}

Eliminating Recursion

I've just been looking at the following piece of code
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(final String[] args) {
final int sizeA = 3;
final int sizeB = 5;
final List<int[]> combos = getAllCombinations(sizeA-1, sizeB);
int counter = 1;
for(final int[] combo : combos) {
System.out.println("Combination " + counter);
System.out.println("--------------");
for(final int value : combo) {
System.out.print(value + " ");
}
System.out.println();
System.out.println();
++counter;
}
}
private static List<int[]> getAllCombinations(final int maxIndex, final int size) {
if(maxIndex >= size)
throw new IllegalArgumentException("The maximum index must be smaller than the array size.");
final List<int[]> result = new ArrayList<int[]>();
if(maxIndex == 0) {
final int[] array = new int[size];
Arrays.fill(array, maxIndex);
result.add(array);
return result;
}
//We'll create one array for every time the maxIndex can occur while allowing
//every other index to appear, then create every variation on that array
//by having every possible head generated recursively
for(int i = 1; i < size - maxIndex + 1; ++i) {
//Generating every possible head for the array
final List<int[]> heads = getAllCombinations(maxIndex - 1, size - i);
//Combining every head with the tail
for(final int[] head : heads) {
final int[] array = new int[size];
System.arraycopy(head, 0, array, 0, head.length);
//Filling the tail of the array with i maxIndex values
for(int j = 1; j <= i; ++j)
array[size - j] = maxIndex;
result.add(array);
}
}
return result;
}
}
I'm wondering, how do I eliminate recursion from this, so that it returns a single random combination, rather than a list of all possible combinations?
Thanks
If I understand your code correctly your task is as follows: give a random combination of numbers '0' .. 'sizeA-1' of length sizeB where
the combination is sorted
each number occurs at least once
i.e. in your example e.g. [0,0,1,2,2].
If you want to have a single combination only I'd suggest another algorithm (pseudo-code):
Randomly choose the step-up positions (e.g. for sequence [0,0,1,1,2] it would be steps (1->2) & (3->4)) - we need sizeA-1 steps randomly chosen at sizeB-1 positions.
Calculate your target combination out of this vector
A quick-and-dirty implementation in java looks like follows
// Generate list 0,1,2,...,sizeB-2 of possible step-positions
List<Integer> steps = new ArrayList<Integer>();
for (int h = 0; h < sizeB-1; h++) {
steps.add(h);
}
// Randomly choose sizeA-1 elements
Collections.shuffle(steps);
steps = steps.subList(0, sizeA - 1);
Collections.sort(steps);
// Build result array
int[] result = new int[sizeB];
for (int h = 0, o = 0; h < sizeB; h++) {
result[h] = o;
if (o < steps.size() && steps.get(o) == h) {
o++;
}
}
Note: this can be optimized further - the first step generates a random permutation and later strips this down to desired size. Therefore it is just for demonstration purpose that the algorithm itself works as desired.
This appears to be homework. Without giving you code, here's an idea. Call getAllCombinations, store the result in a List, and return a value from a random index in that list. As Howard pointed out in his comment to your question, eliminating recursion, and returning a random combination are separate tasks.

Categories