QuickSort stack overflow on big input - java

My QuickSort implementation causes StackOverflow error if I give reverse-sorted array. It is working fine for about 1000 items, but for 10000+ I get StackOverflow error. If I get the error the recursion depth is about 9000. I know my algorithm always choose the latest element of the subarray as pivot, which is not optimal, but I would not change that, because I want to make it work like this.
Here is the code:
private int partition(int[] numbers, int begin, int end) {
int pivot = numbers[end];
int partitionIndex = begin;
for (int i = begin; i < end; ++i) {
comparisonCounter++;
if (numbers[i] <= pivot) {
if (i != partitionIndex) {
swapCounter++;
int temp = numbers[i];
numbers[i] = numbers[partitionIndex];
numbers[partitionIndex] = temp;
}
partitionIndex++;
}
}
if (partitionIndex != end) {
swapCounter++;
int temp = numbers[partitionIndex];
numbers[partitionIndex] = numbers[end];
numbers[end] = temp;
}
return partitionIndex;
}
private void quickSort(int[] numbers, int begin, int end) {
if (begin < end) {
int partitionIndex = partition(numbers, begin, end);
quickSort(numbers, begin, partitionIndex - 1);
quickSort(numbers, partitionIndex + 1, end);
}
}
Is there something wrong with my implementation? How could I fix it?
Thank you!

Nothing seems wrong with your code but bear in mind that this is a recursive function and most languages have a limited-depth stack which you are bound to get to if you have an input large enough. For Java, see:
What is the maximum depth of the java call stack?
How to predict the maximum call depth of a recursive method?
What you could do is turn your method, from a recursive to an iterative approach. There are several ways you can do it. I just found a couple examples online:
http://www.geeksforgeeks.org/iterative-quick-sort/
http://kosbie.net/cmu/summer-08/15-100/handouts/IterativeQuickSort.java

There are some ways to reduce the stack size in Quicksort.
You could put the larger partition on the stack each time, leaving it there while the smaller partition is sorted first. That keeps too many small partitions from clogging up the stack at once.
You could use another method, such as insertion sort, to sort small partitions rather than Quicksort. Again, that keeps a lot of small partitions off the stack. You can experiment, but something in the region of 15 - 20 elements usually counts as a "small" partition.
As you say, using a better method to pick your pivot would also help.

Related

Recursive function that finds the minimum value in an ArrayList of Integers in Java

This is a problem that I have been thinking about as part of self-learning java. The problem consists of writing a recursive function that finds the minimum value in an ArrayList of Integers. Below you will find my attempt. I believe that it is working as intended, but I wonder if there are better ways to get this done. Any comments are appreciated.
public static int findMin(ArrayList<Integer> numbers){
// Base Case
if(numbers.size()==1){
return numbers.get(0).intValue();
}
ArrayList<Integer> numbers_short = new ArrayList<Integer>(numbers);
numbers.remove(numbers.size()-1);
return Math.min(numbers_short.get(numbers_short.size()-1).intValue(), findMin(numbers));
}
Your example is not so good in the way that you should not use recursivity in this case.
But anyway, you can avoid to copy your array each time, by using a method with a start and end parameters to analyze only a part of your initial array.
Something like that:
public static int findMin(ArrayList<Integer> numbers) {
return findMin(numbers, 0, numbers.size() - 1);
}
public static int findMin(ArrayList<Integer> numbers, int start, int end) {
if (end == start)
return numbers.get(start);
int middle = start + (end - start) / 2;
return Math.min(findMin(numbers, start, middle), findMin(numbers, middle + 1, end));
}
And add a check in case the array is empty if needed.
The reason why I'm using the "middle" method is that each time it divides the array by 2, meaning at the end it limits the risk of stack overflow because it will divide by 2 the maximum number of recursivity compare to recurse on every element.
You may want to avoid creating a new ArrayList object every time you call the method. This may seem non consequential for smaller inputs, but for really large inputs it may lead to StackOverflowError.
A cleaner solution with similar time complexity can be:
public static int findMin(ArrayList<Integer> numbers) {
return findMin(numbers, numbers.size());
}
public static int findMin(ArrayList<Integer> numbers, int len) {
// Base Case
if (len == 1) {
return numbers.get(0);
}
return Math.min(numbers.get(len-1), findMin(numbers, len-1));
}
Reference to garbage collector and recursion: Garbage Collection in Java with Recursive Function
Keeping the original method signature and formal parameters - the code can be simplified by eliminating the need to create a new temporary array each time.
public static int findMin(ArrayList<Integer> numbers){
if (numbers.size() == 1) return numbers.get(0);
return Math.min(numbers.remove(0), findMinimum(numbers));
}
numbers.remove(0) will simultaneously remove the first element and return the value of the removed element, eliminating the need to create a temporary array.

