Java: QuickSort on LinkedList gives me exception - java

I'm studying for exam (Algorithms and data structures), and I'm trying to make quicksort work for LinkedList but it's giving me ListIndexOutOfBoundsException.
For homework a while ago, I used straightinsertion for sorting ArrayList and Vector, now I'd like to understand QuickSort (I do in theory) for LinkedList.
I'm not too familiar with linkedlist, but it shouldn't be too different from ArrayList?
public class Sort {
public static void quickSort(LinkedList<Oseba> a) {
sort(a, 0, a.size() - 1); // this is line 16
}
public static void sort(LinkedList<Oseba> a, int l, int r) {
int i = l;
int j = r;
Oseba x = a.get((l + r) / 2), w;
do {
while (a.get(i).mlajsi(x)) {
++i;
}
while (x.mlajsi(a.get(j))) { // this is line 31
--j;
}
if (i <= j) {
w = a.get(i);
a.set(i, a.get(j));
a.set(j, w);
++i;
--j;
}
} while (i <= j);
if (l < j) {
sort(a, l, j);
}
if (i < r) {
sort(a, i, r);
}
}
}
Oseba means 'a Person', it's a class I made for testing various methods (like sorting, comparing)
public class Oseba implements Comparable<Oseba> {
protected String priimekIme; //surnameName
protected int letoRojstva; //year of birth
protected Spol spol; //gender (enum)
public Oseba(String priimekIme, int letoRojstva, Spol spol) {
this.priimekIme = priimekIme;
this.letoRojstva = letoRojstva;
this.spol = spol;
}
#Override
public int compareTo(Oseba o) {
if (this.letoRojstva < o.letoRojstva) {
return -1;
} else if (this.letoRojstva > o.letoRojstva) {
return 1;
} else {
return this.priimekIme.compareTo(o.priimekIme);
}
}
public boolean mlajsi(Oseba o) { //younger
return (o.letoRojstva - this.letoRojstva <= 0);
}
#Override
public String toString() {
String s = priimekIme + ", " + spol.getKratko() + ", " + letoRojstva;
return s;
}
}
And this is an error I get:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: -1, Size: 6
at java.util.LinkedList.checkElementIndex(LinkedList.java:553)
at java.util.LinkedList.get(LinkedList.java:474)
at javaapplication1.Sort.sort(Sort.java:31)
at javaapplication1.Sort.quickSort(Sort.java:16)
at javaapplication1.JavaApplication1.main(JavaApplication1.java:55)
Java Result: 1
This quicksort method is supposed to work with Vector or ArrayList, I don't know why it wouldn't with LinkedList?
Thanks!

Well you don't check for boundaries during your loops.
while (a.get(i).mlajsi(x)) {
++i;
}
while (x.mlajsi(a.get(j))) { // this is line 31
--j;
}
should be
while (i <= r && a.get(i).mlajsi(x)) {
++i;
}
while (j >= l && x.mlajsi(a.get(j))) { // this is line 31
--j;
}
and
} while (i <= j);
strictly speaking, should also take account that i and j are inside the boundaries (but i think it is not neccessary).
It will solve the exception issue, but I didn't verify the correctness of the algorithm.

