I am new to Java. I am trying to create a method for a custom PriorityQueue, that must uses an Array, which merges 2 PriorityQueues.
The problem is that while I have no errors in my code, the Java compiler in Eclipse calls java.lang.ArrayIndexOutOfBoundsException whenever I call that method in main.
About Merge method:
It's basically a method that takes the elements of 2 arrays and merges them into one array. Then it erases all the elements of the second array using the extractMax method:
So far my code is this:
//Custom Priority Queue
class PriorityQueue {
private int capacity;
private int queue[];
private int i;
public PriorityQueue() {
capacity = 100;
queue = new int[100];
i = 0;
}
public PriorityQueue(int size) {
capacity = size;
queue = new int[size];
i = 0;
}
protected void quickSort(int left, int right) {
int i = left, j = right;
int pivot = queue[(left + right) / 2];
while (i <= j) {
while (queue[i] > pivot)
i++;
while (queue[j] < pivot)
j--;
if (i <= j) {
int temp = queue[i];
queue[i] = queue[j];
queue[j] = temp;
i++;
j--;
}
}
if (left < j)
quickSort(left, j);
if (i < right)
quickSort(i, right);
}
public boolean insert(int number) {
if(i < capacity) {
queue[i] = number;
quickSort(0, i++);
return true;
}
return false;
}
public int extractMax() {
if(i == 0)
return -1;
else {
int tempQueue[] = new int[i-1];
int temp = queue[0];
for(int j=0; j<i-1; j++)
tempQueue[j] = queue[j+1];
queue = tempQueue;
i--;
return temp;
}
}
public boolean merge(PriorityQueue myPriorityQueue) {
if(i + myPriorityQueue.i < capacity) {
for(int j=0; j<myPriorityQueue.i; j++)
queue[i++] = myPriorityQueue.extractMax();
quickSort(0, i-1);
return true;
}
return false;
}
//Test Class
class PriorityQueueTest
{
public static void main(String args[])
{
PriorityQueue PQ1 = new PriorityQueue();
PriorityQueue PQ2 = new PriorityQueue();
PQ1.insert(1);
PQ1.insert(3);
PQ1.insert(5);
PQ2.insert(2);
PQ2.insert(4);
PQ2.insert(6);
PQ1.merge(PQ2);
}
}
So whenever i Run this in eclipse i get:
java.lang.ArrayIndexOutOfBoundsException: 2
at PriorityQueue.merge(PriorityQueue.java:89)
at PriorityQueueTest.main(PriorityQueueTest.java:22)
I tried to figure out why it does that, but it makes no sense.
In this example, the length of the 2 PriorityQueues doesn't exceed the capacity of PriorityQueue1. There must be something wrong when I try to access PriorityQueues elements.
Thanks, any help is appreciated.
The problem is here:
for(int j=0; j<myPriorityQueue.i; j++)
Queue[i++] = myPriorityQueue.extractMax();
QuickSort(0, i-1);
return true;
More specifically, since you are incrementing i with Queue[i++] - and that's your class variable i, not a local one - j will always be less than myPriorityQueue.i. Therefore, this loop will always run until j exceeds capacity and throw an ArrayIndexOutOfBounds exception when you try to access a Queue[] element larger than its length. In a loop like this you would usually use j to access the elements in the array inside the loop:
for(int j=0; j<myPriorityQueue.i; j++)
Queue[j] = myPriorityQueue.extractMax();
QuickSort(0, i-1);
This way will only access elements in Queue[] up to i, and j automatically increments at the end of each iteration of the loop.
Things are made more confusing by your choice to use i as one of your class variables to track how many things are in the queue, instead of something more descriptive like queued or stored, when single letters such as i and j are often used in loops; you actually did use i in your QuickSort loop, when your class also already has a variable i. Using un-descriptive names for variables outside of loop counters is bad practice and makes code more difficult to understand.
Related
This question already has answers here:
What is a StackOverflowError?
(16 answers)
Closed 3 years ago.
I'm basically trying implement merge sort in Java. For doing so, I've created a class called Array, which has an integer array a[]. The class also has a method called slice(int left, int right) that produces the slice of array and returns the object. Henceforth , there is a sort() method that recursively calls itself and breaks down the array and returns an Array object at the end.
import java.util.*;
class Array
{
int a[];
public Array()
{
a = null;
}
public Array(int x)
{
a = new int[x];
}
public void input()
{
Scanner sc = new Scanner(System.in);
for(int i = 0; i < a.length; i++)
{
System.out.print("Enter a No. = ");
a[i] = sc.nextInt();
}
}
public void display()
{
for(int i = 0; i < a.length; i++)
System.out.print(a[i] + "\t");
System.out.println();
}
public Array slice(int left, int right)
{
Array ob = new Array(left + right + 1);
for(int i = left; i <= right; i++)
ob.a[i] = this.a[i];
return ob;
}
public static Array merge(Array A, Array B)
{
Array C = new Array(A.a.length + B.a.length);
int i, j, k;
i = j = k = 0;
while(i < A.a.length && j < B.a.length)
{
if(A.a[i] < B.a[j])
C.a[k++] = A.a[i++];
else if(A.a[i] > B.a[j])
C.a[k++] = B.a[j++];
else
{
C.a[k++] = A.a[i++]; j++;
}
}
while(i < A.a.length)
C.a[k++] = A.a[i++];
while(j < B.a.length)
C.a[k++] = B.a[j++];
return C;
}
public Array sort()
{
if(this.a.length == 1)
return this;
else
{
return merge(this.slice(0, (this.a.length - 1) / 2).sort(), this.slice(1 + (this.a.length - 1) / 2, this.a.length - 1).sort());
}
}
public static void main()
{
Array x;
Scanner sc = new Scanner(System.in);
System.out.print("Enter the No. of Elements = ");
Array ob = new Array(sc.nextInt());
ob.input();
System.out.println("\n ORIGINAL ARRAY");
ob.display();
System.out.println("\n SORTED ARRAY");
x = ob.sort();
x.display();
}
}
Suppose if I have an object A, which has an integer array a[], then on calling A.sort() must return an object in which all the array elements will be sorted in ascending order.
Error(s) I Got: java.lang.StackOverflowError: null
Firstly, your slice should be implemented like this. I suspect this is the main problem. The way you did it, the slices aren't getting smaller, so the recursion never bottoms out.
public Array slice(int left, int right)
{
int length = right - left; // this is the proper length
Array ob = new Array(length);
for(int i = 0; i < length; i++)
ob.a[i] = this.a[i + left];
return ob;
}
Secondly, merge should be like this.
public static Array merge(Array A, Array B)
{
Array C = new Array(A.a.length + B.a.length);
int i = 0, j = 0, k = 0;
while(i < A.a.length && j < B.a.length)
{
if(A.a[i] < B.a[j])
C.a[k++] = A.a[i++];
else if(A.a[i] > B.a[j])
C.a[k++] = B.a[j++];
else
{
C.a[k++] = A.a[i++];
C.a[k++] = B.a[j++]; // this preserves duplicates
}
}
while(i < A.a.length)
C.a[k++] = A.a[i++];
while(j < B.a.length)
C.a[k++] = B.a[j++];
return C;
}
Then sort becomes
public Array sort()
{
if(a.length < 2)
return this;
int half = a.length / 2;
Array left = slice(0, half).sort();
Array right = slice(half, a.length).sort();
return merge(left, right);
}
The stack is a region of memory of finite size. It's often not that big. When you call a recursive function, each recursive call is placed on the stack. When the recursion finishes, the calls on the stack are popped off and executed.
The problem is if your array is big, and the recursion goes to deep (many calls), you might run out of space on the stack to put the next recursive call. This is a stack overflow.
I made exactly the same mistake at uni once. :)
To fix your program you can:
increase the stack size (this is a hack, there is still a limit to how many recursive calls you can make, it's just higher now)
decrease the memory use of each call (still kind of a hack, probably not very effective either, unless you're storing large data in a local variable)
implement your merge sort iteratively so that you only deal with small pieces of data at a time, instead of putting it all on the stack first, then dealing with it at the end.
Every recursive algorithm can be implemented with iteration (a loop).
I need to implement this algorithm creating a new ArrayList object at each recursive call.
My starting Array contains integers in this order"20, 40 ,10, 30 ,50 ,5" , after sorting it I have 5,5,5,5,5,5. I think that the problem is in the recursive call and in the last for cicle of the SelectionSort because removing the last for I notice that the the first element is sorted correctly.
import java.util.*;
public class SelectionSort {
//var
public ArrayList<Integer> arr ;
//constructor
public SelectionSort(ArrayList<Integer> arr){
this.arr = arr;
}
public ArrayList<Integer> getarraylist() {
return arr;
}
public void sort(){
//position of the last sorted element
int minimum =0;
if (arr.size() <=0 ) return;
for ( int j = 1; j < arr.size()-1; j++ ) {
if (arr.get(j) < arr.get(0) ) {
minimum = j;
//swap element 0 and minimum
int temp = arr.get(0);
arr.set(0, arr.get(minimum));
arr.set(minimum, temp);
}
}
//recursive call, new array without first element (already sorted)
ArrayList<Integer> arr2 = new ArrayList<>(arr.subList(1,arr.size()));
SelectionSort s2 = new SelectionSort(arr2);
s2.sort();
for(int i=0;i<s2.getarraylist().size();i++) {
arr.set(i, s2.getarraylist().get(i));
}
}
Driver class
public class Main {
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<Integer (Arrays.asList(20,40,10,30,50,5));
System.out.println("\n ARRAY ELEMENTS \n ");
for (int i: arr) {
System.out.println(i);
}
System.out.println("\n SORTED ELEMENTS \n ");
SelectionSort s = new SelectionSort(arr);
s.sort();
for (int i: s.getarraylist()) {
System.out.println(i);
}
}
}
You have actually two bugs in your algorithm that, together, lead to the observed output.
The first bug is within the for-loop that determines the minimal element:
for ( int j = 1; j < arr.size()-1; j++ ) { ...
You terminate one element too early, i.e. the last element is never considered. Thus, after the first iteration, the 5 is the last element in your ArrayList. In fact, it is the last element in every of your ArrayLists. The fix is to not subtract 1 in the for-condition:
for ( int j = 1; j < arr.size(); j++ ) { ...
The second bug is in your last for-loop where you copy the values from index i of s2 to index i of arr. You neglect the fact that s2 is one element shorter than arr. Thus, the only element not overriden is the last element. The fix is to get the i-th element from s2, but write it at the i + 1-th index of arr:
arr.set(i + 1, s2.getarraylist().get(i));
Now let us take look at how those two bugs lead to the observed output. Since
the last element in your ArrayList is never overridden and
the last element is always the same,
all elements have the same value (in your test case: 5).
Some remarks on your code:
the variable minimum is superfluous and can be replaced with j.
If you replace all occurences of ArrayList in SelectionSort with List, you can actually simplify the last part of your code to:
// remove the line declaring arr2, it is no longer needed
SelectionSort s2 = new SelectionSort(arr.subList(1, arr.size()));
s2.sort();
// last for-loop not needed anymore, method ends here
This is possible because ArrayList#subList(...) states that "The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa."
You should take a little bit more care wrt. indentation.
You have some minor inconsistencies in your coding style. For example, sometimes you write a blank after ( or before a ) or {, sometimes you do not. See what you like more and use it uniformly.
In Java, variable-, parameter- and methodnames should be written in camelCase (getarraylist() -> getArrayList())
With your last loop:
for(int i=0;i<s2.getarraylist().size();i++) {
arr.set(i, s2.getarraylist().get(i));
}
This overrides every element with the same number. (Why you got all 5's as your result) This is because you only iterate to the second to last element (arr.size()-1). Then you copy the elements in the line:
ArrayList<Integer> arr2 = new ArrayList<>(arr.subList(1,arr.size()));
Eventually you are only copying the last element over (5) and then copying this to the final ArrayList arr.
Also you create another SelectionSort object every time you call the sort method. This is not good.
Here is the code I wrote:
public void sort(List<Integer> list){
//position of the last ordered element
int minimum =0;
if (list.size() <=0 ) return;
for ( int j = 1; j < list.size(); j++ ) {
if (list.get(j) < list.get(0) ) {
minimum = j;
//swap element 0 and minimum
int temp = list.get(0);
list.set(0, list.get(minimum));
list.set(minimum, temp);
}
}
sort(list.subList(1,list.size()));
}
I changed it to accept an argument of List<Integer> (Because the subList() method returns a List) and then got rid of the last loop and where you created new objects.
Also youll have to change
s.sort();
to:
s.sort(s.getarraylist());
Output:
ARRAY ELEMENTS
20
40
10
30
50
5
SORTED ELEMENTS
5
10
20
30
40
50
Im not sure I understand the question, but I created a recursive (and iterative) selectionSort and InsertionSort just for fun, hope it helps.
public class Sorts {
public static void swap(Comparable[] a, int i, int j) {
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void selectionSortItr(Comparable[] a, int n) {
for (int i = 0; i < n - 1; i++) {
int f = i;
for (int j = i + 1; j < n; j++) {
if (a[j].compareTo(a[f]) < 0)
f = j;
}
swap(a, i, f);
}
}
public static int select(Comparable[] a, int n, int j, int f) {
if (j >= n)
return f;
if (a[j].compareTo(a[f]) < 0)
f = j;
return select(a, n, j + 1, f);
}
public static void selectionSort(Comparable[] a, int n, int i) {
if (i < n - 1) {
swap(a, i, select(a, n, i + 1, i));
selectionSort(a, n, i + 1);
}
}
public static void insertionSortItr(Comparable[] a) {
for (int i = 1; i < a.length; i++) {
int j;
Comparable cur = a[i];
for (j = i; j > 0 && cur.compareTo(a[j - 1]) < 0; j--) {
a[j] = a[j - 1];
}
a[j] = cur;
}
}
public static void insertionSortInner(Comparable[] a, Comparable cur, int j) {
if (j > 0 && cur.compareTo(a[j - 1]) < 0) {
a[j] = a[j - 1];
insertionSortInner(a, cur, j - 1);
} else {
a[j] = cur;
}
}
public static void insertionSort(Comparable[] a, int i, int n) {
if (i < n) {
insertionSortInner(a, a[i], i);
insertionSort(a, i + 1, n);
}
}
public static void main(String[] args) {
Integer[] a = new Integer[10];
for (int i = 0; i < 10; i++)
a[i] = (int) (Math.random()*100);
selectionSort(a, 10, 0);
for (int i = 0; i < 10; i++)
System.out.println(a[i]);
}
}
I am writing code to sort an array in order. I checked some algorithm for sort and merge, however I found that if I just go through the array and compare every 2 elements and swap them and repeat that until the array is sorted.
So if array[I] > array[i++], swap, repeat.
It is not working so far. I also need a break point to avoid stack overflow: I need some help please
Array:
int[] newArray = new int[] {3,9,5,7,4,6,1};
SortArray s = new SortArray();
s.sortThisArray(newArray, 0, 0);
Recursive function:
public String sortThisArray(int[] array, double counter, double a)
{
int swap0 = 0;
int swap1 = 0;
if (a > 1000)
{
return "reached the end" ;
}
for (int i =0; i<array.length; i++)
{
if (array[i] > array[i++])
{
swap0 = array[i];
swap1 = array[i++];
array[i++] = swap0;
array[i] = swap1;
counter = counter++;
a = array.length * counter;
sortThisArray (array, counter, a);
}
}
for (int j = 0; j<array.length ; j++)
{
System.out.println(array[j]);
}
return "completed";
}
}
What you are searching for is recursive bubble sort algorithm.
The main error is confusing i++ (which increments i every time) with i+1, that is only the position in the array after i, without incrementing. Then, there is no need to use double for counter, and also the a variable is not needed. You need only the length of the current segment, this way:
import java.util.*;
public class RecursiveBubbleSort {
public static void main(String[] args) throws Exception {
int[] newArray = new int[] {3,9,5,7,4,6,1};
sortThisArray(newArray, newArray.length);
System.out.println("Sorted array : ");
System.out.println(Arrays.toString(newArray));
}
public static int[] sortThisArray(int[] array, int n) {
if (n == 1) {
return array; //finished sorting
}
int temp;
for (int i = 0; i < n-1; i++) {
if (array[i+1] < array[i]) {
temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
}
}
return sortThisArray(array, n-1);
}
}
The ++ operator evalutes separately. So in the case i++, we will read i and then increment it by 1. And the other way around, ++i, would first apply the increment to i, and then read i. So when you want to check for the next element in the array, [i+1] is what you are looking for. With the same reasoning, can you figure out whats wrong with this line?
counter = counter++;
And do we even need it?
I wrote a program for this problem:
“Write a program that, given an array array[] of n numbers and another number x, determines whether or not there exist two elements in array whose sum is exactly x.”
Which is this:
boolean hasArrayTwoCandidates (int array[], int sum) {
int length = array.length;
quickSort(array, 0, length-1);
int first, last;
first = 0;
last = length-1;
while(first < last){
if( array[first] + array[last] == sum )
return true;
else if( array[first] + array[last] < sum )
first++;
else // array[i] + array[j] > sum
last--;
}
return false;
}
At first place, I don't know where should I put or add "quick sort" codes. I have this problem with other programs, as well; when I want to add written methods to the present one.
Should I create a "new class" under this "project" and put "quicksort" codes there?
Should I put them in this class? but how can I use it?
At second place, I don't know what should I write in my "main method"?
this is my quicksort codes:
public void sort(int[] values) {
if (values == null || values.length == 0){
return;
}
this.array = values;
length = values.length;
quickSort(this.array, 0, length - 1);
}
private void quickSort(int[] array, int low, int high) {
int i = low, j = high;
int pivot = array[low + (high-low)/2];
while (i <= j) {
while (array[i] < pivot) {
i++;
}
while (array[j] > pivot) {
j--;
}
if (i <= j) {
exchange(i, j);
i++;
j--;
}
}
if (low < j)
quickSort(array, low, j);
if (i < high)
quickSort(array, i, high);
}
private void exchange(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
actually, I dont know what should I write in my "main method" to run this program?
For you Question you can do simply this kind of coding in main method:
public static void main(String[]args) {
int x = 20;
int[] arr = {2,5,4,10,12,5};
System.out.println(hasArrayTwoCandidates(arr,x));
}
make the methods static
static boolean hasArrayTwoCandidates (int array[], int sum)
But there are porblems in your coding:
private void exchange(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
Here the array is not defined. you'll get an error. you have to pass the array too to the method make it as.
private void exchange(int i, int j,int[] array)
But since you are not necessary to do sorting. I recommend this.
static boolean hasArrayTwoCandidates (int array[], int sum) {
boolean flag = false;
for(int i=0;i<array.length-1;i++){
for(int j=i+1;j<array.length ;j++){
if(array[i]+array[j] == sum)
flag = true;
}
}
return flag;
}
this will get one element and check while adding other elements that it is true
Then the main method come same way.
you can put all those method in same class, make hasArrayTwoCandidates() static (Note that main method is static and a static method can have access only to static methods)
public static boolean hasArrayTwoCandidates (int array[], int sum) {
....
}
and in your main method you can test it like this :
public static void main(String[] args){
int[] arr = {2,5,12,5,2,7,15};
System.out.print(hasArrayTwoCandidates(arr, 27));
}
Answering your questions: you can write methods and call them within the same class, just write them with the static modifier:
private static <return_type> <methodName> (<type> param1, <type> param2) {
// Your code here
}
For a program like this, I don't get why you are thinking about sorting the array before checking the sum of 2 numbers within it, when you could do all at once. Check out this code, this may shine a light on you. It is straight-forward and only to see if it clarifies your logic.
import java.util.Random;
public class VerifySum {
public static void main(String[] args) {
Random rand = new Random();
int[] array = new int[10];
// Produce a random number from 10 to 20
int randomSum = rand.nextInt(11) + 10;
// Fill out the array with random integers from 0 to 10
for (int i = 0; i < array.length; i++) {
array[i] = rand.nextInt(11);
}
// Check all array indexes against each other
for (int i = 0; i < array.length - 1; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] + array[j] == randomSum) {
System.out.println(array[i] + " + " + array[j] + " = " + randomSum);
}
}
}
// Print "x"
System.out.println("randomSum = " + randomSum);
// Print array for verification of the functionality
for (int i = 0; i < array.length; i++) {
System.out.println("array [" + i + "] = " + array[i]);
}
}
}
Sometimes making it simpler is more efficient. ;-)
Switching from Python to Java and decided to code an insertionsort method. I have written this in Python and tried to switch over the code one-to-one, it all seems good to go but it is not functioning correctly. I cut out all the other class methods/data fields to converse space and tried to limit this code only to that which is relevant to this question:
public class test1 {
static final int NOT_FOUND = -1; // A constant
// Attributes (just one)
private double data[];
// Constructor
test1 (double data[]) {
this.data = data.clone();
}
double getItem (int key) {
return data[key];
}
void insertionSort () {
for (int i = 1;i >= data.length;i++){
double currentElement = data[i];
int k = i - 1;
while(k>=0 & data[k] > currentElement){
data[k+1] = data[k];
k -= 1;
data[k + 1] = currentElement;
}
}
}
public static void main(String[] arg){
double testData[] = {1,4,32,5,673,145,68,14,757};
test1 b = new test1 (testData);
b.insertionSort();
//See how array has changed
for (int i = 0; i < 9; i++) {
System.out.print(b.getItem(i) + ", ");
}
}
}
Change
for (int i = 1; i >= data.length; i++)
to
for (int i = 1; i < data.length; i++)
The reason behind it is that you are retrieving an item from the array data for the index of i. The loop did not work because i was initialized to 1 and the condition i >= data.length was returning false because i is actually smaller than the length of the data array in your example, hence the loop did not run.
There are other troubles with this kind of check in the for loop when retrieving an element from an array because if an index for which you are returning the element is >= than the length of the array you will get an IndexOutOfBoundsException.
I am sure this is not what you meant:
for (int i = 1;i >= data.length;i++){
This will be either an infinite(actually up to overflow) or an empty cycle.
I know that is not exactly what are you asking, but if you are learning Java, maybe you could find useful:
In Java you can use Arrays.sort() :
void insertionSort () {
Arrays.sort(data);
}