Why is my Parallel Code Running Slow?

I have been asked to use ForkJoin to write the following method:
public static boolean hasOver(int val, int[] arr, int sequentialCutoff)
and my code must have a work of O(n), a span of O(lgn) and use the sequentialCutoff argument. I figured this problem would be fairly straight forward and one in which sequentialCutoff would set the threshold, and the recursion would occur over half the array, repeating until hitting the threshold, before hitting the base case which would just traverse the array over the specified indices, checking to see if any elements greater than val exist. Here is the parallel code portion of what I wrote:
protected Boolean compute() {
if(high - lo <= sequentialCutoff) {
return sequentialCheckHasOver(check, array);
} else {
int mid = lo + (high - lo) / 2;
CheckHasOver left = new CheckHasOver(check, array, lo, mid, cutoff);
left.fork();
CheckHasOver right = new CheckHasOver(check, array, mid, high, cutoff);
return right.compute() || left.join();
}
}
The problem is that when I now run timing tests on the code that I wrote, no matter what threshold I choose, the sequential code is running faster than my parallel code and I cannot, for the life of me, figure out where my reasoning has failed. Can anyone see a mistake in what I've done? Some help would be much appreciated.

StackOverflowError while implementing QuickSort

I am trying to implement an QuickSort algorithm on an ArrayList. However, I am getting a
Exception in thread "main" java.lang.StackOverflowError
at sorting.QuickSort.quickSort(QuickSort.java:25)
at sorting.QuickSort.quickSort(QuickSort.java:36)
at sorting.QuickSort.quickSort(QuickSort.java:36)
at sorting.QuickSort.quickSort(QuickSort.java:36)
at sorting.QuickSort.quickSort(QuickSort.java:36)
...
I am not sure as to why is there an overflow. Below is my implementation:
public static void quickSort(ArrayList<Integer> al, int fromIdx, int toIdx) {
int pivot, pivotIdx;
if (fromIdx < toIdx) {
pivot = al.get(fromIdx);
pivotIdx = fromIdx;
for (int i = 0; i != (fromIdx + 1); i++) {
if (al.get(i) <= pivot) {
pivotIdx += 1;
swap(al, pivotIdx, i);
}
}
swap(al, fromIdx, pivotIdx);
quickSort(al, fromIdx, pivotIdx - 1);
quickSort(al, pivotIdx + 1, toIdx);
}
}
public static void swap(ArrayList<Integer> al, int xIdx, int yIdx) {
Integer temp = al.get(xIdx);
al.set(xIdx, al.get(yIdx));
al.set(yIdx, temp);
}
If you're trying to sort the chunk of the array from fromIdx to toIdx, you should never look at element 0 unless it is in that chunk. But your implementation does.
Your indexing trick in the middle is also kind of..odd. I highly recommend that you do the exercise of cutting out little pieces of paper for the array, and writing tracking information on a sheet of paper so that you can trace through the algorithm. If when you physically do it, you see it not make sense, then it doesn't make sense in the computer either. And the act of holding physical pieces of paper may seem silly, but is of great help in visualizing exactly what you are doing. (The more precisely you can visualize what your code actually does, the more likely you are to be able to figure out if it is doing something wrong.)

Stackoverflow error while implementing insertion sort using recursion

