I'm trying to implement a priority queue that allows for deletion of the min and the max values. I'm doing this by creating two heaps within the PQ object and two additional arrays to keep track of the location of the same values in each heap. I'm confused about how to maintain those two arrays when performing a sink, swim, or exchange.
Online, I found the comparison of the two arrays to be a[N] = b[ab[N]] and b[N] = a[ba[N]], but the problem is I don't understand those equations. If someone can offer me some insight, I think I would then be able to maintain the arrays.
Here's my code so far:
public class MyMinMaxPQ<Key extends Comparable<Key>> {
private int N = 0; // number of items on priority queue
private Key[] a; // minheap
private Key[] b; // maxheap
private int[] ab; // index a to b: a[i] == b[ab[i]]
private int[] ba; // index b to a: b[i] == a[ba[i]]
public MyMinMaxPQ(int maxN) { //constructor
a = (Key[]) new Comparable[maxN+1];
b = (Key[]) new Comparable[maxN+1];
ab = new int[maxN+1];
ba = new int[maxN+1];
}
public Key delMin() {
Key min = b[1];
exch(b, 1, N--);
a[N+1] = null;
sink(a, 1);
return min;
}
public Key delMax() {
Key max = a[1]; // Retrieve max key from top.
exch(a, 1, N--); // Exchange with last item.
a[N+1] = null; // Avoid loitering.
sink(a, 1); // Restore heap property.
return max;
}
public void insert(Key item) {
a[++N] = item;
b[++N] = item;
swim(a, N);
swim(b, N);
a[N] = b[ab[N]];
b[N] = a[ba[N]];
}
private void swim(Key[] heap, int k) {
while (k > 1 && less(heap, k/2, k)) {
exch(heap, k/2, k);
k = k/2;
}
}
/**
* Moves the numbers down the heap
* #param heap the heap that the sink will occur on
* #param k the index number on the heap
*/
private void sink(Key[] heap, int k) {
while (2*k <= N) {
int j = 2*k;
if (j < N && less(heap, j, j+1)) j++;
if (!less(heap, k, j)) break;
exch(heap, k, j);
k = j;
}
}
/**
* #param i = first integer to be compared
* #param j = second integer to be compared
* #return <0 : i precedes j; =0 : i and j are equal; >0 : j precedes i
*/
private boolean less(Key[] heap, int i, int j) {
if (heap == a) {
return a[i].compareTo(a[j]) <= 0;
} else {
return b[i].compareTo(b[j]) >= 0;
}
}
/** exchange the two values at indexes i and j in the same heap */
private void exch(Key[] heap, int i, int j) {
Key t = heap[i];
heap[i] = heap[j];
heap[j] = t;
}
/** Is the priority queue empty? */
public boolean isEmpty() {
return N == 0;
}
/** Return the number of items on the priority queue. */
public int size() {
return N;
}
}
Related
I am trying to make radix sort function that calculates the number of digits of integer numbers using the radix sort as a base, and then sort the numbers from least significant to most significant.
I am using an array that holds random integers.
How can I make this method works well?
I am using this code:
public static void sort( int[] a, int radix)
{
int i, m = a[0], exp = 1, n = a.length;
int[] b = new int[10];
for (i = 1; i < n; i++)
if (a[i] > m)
m = a[i];
while (m / exp > 0)
{
int[] bucket = new int[10];
for (i = 0; i < n; i++)
bucket[(a[i] / exp) % 10]++;
for (i = 1; i < 10; i++)
bucket[i] += bucket[i - 1];
for (i = n - 1; i >= 0; i--)
b[--bucket[(a[i] / exp) % 10]] = a[i];
for (i = 0; i < n; i++)
a[i] = b[i];
exp *= 10;
}
}
Try
int[] b = new int[a.length];
or since n = a.length
int[] b = new int[n];
I am just pointing out the most obvious problems and leave the details to you.
Each bucket will have to hold a list of numbers so using just an int as a bucket is not going to work. Use something like this instead:
List<Integer>[] bucket = new List<Integer>[10];
The array used to collect the elements in the new order needs to be the same size as the original array. You just have made it 10 long.
Statement in last for loop
a[i] = b[i];
tells that size of b must be equal to or greater than a. Hence go for #rcgldr answer. Also the value of radix passed to your function is lying unused. You can also make your function a tad faster by swapping array pointers instead of copying the elements i.e. avoiding the last for loop.
int swap[] = a;
a = b;
b = swap;
then finally after all the loops are over, return the array a
return a;
Above is your program for sorting in ascending order. I am giving below the program to sort in descending order. The only change that was required was to start adding frequencies from the end of the base array( in this case Z ) to index zero.
public static int[] DSC(int A[], int radix, int base)
{
int length = A.length ;
int B[] = new int[length] ;
int div = 1 ;
int swap[] ;
int i ;
while(radix > 0)
{
int Z[] = new int[base] ;
i = 0 ;
while(i < length)
{
Z[( A[i] / div ) % base]++ ;
i++ ;
}
i = base ;
while(i > 1)
{
i--;
Z[i-1] += Z[i] ;
}
i = length ;
while(i > 0)
{
i-- ;
B[--Z[( A[i] / div ) % base]] = A[i];
}
swap = A;
A = B;
B = swap;
div *= base;
radix--;
}
return A;
}
For non-negative integers, using binary instead of decimal, might be more efficient & intuitive for machine.
Here is an implementation I wrote, with test case:
RadixSort.java
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Radix sort.
*
* #author eric
* #date 3/12/20 12:05 PM
*/
public class RadixSort {
/**
* Sort given array, the array will be modified.
*
* #param data array of integer of non-negative integers,
* #throws IllegalArgumentException if input contain negative integer,
*/
public static void sort(int[] data) {
int numSys = 2;
int bits = validAndfindHighestBit(data); // find highest bit,
// create queues,
List<List<Integer>> queues = new ArrayList<>(numSys);
for (int i = 0; i < numSys; i++) queues.add(new LinkedList<>());
// sort bit by bit, low to high,
for (int i = 0; i < bits; i++) {
// re-init queues,
for (int j = 0; j < numSys; j++) queues.get(j).clear();
// array -> queues,
for (int x : data) {
int bit = (x >> i) & 1; // get the i-th bit,
queues.get(bit).add(x);
}
// queues -> array,
int t = 0;
for (List<Integer> queue : queues) {
for (int x : queue) data[t++] = x;
}
}
}
/**
* Valid input number, and find highest bit that has 1.
*
* #param data
* #return
* #throws IllegalArgumentException if input contain negative integer,
*/
private static int validAndfindHighestBit(int[] data) {
// find max number,
int max = 0;
for (int x : data) {
if (x < 0) throw new IllegalArgumentException("negative integers are not supported");
if (x > max) max = x;
}
System.out.printf("max number: %d, ", max);
// find highest bit,
int highestBit = 0;
while (max != 0) {
highestBit++;
max >>= 1;
}
System.out.printf("highest bit: %d\n", highestBit);
return highestBit;
}
}
RadixSortTest.java
(Test case via TestNG)
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import static org.testng.Assert.*;
/**
* RadixSort test.
*/
public class RadixSortTest {
#Test
public void testSort() {
int[] data;
// generated un-sorted random array,
do data = genRandomArr(); while (data.length > 1 && isSorted(data));
System.out.printf("input arr:\t%s\n", Arrays.toString(data));
if (data.length > 1) Assert.assertFalse(isSorted(data));
// sort
RadixSort.sort(data);
System.out.printf("output arr:\t%s\n", Arrays.toString(data));
Assert.assertTrue(isSorted(data));
}
// corner case,
#Test
public void testSort_corner() {
int[] dataEmpty = new int[0]; // empty array,
RadixSort.sort(dataEmpty);
Assert.assertTrue(isSorted(dataEmpty));
int[] dataSingle = new int[]{5}; // single element,
RadixSort.sort(dataSingle);
Assert.assertTrue(isSorted(dataSingle));
}
// invalid input,
#Test(expectedExceptions = IllegalArgumentException.class)
public void testSort_invalid() {
int[] dataSingle = new int[]{1, -1}; // negative number,
RadixSort.sort(dataSingle);
}
/**
* generate random array, of size 10, in range [0, 1024),
*
* #return
*/
public static int[] genRandomArr() {
return genRandomArr(10, 100);
}
/**
* generate random array,
*
* #param size array size, default to 10,
* #param bound upper bound, default to 100,
* #return
*/
public static int[] genRandomArr(int size, int bound) {
if (size <= 0) size = 10;
if (bound <= 0) bound = 100;
ThreadLocalRandom rd = ThreadLocalRandom.current();
int[] arr = new int[size];
for (int i = 0; i < arr.length; i++) arr[i] = rd.nextInt(bound);
return arr;
}
// check whether array is sorted,
private static boolean isSorted(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) return false;
}
return true;
}
}
For the below program:
public class MinHeap<T extends Comparable<? super T>>
implements HeapInterface<T> {
private T[] backingArray;
private int size;
// Do not add any more instance variables
/**
* Creates a Heap.
*/
public MinHeap() {
backingArray = (T[]) new Comparable[STARTING_SIZE];
size = 0;
}
#Override
public void add(T item) {
if (item == null) {
throw new IllegalArgumentException("Item was null.");
}
if (size + 1 >= backingArray.length) {
resize();
}
backingArray[size + 1] = item;
int i = size + 1;
while (i > 1 && backingArray[i].compareTo(backingArray[i / 2]) <= 0) {
swap(backingArray[i], backingArray[i / 2], i, i / 2);
i = i / 2;
}
size++;
}
private void resize() {
T[] backingArrayTemp = backingArray;
backingArray = (T[]) new Comparable[backingArrayTemp.length * 2];
for (int i = 1; i < size + 1; i++) {
backingArray[i] = backingArrayTemp[i];
}
}
private void swap(T item1, T item2, int i, int parent) {
backingArray[parent] = item1;
backingArray[i] = item2;
}
#Override
public T remove() {
if (isEmpty()) {
throw new NoSuchElementException("Heap is empty.");
}
T temp = backingArray[1];
backingArray[1] = backingArray[size + 1];
size--;
heapify(1);
return temp;
}
private void heapify(int i) {
int left = 2*i;
int right = 2*i + 1;
int min = i;
if (left < size && backingArray[left].compareTo(backingArray[min])
< 0) {
min = left;
}
if (right < size
&& backingArray[right].compareTo(backingArray[min]) < 0) {
min = right;
}
if (min != i) {
swap(backingArray[i], backingArray[min], i, min);
heapify(min);
}
}
#Override
public boolean isEmpty() {
return size == 0;
}
#Override
public int size() {
return size;
}
#Override
public void clear() {
size = 0;
backingArray = (T[]) new Comparable[STARTING_SIZE];
}
We start indexing at i = 1.
My add method works fine, and I've tried changing from backingArray[1] = backingArray[size + 1]; to backingArray[1] = backingArray[size]
in the remove method, but that doesn't seem right and also didn't work. It got rid of the null pointer but didn't pass all my tests. I'm getting null pointers at
backingArray[left].compareTo(backingArray[min]) < 0)
because backingArray[min] is null.
Stack trace
java.lang.NullPointerException
at java.lang.Integer.compareTo(Integer.java:1216)
at java.lang.Integer.compareTo(Integer.java:52)
at MinHeap.heapify(MinHeap.java:68)
at MinHeap.remove(MinHeap.java:60)
I can't really test this, right now, but I think the main problem is in the resize() method. When you create the temporary array to hold the data
T[] backingArrayTemp = backingArray;
you actually tell the new array to point to the same address in the memory as the original backingArray. Then you reallocate backingArray and also the temp array, since they point to the same place in memory. Then, of course, all the elements will not be initialized and the bеhaviour will not be as expected.
The correct way to do this is to create a "new" array and then copy the values:
T[] backingArrayTemp = new T[backingArray.length];
for(int i = 0; i < backingArray.length; ++i)
backingArrayTemp[i] = new T(backingArray[i]);
Where each element is copied with a constructor to avoid similar issues.
About heapify() - I don't know how exactly you will use that min heap, but I guess you will always put the elements one at a time. If you ever need to create the heap from a random array you will need a more complex routine that goes through the entire heap. I can provide you with some more information, if interested.
The problem was I didn't set backingArray[size] = null.
I'm implementing Dijkstra's Algorithm using priority queue, I want a function to remove an element from the heap but I can only send it the vertex index from the Dijkstra's main and I can't find its position on the heap and I can't afford to do a binary search. Any ideas?
public class MinHeap {
Vertex[] Heap = null; // Vertex array
int Lenght;
int Size;
int[] elementsPostion; // Array of Index of Vertices
private int parent(int i) {
if (i % 2 == 0)
return (i / 2) - 1;
else
return i / 2;
}
private int leftChild(int i) {
return (2 * i) + 1;
}
private int rightChild(int i) {
return (2 * i) + 2;
}
// Initialize PQ
public MinHeap(int len) {
Lenght = len;
Size = 0;
Heap = new Vertex[Lenght];
elementsPostion = new int[Lenght];
}
// Extract Min
public Vertex ExtractMin() {
Vertex v;
v = Heap[0]; // min = index of min
elementsPostion[Heap[0].index] = -1;
Heap[0] = Heap[Size - 1];
elementsPostion[Heap[0].index] = 0;
Size = Size - 1;
minHeapify(0);
return v;
}
// ----------------------------
// Sort Inside PQ
public void minHeapify(int pos) {
int L;
int R;
L = leftChild(pos);
R = rightChild(pos);
while (pos < Size
&& (Heap[L].minDistance < Heap[pos].minDistance || Heap[R].minDistance < Heap[pos].minDistance)) {
Vertex tmp;
if (Heap[L].minDistance < Heap[R].minDistance) {
elementsPostion[Heap[L].index] = pos;
elementsPostion[Heap[pos].index] = L;
tmp = Heap[L];
Heap[L] = Heap[pos];
Heap[pos] = tmp;
pos = L;
} else {
elementsPostion[Heap[R].index] = pos;
elementsPostion[Heap[pos].index] = R;
tmp = Heap[R];
Heap[R] = Heap[pos];
Heap[pos] = tmp;
pos = R;
}
L = leftChild(pos);
R = rightChild(pos);
/*
* if(pos< Size && Heap[L].minDistance <Heap[pos].minDistance)
* min=L.index; else min=pos; if(R.index<=Size &&Heap[R]<Heap[pos])
* min=R.index; if(min !=pos) { int tmp = Heap[pos]; Heap[pos] =
* Heap[min]; Heap[min] = tmp; minHeapify(min); }
*/
}
// swap in P.Q with Swapping in arrayofVertNum
}
// insert vertex
public void insertVertex(Vertex element) {
Heap[Size] = element; // size = number of verticies
HeapDecreaseKey(Size, element); //
Size++;
}
// Compare when insert with Parents
public void HeapDecreaseKey(int index, Vertex key) // bta5od el element ele hy3mlo insert ,,
{
// index=size , key=element // add in last
// Heap[index]=key; //add in last
Vertex v = new Vertex(key.index, key.xPos, key.yPos, key.minDistance);
//int swap;
boolean b = false;
while (index > 0
&& Heap[parent(index)].minDistance > Heap[index].minDistance) {
b = true;
elementsPostion[Heap[parent(index)].index] = index;
elementsPostion[Heap[index].index] = parent(index);
Vertex tmp = Heap[parent(index)];
Heap[parent(index)] = Heap[index];
Heap[index] = tmp;
index = parent(index);
}
if (b == false)
elementsPostion[key.index] = index;
// Swap in array
}
// check if PQ is empty
public boolean isEmpty() {
return Heap == null;
}
public void display() {
for (int i = 0; i < Size; i++) {
System.out.print(Heap[i].minDistance);
}
System.out.println();
}
}
Keep track of the vertex in the heap using simple index array Positions[Vertex] and record (Vertex,Distance) as element in heap array. But implementing only this is not enough because you need to update positions of vertex very time you do swap operation on heap in any routine.
This method due to restrictions can not use ArrayLists. The method accepts an array, the desired value to find, and then the certain number of near values. It uses integers and integer arrays only. This is what I have so far
/**
* Return the k elements of a nearest to val.
* The array a is not changed as a result of calling this method.
* This method throws an IllegalArgumentException if k is negative.
* This method returns an array of zero length if k == 0 or if
* k > a.length.
*
* #param a the array to be searched
* #param val the reference value
* #param k the number of near elements to identify
* #return the k elements a[i] such that ABS(a[i] - val)
* are the k smallest evaluations
*
*/
public static int[] nearestK(int[] a, int val, int k) {
int x = 0;
int[] answer = new int[k];
if (k < x || a.length == 0 || a == null)
{
throw new IllegalArgumentException();
}
if (k == 0 || k > a.length)
{
int[] badAnswer = new int[0];
return badAnswer;
}
int[] copy = Arrays.copyOf(a, a.length);
Arrays.sort(copy);
int nearest = copy[0];
for (int i = 0; (i < copy.length); i++) {
if (Math.abs(nearest - val) > Math.abs(copy[i] - val)) {
nearest = copy[i]; x = i;
}
}
int index = 0;
while (index < answer.length) {
answer[index] = nearest;
nearest = copy[x + (index + 1)];
index++;
}
return answer;
}
This method works sometimes but i began to realize that it only used values after the desired element.
i.e
int[1,3,5,7,10,11,12} this method, if searched for 6, with 3 nearest values, would only return
7,10,11 as an array. This clearly is not correct. I am very new to java so at this point I am wondering what are some alternatives to this or ways of correcting this method.
Here's a clever answer: Instead of sorting the array in natural order, sort it according to the distance to val. Then, all you need to do is pick the first k elements:
public static int[] nearestK(int[] a, int val, int k) {
// omitted your checks for brevity
final int value = val; // needs to be final for the comparator, you can also make the parameter final and skip this line
Integer[] copy = new Integer[a.length]; // copy the array using autoboxing
for (int i = 0; i < a.length; i++) {
copy[i] = a[i];
}
Arrays.sort(copy, new Comparator<Integer>() { // sort it with a custom comparator
#Override
public int compare(Integer o1, Integer o2) {
int distance1 = Math.abs(value - o1);
int distance2 = Math.abs(value - o2);
return Integer.compare(distance1, distance2);
}
});
int[] answer = new int[k]; // pick the first k elements
for (int i = 0; i < answer.length; i++) {
answer[i] = copy[i];
}
return answer;
}
Greetings,
I'm trying to implement a Deque using a circular array that extends when the array gets full. My problem seems to be that the array refuses to extend. Either I am computing size() incorrectly or there is a problem with how I update my front and rear indices. I've looked over it many times and I seem to figure this out. Can anyone help?
public class ArrayDeque
{
public static final int INIT_CAPACITY = 8; // initial array capacity
protected int capacity; // current capacity of the array
protected int front; // index of the front element
protected int rear; // index of the rear element
protected int[] A; // array deque
public ArrayDeque( ) // constructor method
{
A = new int[ INIT_CAPACITY ];
capacity = INIT_CAPACITY;
front = rear = 0;
}
/**
* Display the content of the deque
*/
public void printDeque( )
{
for ( int i = front; i != rear; i = (i+1) % capacity )
System.out.print( A[i] + " " );
System.out.println();
}
/**
* Returns the number of items in this collection.
*/
public int size()
{
return (capacity - front + rear) % capacity;
}
/**
* Returns true if this collection is empty.
*/
public boolean isEmpty()
{
return front == rear;
}
/**
* Returns the first element of the deque
*/
public int getFirst() throws EmptyDequeException
{
if(isEmpty()){
throw new EmptyDequeException("Deque is empty.");
}
return A[front % capacity];
}
/**
* Returns the last element of the deque
*/
public int getLast() throws EmptyDequeException
{
if(isEmpty()){
throw new EmptyDequeException("Deque is empty.");
}
return A[(rear - 1) % capacity];
}
/**
* Inserts e at the beginning (as the first element) of the deque
* If array is full, extend array by doubling its capacity and insert element in new array
*/
public void insertFirst(int e)
{
if(size() == capacity){
int[] B = new int[2*capacity];
for(int i = 0; i < size(); i++){
B[i] = A[i];
}
A = B;
}
int[] B = new int[capacity];
for(int i = 0; i < size(); i++){
B[i] = A[i];
}
A = B;
for(int i = size(); i >= front; i--){
A[i+1] = A[i];
}
A[front] = e;
rear = (rear + 1) % capacity;
}
/**
* Inserts e at the end (as the last element) of the deque
* If array is full, extend array by doubling its capacity and insert element in new array
*/
public void insertLast(int e)
{
if(size() == capacity){
capacity *= 2;
int[] B = new int[capacity];
for(int i = 0; i < size(); i++){
B[i] = A[i];
}
A = B;
}
A[rear] = e;
rear = (rear + 1) % capacity;
}
/**
* Removes and returns the first element of the deque
* Shrink array by half of current size N when number of elements in the deque falls below N/4
* minimum capacity should always be 8
*/
public int removeFirst() throws EmptyDequeException
{
if(isEmpty()){
throw new EmptyDequeException("Deque is empty.");
}
else if(capacity >= 8){
if(size() < capacity/4){
capacity /= 2;
int[] B = new int[capacity];
for(int i = 0; i < size(); i++){
B[i] = A[i];
}
A = B;
}
}
int temp = A[front];
A[front] = 0;
front = (front + 1) % capacity;
return temp;
}
/**
* Removes and returns the last element of the deque
* Shrink array by half of current size N when number of elements in the deque falls below N/4
* minimum capacity should always be 8
*/
public int removeLast() throws EmptyDequeException
{
if(isEmpty()){
throw new EmptyDequeException("Deque is empty.");
}
else if(capacity >= 8){
if(size() < capacity/4){
int[] B = new int[capacity/2];
for(int i = 0; i < capacity; i++){
B[i] = A[i];
}
A = B;
}
}
int temp = A[rear - 1];
A[rear] = 0;
rear = (rear - 1) % capacity;
return temp;
}
} // end class
Test Input:
for(i = 1; i <= 100; i++)
q.insertLast(i);
q.printDeque();
for(i = 1; i <= 99; i++)
k = q.removeFirst();
q.printDeque();
Test Output: I've set up several print statements and the size always remains at 7 for some reason...
Exception in thread "main" A3.EmptyDequeException: Deque is empty.
at A3.ArrayDeque.removeFirst(ArrayDeque.java:133)
at A3.ArrayMain.main(ArrayMain.java:37)
Well, consider this...
If your max capacity is 8, then your queue can have 9 total size states: 0 1 2 3 4 5 6 7 and 8.
ANY_NUMBER % 8 can only have 8 states: 0 1 2 3 4 5 6 and 7.
This is homework (thanks for being honest about it) so I don't want to spoil it all for you, but this should point you in the right direction. Good luck!
Look at your insertFirst method, and what does it do when the array is full. Read the whole method, not only the first if block. Are you ever changing your capacity?