Using recursion to identify an array as a MaxHeap - java

On this implementation of Priority Queue I have to use recursion to determinate if the array that I receive as a parameter of the method IsMaxHeap is a max heap.
I want to be sure I evaluate all the cases that could make this work or not work without any problem.
static boolean isMaxHeap(int[] H, int idx) {
if(2*idx == (H.length -1) && H[2 * idx] <= H[idx])
return true;
if(2*idx > (H.length -1))
return true;
if ((2*idx+1) == (H.length -1) && H[2 * idx] <= H[idx] && H[2 * idx + 1] <= H[idx])
return true;
if((2*idx+1) > (H.length -1))
return true;
if (H[2 * idx] <= H[idx] && H[2 * idx + 1] <= H[idx])
return isMaxHeap(H, (idx + 1));
else
return false;
}
Could you help me?

Your code is hard to follow because you do so many calculations in your conditionals. So it's hard to say whether it would actually work. Also, what you've written is basically a recursive implementation of a for loop. That is, you check nodes 1, 2, 3, 4, 5, etc.
Whereas that can work, you end up using O(n) stack space. If you have a very large heap (say, several hundred thousand items), you run the risk of overflowing the stack.
A more common way to implement this recursively is to do a depth-first traversal of the tree. That is, you follow the left child all the way to the root, then go up one level and check that node's right child, and its left children all the way to the root, etc. So, given this heap:
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
You would check nodes in this order: [1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
Doing it that way simplifies your code and also limits your stack depth to O(log n), meaning that even with a million nodes your stack depth doesn't exceed 20.
Since you're using 2*idx and 2*idx+1 to find the children, I'm assuming that your array is set up so that your root node is at index 1. If the root is at index 0, then those calculations would be: 2*idx+1 and 2*idx+2.
static boolean isMaxHeap(int[] H, int idx)
{
// Check for going off the end of the array
if (idx >= H.length)
{
return true;
}
// Check the left child.
int leftChild = 2*idx;
if (leftChild < H.length)
{
if (H[leftChild] > H[idx])
return false;
if (!isMaxHeap(H, leftChild)
return false;
}
// Check the right child.
int rightChild = 2*idx + 1;
if (rightChild < H.length)
{
if (H[rightChild] > H[idx])
return false;
return isMaxHeap(H, rightChild);
}
return true;
}

Related

How to optimize an algorithm with multiple while loops?

I am trying to solve a problem on leetcode.com Ugly Number II.
problem: An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5.
Given an integer n, return the nth ugly number.
example:
Input: n = 10
Output: 12
Explanation: [1, 2, 3, 4, 5, 6, 8, 9, 10, 12] is the sequence of the first 10 ugly numbers.
This is my solution
class Solution {
public int nthUglyNumber(int n) {
int outputNumber = 6;
int temp = 1;
if (n < 7) {
return n;
}
int i = 7;
while (i != (n + 1)) {
outputNumber = outputNumber + 1;
temp = outputNumber;
while (temp % 5 == 0) {
temp = temp / 5;
}
while (temp % 2 == 0) {
temp = temp / 2;
}
while (temp % 3 == 0) {
temp = temp / 3;
}
if (temp == 1) {
i = i + 1;
}
}
return outputNumber;
}
}
this works for small numbers, but when the input is a big number, then I have Time Limit Exceeded
The question is how to optimize this code?
Thank you!
Hint: You're looking for numbers of the form 2a×3b×5c for non-negative integers a, b, c. Instead of looking for ugly numbers, wouldn't it be easier to just generate them?
I used two tricks to make it about twice as fast, but it's still far too slow. I suspect the check-all-integers-for-ugliness approach is hopeless, and you'll find faster approaches in the discussions on LeetCode.
class Solution {
public int nthUglyNumber(int n) {
for (int i = 1; true; i++)
if (1418776204833984375L % (i / (i & -i)) == 0)
if (--n == 0)
return i;
}
}
The two tricks:
i & -i extracts the lowest 1-bit, so dividing by that takes out every factor 2.
1418776204833984375 is 319×513. Every positive int with only factors 3 and 5 divides that, and every other positive int doesn't.
I think the easiest way is to just maintain a collection of ugly numbers that we will need to visit. We start with a collection containing just 1, and then at each step, we remove the lowest value, and add the values found by multiplying our lowest value by 2, by 3, and by 5. Since these may be duplicates (24 = 8 * 3 and 24 = 12 * 2) but we only want them once apiece, our collection should be a Set.
My Java is far too rusty, but here's a JavaScript implementation that you could use as pseudocode:
const ugly = (count) => {
const upcoming = new Set ([1])
const found = []
while (found .length < count) {
const next = Math .min (...upcoming .values ())
found .push (next)
upcoming .delete (next)
upcoming .add (2 * next)
upcoming .add (3 * next)
upcoming .add (5 * next)
}
return found
}
const uglies = ugly (1690) // This was the upper bound specified in the problem
const nthUglyNumber = (n) => uglies [n - 1]
console .log (nthUglyNumber (10))
console .log (nthUglyNumber (1690))
ugly finds the first count ugly numbers, returning them as an Array. Internally, it keeps two collections. upcoming is the ugly numbers we know we would eventually hit. found is an array of numbers we have actually reached, always taking the smallest of the upcoming values. When we select the next one, we remove it from upcoming and add to upcoming its 2-, 3-, and 5-multiples.
This could be modified to be our only function. But it's nice to call it once for our top value, and store the resulting array, as uglies. Then nthUglyNumber is a simple function that extracts the value at the correct index.

Recursion confusion

I am writing a function according to the following guidelines:
Given an array of ints, is it possible to choose a group of some of the ints, beginning at the start index, such that the group sums to the given target? However, with the additional constraint that all 6's must be chosen. (No loops needed.)
groupSum6(0, {5, 6, 2}, 8) → true
groupSum6(0, {5, 6, 2}, 9) → false
groupSum6(0, {5, 6, 2}, 7) → false
This is the solution:
public boolean groupSum6(int start, int[] nums, int target) {
if (start == nums.length) {
if (target == 0) {
return true;
}
return false;
}
if (nums[start] == 6) {
return groupSum6(start + 1, nums, target - nums[start]);
}
if (groupSum6(start + 1, nums, target - nums[start])) {
return true;
}
return groupSum6(start + 1, nums, target);
}
I am a little confused about why this works. I understand how it adds 6s, but where are the other numbers tested to see if they will add up to the target after the 6s have been added? Where are other non-6 numbers added?
The reason this works is that the recursive call always checks to see if the number at the current index is a 6 and subtracts that from the target. So the order of evaluation is:
1)
if (start == nums.length) {
if (target == 0) {
return true;
}
return false;
}
Check to see if the passed index is the length of the array (so this would mean the index is BEYOND the bounds of the array - the last index of the array would be nums[nums.length - 1] ). If yes, AND if the target is 0, return true. Otherwise return false, because there are no more values to check
2)
if (nums[start] == 6) {
return groupSum6(start + 1, nums, target - nums[start]);
}
Check if the number at the index is 6. If it is, subtract 6 from the target, add one to the index, and check the remaining values.
3) Otherwise...
if (groupSum6(start + 1, nums, target - nums[start])) {
return true;
}
Evaluate if the rest of the array works to add up to the target. If it does return true. The key is to notice that this step will always happen AFTER we check if the value is a 6. Essentially here you are looking for a solution that COUNTS the number in the start index. If a solution is found we can end. If no solution is found using the number at num[start], then we go on to:
4)
return groupSum6(start + 1, nums, target);
Checking for a solution that exists when we DON'T use the number at num[start].
The key is that each time the recursive call is made the check for a 6 is done first ( assuming you aren't beyond the bounds of the array).
The idea is to test all the possible subarray options, something like "what happen if i get next element?" and... "what happen if i don't?".
So, for any element in the array your are making two recursive calls (two options, take it or not) and... if one of them is OK the solution is reached (so return true).
In the next recursive call you pick the number, so you call with the next index element (start+1) and substract the picked number from the target (imagine your target is 8 and the current element is 2... the recursive call is to check the next numbers with target 8-2= 6).
if (groupSum6(start + 1, nums, target - nums[start])) {
return true;
}
The next recursive call is the same but you don't pick the current number so... target will be the same (in the previous example you will check the next number with target 8);
return groupSum6(start + 1, nums, target);
And... here it comes the 6 restriction... if you find a 6 then you have no more options than to pick the number so... you put that condition first (and in this scenario there is only one call because you have no options):
if (nums[start] == 6) {
return groupSum6(start + 1, nums, target - nums[start]);
}
Just do this check if the number is 6 and then decide to skip it
public boolean groupSum6(int start, int[] nums, int target) {
//exit condition and if target becomes 0
if(start >= nums.length) return (target ==0);
// subtracting the number at start from target and make a recursive //call to groupSum6
if(groupSum6(start+1 , nums , target - nums[start])) return true;
//check if the number at start is 6 or not then decide to skip it
if(nums[start] != 6 && groupSum6(start+1 , nums , target)) return true;
//if target does not becomes 0
return false;
}

Java: Breadth-First-Traversal Order Over an Array

I wish to iterate over a sorted array in the order that a breadth first traversal would give if I put the array into a binary tree and performed a BFT on that (which is how I currently achieve this). Obviously this involves additional memory overhead since you need to build and store the array again in the binary tree. I know this should be similar to a binary search but I can't quite get the ordering right.
Here's how I currently achieve this:
BTree bst = sortedArrayToBST(array);
Queue<BTree> queue = new LinkedList<BTree>();
queue.add(bst);
while(!queue.isEmpty()) {
BTree node = queue.remove();
System.out.println(node.data);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
Here's what I have currently (but this obviously gives the wrong ordering):
public void bstIterate(int[] array, int start, int end) {
if(array.length == 0) return;
int med = array.length /2;
System.out.println(array[med]);
bstIterate(array,start,mid);
bstIterate(array,mid+1,array.length);
}
Can this be done without extra memory overhead, or do I have to store the items in a stack, vector or queue, and if so, how and would it require less memory than a binary tree?
I'm not sure this is particularly efficient, but one possible solution is to pass a depth parameter to your bstIterate method and call it repeatedly with increasing depth until it returns no more results.
Something like this:
public static boolean bstIterate(int array[], int start, int end, int depth) {
if (end <= start)
return false;
else {
int mid = start + (end-start)/2;
if (depth == 0) {
System.out.println(array[mid]);
return true;
}
else {
boolean res1 = bstIterate(array, start, mid, depth-1);
boolean res2 = bstIterate(array, mid+1, end, depth-1);
return res1 || res2;
}
}
}
which you would call like this:
int depth = 0;
while (bstIterate(array, 0, array.length, depth))
depth++;
Given this array:
int array[] = {1, 3, 4, 7, 9, 13, 18, 23, 25, 30};
that produces this tree:
13
4 25
3 9 23 30
1 7 18
and this output:
13
4
25
3
9
23
30
1
7
18
Is that what you had in mind?
I hope your sortedArrayToBST method is building a balanced binary tree from the given array. In that case the method you tried to implement will mimic a DFS (Depth first search) iteration over a BST. But your implementation is buggy, a correct implementation will look like this:
void bstIterate(int[] array, int start, int end) {
if (start > end) return;
int mid = start + (end - start) / 2; //it should not be array.lenght/2 because we are not updating the array at any point
bstIterate(array, start, mid-1); // mid is already printed so we no longer need it
bstIterate(array, mid+1, end);
}
//Now Call the method from you main
bstIterate(array, 0, array.length-1); //it should be array.length-1, as it is the last index of last element of the array
But from the question title I understand you are looking for BFS traversal over a sorted array by assuming the array as balanced binary tree.
Lets say our sorted array is this {1, 2, 3, 4, 5, 6, 7}. In that case a balanced BST will look like this:
4
/ \
2 6
/ \ / \
1 3 5 7
The BFS traversal on the above tree should output as follows: 4 2 6 1 3 5 7
I wrote a quick C++ implementation for this which will do the job in O(n) complexity (hope you can easily convert it to java):
#include <stdio.h>
#include <queue>
using namespace std;
class Node {
public:
int start;
int end;
};
void BFSiterate(int *arr, int start, int end) {
queue<Node>que;
Node n;
n.start = start;
n.end = end;
que.push(n);
while(!que.empty()) {
n = que.front();
que.pop();
int mid = n.start + (n.end - n.start) / 2;
printf("%d\n", arr[mid]);
Node x;
x.start = n.start;
x.end = mid-1;
if (x.start<=x.end)
que.push(x); //left
x.start = mid+1;
x.end = n.end;
if (x.start<=x.end)
que.push(x); //right
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7};
int len = sizeof(arr)/4;
BFSiterate(arr, 0, len-1);
return 0;
}

Heapify function not working

I have been trying to write a recursive heapify method that turns an array of integers into a min-heap. The Main and Heap classes are shown below. Most of the array shown in Main is already a min-heap, but the subtree [11, 4, 5] is not a min-heap. However, the heapify function doesn't seem to reach that subtree. I can't figure out what the problem is, any help would be greatly appreciated.
public class Heap {
public Heap(int[] array) {
heap = array;
}
public void heapify() {
heapifyHelper(0);
}
public void heapifyHelper(int rootIndex) {
if(isLeafIndex(rootIndex)) {
return;
}
else {
int leftChildIndex = getLeftChildIndex(rootIndex);
int rightChildIndex = getRightChildIndex(rootIndex);
int leftChildValue = heap[leftChildIndex];
int rightChildValue = heap[rightChildIndex];
int rootValue = heap[rootIndex];
if(leftChildValue < rootValue && leftChildValue < rightChildValue) {
swap(rootIndex, leftChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
else if(rightChildValue < rootValue && rightChildValue < leftChildValue) {
swap(rootIndex, rightChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
}
}
public int getLeftChildIndex(int parentIndex) {
return 2 * parentIndex + 1;
}
public int getRightChildIndex(int parentIndex) {
return 2 * parentIndex + 2;
}
public int getParentIndex(int childIndex) {
if(childIndex == 0) {
throw new IllegalArgumentException("Cannot get the parent index of the root.");
}
else {
return (childIndex / 2) - 1;
}
}
public boolean isLeafIndex(int index) {
int leftIndex = getLeftChildIndex(index);
int rightIndex = getRightChildIndex(index);
if(leftIndex >= heap.length && rightIndex >= heap.length) {
return true;
}
else {
return false;
}
}
public void swap(int index1, int index2) {
int temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
public void printHeap() {
System.out.println(Arrays.toString(heap));
}
int[] heap;
}
public class Main {
public static void main(String[] args) {
int[] x = {0, 5, 2, 9, 11, 6, 12, 21, 32, 4, 5};
Heap heap = new Heap(x);
heap.printHeap();
heap.heapify();
heap.printHeap();
}
}
There are several problems in your heapifyHelper:
public void heapifyHelper(int rootIndex) {
if(isLeafIndex(rootIndex)) {
return;
}
else {
int leftChildIndex = getLeftChildIndex(rootIndex);
int rightChildIndex = getRightChildIndex(rootIndex);
int leftChildValue = heap[leftChildIndex];
int rightChildValue = heap[rightChildIndex];
What if leftChildIndex == heap.length - 1? Then rightChildValue will cause an ArrayIndexOutOfBoundsException.
int rootValue = heap[rootIndex];
if(leftChildValue < rootValue && leftChildValue < rightChildValue) {
swap(rootIndex, leftChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
else if(rightChildValue < rootValue && rightChildValue < leftChildValue) {
What if both children are equal, and smaller than the parent? In that case you don't swap at all.
swap(rootIndex, rightChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
}
}
And the reason why the subtree [11, 4, 5] isn't reached is because you only call heapifyHelper for the children if one of the children is smaller than the parent, but when you call heapifyHelper(1), the two children of the node 5 are 9 and 11, both larger than the root value. (Actually, you don't even call heapifyHelper(1), since heap[0]is already smaller than both its children.)
But rectifying that alone by unconditionally recurring (on the children that exist) doesn't make your heapify correct. If you recur from the root to the leaves, each value can bubble up at most one level. You must recur from the leaves to the root(1), and you need to sift the values down completely, not just one level.
If you only swap a value with one of its children, each position is considered at most twice. Once when comparing it to its parent, once when comparing it to its children. When you go from the root to the leaves, when you compare a position to its children, no position above it (no position with a smaller index, even) can ever be changed anymore.
So each value can bubble up at most one level. If the smallest element is below the direct children of root, root won't become the smallest element in the tree. If you start from the leaves (or rather the parents of the leaves), the values can bubble up as far as they need. But if you only swap a value with the smaller of its children (if that is smaller than the value), each value can still only bubble down one level, which still need not create a heap.
Let us consider the tree
7
/ \
/ \
2 6
/ \ / \
1 3 4 5
If you go from the root to the leaves, you swap 2 and 7 first, giving
2
/ \
/ \
7 6
/ \ / \
1 3 4 5
The top two levels are now a min-heap.
Then you treat the left subtree, and finally the right subtree, producing
2
/ \
/ \
1 4
/ \ / \
7 3 6 5
altogether. Now the bottom two levels are composed of min-heaps, but the heap property was destroyed in the level above. To make that a heap again, the 1 must be sifted up further (in this case, just one level).
If you go from the leaves to the root, you first treat the right subtree,
6
/ \
4 5
producing
4
/ \
6 5
for that, then the left subtree
2
/ \
1 3
producing
1
/ \
2 3
there. Both subtrees are now min-heaps. Altogether, you have
7
/ \
/ \
1 4
/ \ / \
2 3 6 5
Then you'd swap 7 and 1, producing
1
/ \
/ \
7 4
/ \ / \
2 3 6 5
Now the root is the smallest value, but the last swap destroyed the heap property of the left subtree. To make that a heap again, the 7 must be sifted down further.
So you need a siftDown method (and/or a siftUp method) that sifts a value down (up) as far as needed.
private void siftDown(int index) {
int leftChildIndex = getLeftChildIndex(index);
if (leftChildIndex >= heap.length) {
// a leaf, no further sifting down possible
return;
}
int rightChildIndex = getRightChildIndex(index);
if ((heap[leftChildIndex] < heap[index])
&& (rightChildIndex >= heap.length || heap[rightChildIndex] >= heap[leftChildIndex)) {
// left child is smallest or only, and smaller than parent
swap(index, leftChildIndex);
siftDown(leftChildIndex);
} else
// left child not smaller than parent, or right child exists and is smaller than parent
if (rightChildIndex < heap.length && heap[rightChildIndex] < heap[index]) {
swap(index, rightChildIndex);
siftDown(rightChildIndex);
}
// otherwise, this one has no smaller child, so no more sifting needed
}
Then a correct heapify would be
public void heapify() {
// last index that has a child:
int lastNonLeafIndex = heap.length/2 - 1;
for(int index = lastNonLeafIndex; index >= 0; --index) {
siftDown(index);
}
}
That works because if you have a (binary) tree where both of the subtrees are min-heaps, sifting down the root value constructs a min-heap:
If the root value is smaller than (or equal to) both its children, the entire tree is already a min-heap.
Otherwise, after the root value has been swapped with the smaller of its children (without loss of generality the left), the other subtree is unchanged, hence still a min-heap. And, since the left child was the smallest value in the left subtree before the swap, the value at the root is the smallest value in the entire tree after the swap. Swapping may have destroyed the min-heap property of the left child, though. But the left-left and the left-right subtrees have not been changed, so they are still min-heaps. And the new left subtree is smaller than the original tree, so by the induction hypothesis, sifting down its root value creates a min-heap from that. So after sifting down has finished, we have a tree with the smallest value at the root, both of whose subtrees are min-heaps, that is, a min-heap.
Since each leaf is trivially a min-heap, for each index processed in heapify, the subtree rooted at that index becomes a min-heap.
The alternative, using siftUp:
private void siftUp(int index) {
if (index == 0) return; // root, nothing to do
int parentIndex = getParentIndex(index); // see Note below
if (heap[index] < heap[parentIndex]) {
swap(index, parentIndex);
siftUp(parentIndex);
}
}
public void heapify() {
for(int index = 1; index < heap.length; ++index) {
siftUp(index);
}
}
The code for siftUp is much shorter than for siftDown, since only two nodes are involved here, and there is no need to check whether any child index falls outside the array. But the heapify is less efficient (see footnote (1)).
siftUp is the method used to insert a new value into a heap. So this one builds a heap by inserting all values (except the root value) into an existing min-heap [when siftUp(index) is called, the part of the array before index is already a min-heap].
Note: your getParentIndex is incorrect,
return (childIndex / 2) - 1;
says the parent of index 1 is -1, and the parent of index 3 is 0, correct is
return (childIndex - 1) / 2;
(1) Actually, you can proceed from the root to the leaves, if you sift each value up as far as needed. It's just more efficient to heapify going from the [parents of the] leaves to the root. If you go from the root to the leaves, at level k you have 2^k values that may need to bubble up k levels, which gives an O(n*log n) complexity for building the heap. If you proceed from the [parents of the] leaves upward, you have 2^(log n - 1 - k) values that may need to bubble down k levels, which gives a complexity of O(n) for building the heap.
So i think I figured out what the problem is.
Your heapify helper stops the minute you find a root where the root is smaller than leftChild and rightChild.
In running your case.. you reach a situation where root (5) is lesser than 11 and 9..But 11 is not heapified..
Many ways to fix this. That i leave to you.
EDIT
So heapify is ideally meant only to put the first element in the rootIndex in a correct place. Not to create a Heap.
If you want to create a correct Heap, you need to insert a new element and call heapify on every such insert.

Recursively check if an array represents a heap

How do we check if an array represents a heap data structure recursively?
Assume it is an array of integers, and I am checking for max heap.
The following is what I came up with, but I don't know if that is correct.
public static boolean isHeap(int[] arr) {
if (arr = null)
return false;
return isHeapTree(arr, 0);
}
private static boolean isHeapTree(int[] arr, int i) {
if (i = arr.length - 1)
return true;
// check if a parent's value is larger or equal to both of
// its left child and right child
else if (arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2])
return (isHeapTree(arr, 2i + 1) && isHeapTree(arr, 2i + 2));
else
return false;
}
is it possible a binary heap could be incomplete? say:
100
/ \
50 20
/ \ \
30 40 26
Some comments:
You definitely don't need the while loop - you always return after the first iteration
2i + 1 doesn't work in java, you need to use 2*i + 1
arr[2*i + 1] and arr[2*i + 1] may throw and ArrayIndexOutOfBoundsException so you need to either manually do a range check, or wrap it in a try.. catch block
Typically, when a heap is stored in an array, it is always packed to the left. In other words, when the heap has n elements, the all elements in the array in the index range [0, n-1] contain elements. Every insert starts by placing the element at index size(), and then it jumps up until it is smaller than its parent.
So the approach you are taking can succeed.
Also note that in your code, you can deal with going out of bounds with a small change to your guard on top:
public static boolean isHeap(int[] arr, int size) {
return (null == arr) ? false : isHeapTree(arr, size, 0);
}
private static boolean isHeapTree(int[] arr, int size, int i) {
assert i >= 0;
assert size <= arr.length;
if (i >= size) return true;
...

Categories

Resources