I want to solve this problem: https://www.hackerrank.com/challenges/find-median ,ie find the median element in unsorted array. To do this I perform quickselect algorithm.
My program works correctly on my computer. However when I submitted in the system it gave me StackOverflowError. I think this is because of the depth of the recursion calls. I guess I make too many recursion calls than Java allows (the error is caused by a test case with 10 001 numbers). Can someone suggest me how to avoid this?
This is my code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class FindMedian {
static int res;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
String line1 = br.readLine();
int N = Integer.parseInt(line1);
String[] line2 = br.readLine().split(" ");
int[] arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = Integer.parseInt(line2[i]);
}
selectKth(arr, 0, N - 1);
System.out.println(res);
}
public static void selectKth(int[] arr, int start, int end) {
// it is written to select K-th element but actually
// it selects the median element
if (start >= end) {
res = arr[start];
return;
}
int pivot = arr[start];
int n = arr.length - 1;
int i = start + 1;
int j = end;
while (i <= j) {
while (arr[i] <= pivot) {
i++;
}
while (pivot < arr[j]) {
j--;
}
if (i < j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
int tmp = arr[j];
// j is the index of the last element which is bigger or equal to pivot
arr[j] = pivot;
arr[start] = tmp;
if (n / 2 <= j) {
selectKth(arr, start, j);
} else {
selectKth(arr, i, end);
}
}
}
A basic concept of IT science is to replace recursion with iteration. By using iteration you'll never run into "recursion too deep" errors. Every problem can be solved either by recursion or by iteration.
See my link for details. http://www.refactoring.com/catalog/replaceRecursionWithIteration.html
Since you only call selectKth
at the very end of itself (it's practically tail recursive already), it's trivial to unroll your recursion into an iteration.
func foo(param) {
if (param == bar) //bailout condition
return;
x = doStuff(param); //body of recursive method
foo(x); //recursive call
}
Can be rewritten as
func foo(param) {
x = param;
while ( x != bar ) {
x = doStuff( x );
}
}
In your case the only tricky bit is the last if
:
if(n/2 <= j){
selectKth(arr, start, j);
}
else{
selectKth(arr, i, end);
}
So your method will look like this:
public static void selectKth(int[] arr, int start, int end) { //it is written seclect K-th element but actually it selects the median element
while( start < end ) {
//method body as is up to the last `if`
if(n/2 <= j) { //instead of recursion we just adjust start and end, then loop
end = j;
}
else{
start = i;
}
}
res = arr[start];
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.