public static void insertionSortRecursion(String a[], int first, int last) {
if (first < last) {
//sort all but last
insertionSortRecursion(a, first, last - 1);
insertInOrder(a[last], a, first, last -1);
}
}
private static void insertInOrder(String element, String[] a, int first, int last) {
// System.out.println(last - 1);
// System.out.println(element);
// System.out.println(a[last]);
if (element.compareTo(a[last]) >= 0) {
a[last + 1] = element;
} else if(first < last) {
a[last + 1] = a[last];
insertInOrder(element, a, first, last - 1);
} else {
a[last + 1] = a[last];
a[last] = element;
}
}
Hey Guys,
I am trying to implement insertion sort using recursion it's working fine on small number of words but I am getting stackoverflow after implementing it because the size of file I am sorting have a lot of words around 10,000. Please suggest what should I do to remove the error.
These are the methods I am using for insertion sort using recursion and I am calling them in my constructor.
Assuming your algorithm is correct, leave this function as it is. Don't try to fix it. In general to get rid of stack overflow (while keeping recursion) there are two solutions :
Use tail call optimization
Increase the stack size
But let sit back and assume this code is going to be anything else than a programming exercise. Any other person who have to read will think :
Why does he use insertion sort ?
Why does he re-implement insertion-sort?
Did it have to be recursion?? my lord!!
Why did he waste his time to find a tail-call insertion algorithm? Or
Did he just increased the stack size for the only sake of running his method?
Good. Now we have 1000,000 items to sort and the program keep crashing.
Conclusion, they will erase your code at once and use Collections.sort().
As I said, if you are doing a programming exercise then good, your recursive insertion
sort work till some point. move on.
Java is not able to handle a recursion depth that deep (10000) by default. Consider the below oversimplified example still throws a StackOverflowError.
static void test(int i)
{
if (i == 0) return;
test(i-1);
}
public static void main(String[] args)
{
test(10000);
}
You must specify command-line parameters to achieve this (-Xss and possibly -Xmx to allocate more memory).
I ran your algorithm successfully for an array of size 100000 using -Xmx1000m -Xss10000000 (though it took a while).
I assume there a reason you're using recursion and not a simple double for loop.

Compare elements in an array of string in Java

I have to write a method that will compare elements in an array of strings and return the index of the largest element. It's going to be done recursively using a divide and conquer approach. I have an idea, and I was just looking to see if my idea was right or if it should be done in a different way.
I was planning on looking at the array from the left side to mid -1, then look at mid, and then look at mid +1 to right. I have a variable that will keep track of the largest index, and then after that make the recursive call for the left side and the right side.
Does that sound like a good way to approach this problem?
This is what I have so far:
public int longest()
{
longest(0, a.length-1);
return longestIndex;
}
private int longest( int left, int right)
{
int longestIndex;
int mid;
if(left > right)
{
longestIndex = -1;
}
else if(left == right)
{
longestIndex = 0;
}
else
{
longestIndex = 0;
mid = (left + right) / 2;
longest(left, mid - 1);
if (a[mid].compareTo(a[longestIndex]) > 0)
{
longestIndex = mid;
}
longest(mid + 1, right);
}
return longestIndex;
}
Also, since the methods are supposed to return an int, how would I pass the longestIndex n the private method up to the public method so that it would show up in my test program when longest is called?
Does it have to be recursive? Using recursion for this sounds like a case of:
And your recursion looks totally wrong anyways, because not only you are not keeping track of the actual index but also your base cases and recursive calls don't make any sense.
If I were compelled to use recursion, I would do something like:
int longest(array):
return longest_helper(0, 0, array)
int longest_helper(max_index, curr_idx, array):
# base case: reached the end of array
if curr_idx == array.length:
return max_index
if array[curr_idx].length > array[max_index].length:
max_index = curr_idx
# recursive call
return longest_helper(max_index, curr_idx + 1, array)
And then I would proceed to drop the class and tell the professor to give students problems where recursion is actually helpful next time around.
Since it doesn't look like the array is sorted, the easiest (and fastest) way to do this would just be go through the whole thing (pseudocode):
max_index = 0
max_length = array[0].length
for index in 1 .. array.length:
if array[index].length > max_length:
max_length = array[index].length
max_index = index
return max_index
This is your fourth question in two days on recursion. It is good that you are putting homework tag but your time would be better spent understanding how recursion works.
My recommendation is to take a few colored discs (poker chips or playing cards of a single suite work well), work out manually the recursive solution to Towers of Hanoi and then come back and look at the individual questions you have been asking.
In all likelihood you will be able to answer all the questions yourself. You would also be able to accept the answers, increasing you chances in future of getting responses when you face tougher questions.

Categories