One of the big rules in Java (and OO in general) is "code to interfaces, not to implementations." Right now, you're coding to the LinkedList implementation of the List interface. The only way to guarantee that this code will work with any List (Vector, ArrayList, etc.) is to change your declarations. For instance:
public static void quickSort(LinkedList<Oseba> a) {
Should become
public static void quickSort(List<Oseba> a) {
And similarly with sort:
public static void sort(List<Oseba> a, int l, int r) {
Now, whenever you declare a person, it should look like this:
List<Oseba> a = LinkedList<Oseba>();
But in the place of LinkedList, you can substitute any other type of list.
This doesn't answer the question of why your code is failing -- I think UmNyobe's advice is good, though I didn't test it -- but it answers your lesser question as to why this code isn't acting like other list types. It's because you're coding to the implementation, where you should be using the interface.

Related

Yaroslavsky Dual-pivot Quicksort Java implementation

I'd like to ask you if you can show me how my code might look like so I might not get java.lang.StackOverflowError during the sort of 100000 ints (My implementation is good but only to 20000 numbers , bigger sizes of table to sort will produce that error). I've tryied to change the size of heap in InteliJ , but that way seems not to be working.Also I've spend like 2 hours trying to modyfy it and reading about it over the web , yet I can't overcome this problem. That's why I'm asking you guys , to show me where can I change my code in my implementation so I won't recive that error.
Here is the code:
import java.util.ArrayList;
import java.util.Random;
public class YaroslawskiSort {
Random gener;
public int temporary,genertype,NInts;
ArrayList<Integer> mylist;
public YaroslawskiSort(int type,int ilosc){
gener = new Random();
mylist= new ArrayList<>();
this.genertype=type;
this.NInts=ilosc;
}
void generate(){
if(genertype==0){
for(int i=0;i<NInts;i++){
mylist.add(gener.nextInt(100000));
}
}else {
for(int i=0;i<NInts;i++){
mylist.add(NInts-i);
}
}
}
void sortingI(int left,int right) {
for (int i=left+1;i<=right;i++)
{
int value = mylist.get(i);
int j =i-1;
while (j >= left && mylist.get(j)>value)
{
mylist.set(j+1,mylist.get(j));
j--;
}
mylist.set(j+1,value);
}
}
private void sorting( int left, int right) {
if((right-left)>=17) {
int[] index=new int[2];
index = partition(left, right);
if (left < index[0]) {
sorting(left, index[0]);
}
if(index[0]<index[1]){
sorting(index[0], index[1]);
}
if (index[1] < right) {
sorting(index[1], right);
}
}
if((right-left)<17 && (right-left)!=0){
sortingI(left,right); //INSERTION SORT!
}
}
private int[] partition( int left, int right) {
int pivot1 = mylist.get(left);
int pivot2 = mylist.get(right);
if(pivot1>pivot2){
mylist.set(left,pivot2);
mylist.set(right,pivot1);
temporary=pivot1;
pivot1=pivot2;
pivot2=temporary;
}
int L=left+1;
while(mylist.get(L)<pivot1 && L<right){
L++;
}
int K=L;
while( K<=right&& mylist.get(K)>=pivot1 && mylist.get(K)<=pivot2 ){
K++;
}
int G=right-1;
while(mylist.get(G)>pivot2 && G>left){
G--;
}
while (K <= G) {
if(mylist.get(K)<pivot1){
mylist.add(left+1,mylist.remove(K));
L++;
K++;
}
if(mylist.get(K)>=pivot1 && mylist.get(K)<=pivot2){
mylist.add(L,mylist.remove(K));
K++;
}
if(mylist.get(K)>pivot2){
mylist.add(right,mylist.remove(K));
G--;
}
}
mylist.set(left,mylist.get(L-1));
mylist.set(L-1,pivot1);
mylist.set(right,mylist.get(G+1));
mylist.set(G+1,pivot2);
int[] table=new int[2];
table[0]=L;
table[1]=G;
return table;
}
void printing(){
for(int k=0;k<NInts;k++){
System.out.print(" "+mylist.get(k));
}
}
public static void main(String[] args){
YaroslawskiSort instance = new YaroslawskiSort(1,100000);
instance.generate();
instance.sorting(0, instance.mylist.size() - 1);
instance.printing();
}
}
Thanks for helping :)
Your code is working good, to increase stack size open Edit configuration... of your project in intellij IDEA (top right corner) and in field VM options type -Xss16m. If you do so, you will not get StackOverflowError.
In my opinion you get StackOverflow cause you're recursively call sorting method and it in its turn call partition method where always a new int[2] is created and all of these arrays are on stack.
If you can rewrite partition method so it (for example) return int and call it for left and right bound separately, I think this will fix the problem.

Calling Comparator parameter in main method

I am implementing insertion sort method. Here is the requirement of my code.
The method insertionSort is a static method that returns nothing.
It has two parameters: a generic array and a Comparator (generic).
It sorts the generic array using the merge sort algorithms
My Question is: What do I use for Comparator parameter c when calling in main method?
Here is what I have so far, I have some unimplemented method (merge sort an isAnagaram) ignore those
public class Sorting
{
public static <T extends Comparable<T>> void insertionSort(T[] a, Comparator<T> c)
{
for (int i = 0; i < a.length; i++)
{
T key = a[i];
int j;
for (j = i - 1; j >= 0; j--)
{
if (c.compare(a[j], key) <= 0)
break;
a[j + 1] = a[j];
}
a[j + 1] = key;
}
}
public static void mergeSort()
{
//TODO
}
public static boolean isAnagram(String first, String second)
{
//TODO
return false;
}
public static void main(String[] args)
{
Integer a[] = { 99, 8, 19, 88, 62, 2, 1, 9, 19 };
// not sure how to pass parameter comparator
insertionSort(a, null );
for (int i = 0; i < a.length; i++)
{
System.out.print(a[i] + " ");
}
}
}
I looked around on stack overflow as well as googled a lot on a Comparator interface but I couldn't really find any method where you are required to pass Generic comparator as parameter. Can someone help me tell what I am not understanding or direct me to right direction.
Comparator is an interface, which can not be instantiated. You need to implement it. There are two methods to implement:
compare
equals
You need to implement them for Integer elements. Like this:
public class IntegerComparator implements Comparator {
public int compare(Integer a, Integer b) {
return a.intValue() - b.intValue();
}
public int equals(Object obj) {
return this.equals(obj);
}
}
and in your main you call it like this:
insertionSort(a, new IntegerComparator );
Explanation: Comparator is an interface, therefore it cannot be instantiated. You need to implement it. You have an array of Integer elements to sort, therefore you can implement an Integer Comparator. The compare method returns the subtraction of the int values. If a < b, then it is negative. If a == b, then it is 0. If a > b, then it is positive.
Read more here and here.

How do I return these using only one method?

public class newClass {
public static void main(String[] args)
{
int nullValue=0;
int nullValue2=1;
int nullValue3=0;
int nullValue4=0;
int [] sourceArray = {4,5,6,7};
int [] targetArray = new int [4];
for (int i=0; i<sourceArray.length; i++)
{
nullValue+=sourceArray[i];
}
targetArray[0]=nullValue;
// I added all sourceArray elements together and passed it to targetArray[0]
for (int i=0; i<sourceArray.length; i++)
{
nullValue2*=sourceArray[i];
}
targetArray[1]=nullValue2;
// I multiplied all sourceArray elements together and assigned the result to targetArray[1]
for (int i=0; i<sourceArray.length; i++)
{
nullValue3 += getResult(sourceArray[i]);
}
targetArray[2]=nullValue3;
// I tried to add all odd numbers in sourceArray together and assign it to targetArray[2]
for (int i=0; i<sourceArray.length; i++)
{
nullValue4 += getResult(sourceArray[i]);
}
targetArray[3]=nullValue4;
// Same as previous except I need to do that with even numbers.
}
public static int getResult (int x)
{
if (x%2 == 0)
{
return x;
}
else
{
return 0;
}
}
}
You can read my comments above. I realize I can create another method for the last part but I am supposed to use only one method to return odds and evens. I tried almost anything. I can't think of any other ways anymore. Obviously I can't return x in both cases(Yeah I was too desperate to try that).
Straight to the point. I need one method to return x if it's odd or if it's even(We can say it's impossible by the look of that sentence already). I guess that's impossible to do with only one method. I'm not good at java yet so I'm not sure. Maybe there are other ways to do that with only one method which may be so easy. I worked on it for like 6 hours so I'm asking you guys. Thanks.
Create a method to return a boolean if the number is even like so
public static boolean isEven(int x)
{
return (x%2 == 0)
}
Then in your loop for evens
for (int i=0; i<sourceArray.length; i++)
{
if(isEven(x))
nullValue3 += sourceArray[i];
}
For odds just change to if(!isEven(x))
But this is probably deviating from the requirements as you probably want a method that returns an int and you could just put the condition directly in the loop and not need a method
If I understand your question correctly, what you want is to be able to tell the getResult function whether to give you only odd numbers or only even numbers. Without getting complicated, this is what I would do:
public static int getResult(int x, boolean evens) {
if (x % 2 == 0) {
return evens ? x : 0; // shorthand for: if(evens) {return x;} else {return 0;}
} else {
return evens ? 0 : x;
}
}
Simply speaking, I pass a flag value (evens) to the getResult function. This flag tells me whether to filter for even numbers or for odd numbers.
I test whether x is even (x % 2 == 0). If it is, I return it if I'm looking for evens, and I return 0 if I'm looking for odds. If x wasn't even, then I do the opposite.
It would be a little cleaner to write a pair of helper functions, which you could then call from your getResult function.
private static int getIfEven(x) {
if (x % 2 == 0) {
return x;
}
return 0;
}
private static int getIfOdd(x) {
if (x % 2 == 0) {
return 0;
}
return x;
}
public static int getResult(int x, boolean evens) {
// shorthand for:
// if (evens) {
// return getIfEven(x);
// } else {
// return getIfOdd(x);
// }
return evens ? getIfEven(x) : getIfOdd(x);
}
Depending on how much you're allowed to deviate from the current setup (I assume this is homework), you could also just write an isEven(int x) function and call that at each step through the loop, only adding the number if it is/isn't even.

Why aren't my objects that implement Comparable sorted?

my simple example (compiled working code) just does not sort fruits by their weight.
import java.util.Arrays;
public class Test {
public static class Fruit implements Comparable<Fruit> {
public int weight = 0;
public Fruit(int w) { weight = w; }
// compare this fruit to a given fruit f
public int compareTo(Fruit f) {
return (weight > f.weight) ? 1 : 0;
}
}
public static void main(String[] args) {
// get some fruits (we intentionally create a box for 100 fruits)
Fruit[] fruits = new Fruit[100];
for (int i = 0; i < 10; i++) {
fruits[i] = new Fruit((int)(Math.random() * 50 + 1));
}
// sort fruits by weight
Arrays.sort(fruits, 0, 10);
// print fruit weights
for (int i = 0; i < 10; i++) {
System.out.print(fruits[i].weight + " ");
}
}
}
Why it is so?
Alright, in my problem (not about fruits), I have objects that are never pairwise equal, that is why I thought one object is either bigger or smaller than another. So how can I handle this situation when I know that 0 (objects are equal) will never happen?
compareTo must return one of 3 values:
>0 --> Bigger than
0 --> Equal
<0 --> Less than
Your compareTo method only returns 0 or 1; fix that.
Use the method public static int compare(int x, int y) from the class java.lang.Integer (since Java 7).
public int compareTo(Fruit f) {
return Integer.compare(weight, f.weight);
}
If weight is never negative then you can try
return weight - f.weight;
instead of
return (weight > f.weight) ? 1 : 0;
to sort from lowest to highest value.
The best approach is to use the JDK-supplied method for comparing int values, which also makes it crystal clear what the code is doing
public int compareTo(Fruit f) {
return Integer.compare(weight, f.weight);
}
Prior to version 7 java, you have two choices:
public int compareTo(Fruit f) {
return weight - f.weight; // terse, but slightly obtuse
}
public int compareTo(Fruit f) {
return new Integer(weight).compareTo(f.weight); // ugly, but supposedly clear
}
My preference is the subtraction, because once you understand it, it's clear from then on.
Your compareTo method should return -1, 0, 1
LESSER = -1;
EQUAL = 0;
BIGGER = 1;

Finding the largest positive int in an array by recursion

I decided to implement a very simple program recursively, to see how well Java handles recursion*, and came up a bit short. This is what I ended up writing:
public class largestInIntArray {
public static void main(String[] args)
{
// These three lines just set up an array of ints:
int[] ints = new int[100];
java.util.Random r = new java.util.Random();
for(int i = 0; i < 100; i++) ints[i] = r.nextInt();
System.out.print("Normal:"+normal(ints,-1)+" Recursive:"+recursive(ints,-1));
}
private static int normal(int[] input, int largest) {
for(int i : input)
if(i > largest) largest = i;
return largest;
}
private static int recursive(int[] ints, int largest) {
if(ints.length == 1)
return ints[0] > largest ? ints[0] : largest;
int[] newints = new int[ints.length - 1];
System.arraycopy(ints, 1, newints, 0, ints.length - 1);
return recursive(newints, ints[0] > largest ? ints[0] : largest);
}
}
And that works fine, but as it's a bit ugly I wondered if there was a better way. If anyone has any thoughts/alternatives/syntactic sugar to share, that'd be much appreciated!
P.s. If you say "use Lisp" you win nothing (but respect). I want to know if this can be made to look nice in Java.
*and how well I handle recursion
Here's how I might make the recursive method look nicer:
private static int recursive(int[] ints, int largest, int start) {
if (start == ints.length) {
return largest;
}
return recursive(ints, Math.max(ints[start], largest), start + 1);
}
This avoids the expensive array copy, and works for an empty input array. You may implement an additional overloaded method with only two parameters for the same signature as the iterative function:
private static int recursive(int[] ints, int largest) {
return recursive(ints, largest, 0);
}
2 improvements:
no copy of the array (just using the offset)
no need to give the current max
private static int recursive(int[] ints, int offset) {
if (ints.length - 1 == offset) {
return ints[offset];
} else {
return Math.max(ints[offset], recursive(ints, offset + 1));
}
}
Start the recursion with recursive(ints, 0).
You could pass the current index as a parameter rather than copying almost the entire array each time or you could use a divide and conquer approach.
public static int max(int[] numbers) {
int size = numbers.length;
return max(numbers, size-1, numbers[size-1]);
}
public static int max(int[] numbers, int index, int largest) {
largest = Math.max(largest, numbers[index]);
return index > 0 ? max(numbers, index-1, largest) : largest;
}
... to see how well Java handles recursion
The simple answer is that Java doesn't handle recursion well. Specifically, Sun java compilers and Hotspot JVMs do not implement tail call recursion optimization, so recursion intensive algorithms can easily consume a lot of stack space.
However, I have seen articles that say that IBM's JVMs do support this optimization. And I saw an email from some non-Sun guy who said he was adding it as an experimental Hotspot extension as a thesis project.
Here's a slight variation showing how Linked Lists are often a little nicer for recursion, where "nicer" means "less parameters in method signature"
private static int recursive(LinkedList<Integer> list) {
if (list.size() == 1){
return list.removeFirst();
}
return Math.max(list.removeFirst(),recursive(list));
}
Your recursive code uses System.arrayCopy, but your iterative code doesn't do this, so your microbenchmark isn't going to be accurate. As others have mentioned, you can clean up that code by using Math.min and using an array index instead of the queue-like approach you had.
public class Maximum
{
/**
* Just adapted the iterative approach of finding maximum and formed a recursive function
*/
public static int max(int[] arr,int n,int m)
{
if(m < arr[n])
{
m = arr[n];
return max(arr,n - 1,m);
}
return m;
}
public static void main(String[] args)
{
int[] arr = {1,2,3,4,5,10,203,2,244,245,1000,55000,2223};
int max1 = max(arr,arr.length-1,arr[0]);
System.out.println("Max: "+ max1);
}
}
I actually have a pre made class that I setup for finding the largest integer of any set of values. You can put this class into your project and simply use it in any class like so:
System.out.println(figures.getLargest(8,6,12,9,120));
This would return the value "120" and place it in the output. Here is the methods source code if you are interested in using it:
public class figures {
public static int getLargest(int...f) {
int[] score = new int[f.length];
int largest=0;
for(int x=0;x<f.length;x++) {
for(int z=0;z<f.length;z++) {
if(f[x]>=f[z]) {
score[x]++;
}else if(f[x]<f[z]) {
}else {
continue;
}
if(z>=f.length) {
z=0;
break;
}
}
}
for(int fg=0;fg<f.length;fg++) {
if(score[fg]==f.length) {
largest = f[fg];
}
}
return largest;
}
}
The following is a sample code given by my Java instructor, Professor Penn Wu, in one of his lectures. Hope it helps.
import java.util.Random;
public class Recursion
{
static int s = 0;
public static Double max(Double[] d, int n, Double max)
{
if (n==0) { return max;}
else
{
if (d[n] > max)
{
max = d[n];
}
return max(d, n-1, max);
}
}
public static void main(String[] args)
{
Random rn = new Random();
Double[] d = new Double[15];
for (int i=0; i
{
d[i] = rn.nextDouble();
System.out.println(d[i]);
}
System.out.print("\nMax: " + max(d, d.length-1, d[0]));
}
}
Here is my alternative
public class recursion
{
public static int max( int[] n, int index )
{
if(index == n.length-1) // If it's simple, solve it immediately:
return n[index]; // when there's only one number, return it
if(max(n, index+1) > n [index]) // is one number bigger than n?
return max(n, index+1); // return the rest, which contains that bigger number
return n[index]; // if not, return n which must be the biggest number then
}
public static void main(String[] args)
{
int[] n = {100, 3, 5, 1, 2, 10, 2, 15, -1, 20, -1203}; // just some numbers for testing
System.out.println(max(n,0));
}
}

Categories

Resources