I'm trying to make a code for finding kth smallest element using partitions.
When I run this code, I think it works pretty fine for less than 5 numbers, but whenever I use bigger data--like over 100 or 10000 datas--and try to find kth number, it keeps giving me back java.lang.StackOverflowError message, or just simply complete wrong answer.
Here is my code:
import java.util.Scanner;
public class test2 {
public static int partition(double arr[], int begin, int end){
int pivotIndex = begin;
double pivot = arr[pivotIndex];
begin++;
int p = begin;
int r = (int) end;
while (p <= r){
while (p <= r && arr[p] < pivot)
p++;
while(p<=r && arr[r] > pivot)
r--;
if (p > r){
swap(arr, pivotIndex, r);}
else {swap(arr, p, r);
}
}
return r;}
public static void swap(double[] arr, int a, int b){
if (a <= b){
double temp;
temp = arr[a];
arr[a] = arr[b];
arr[b] = temp; }
}
public static double selection(double[] arr, int begin, int end, int k){
int pivotIndex = begin;
int i = pivotIndex;
pivotIndex = partition(arr, begin, end);
if (i == k-1){
return arr[(k-1)];
}
else if (i > k-1 ){
return selection(arr, begin, i-1, k);
}
else{
return selection(arr, i+1, end, k-i); }}
public static void main (String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Selection Test\n");
int n, i;
System.out.println("Enter number of elements");
n = scan.nextInt();
double arr[] = new double [n];
System.out.println("\nEnter "+ n +" elements");
for (i = 0; i < n; i++)
arr[i] = scan.nextDouble();
System.out.println("\nEnter the kth smallest element you'd like to find");
int k = scan.nextInt();
kthsmall(arr, k);
System.out.println(arr[k-1
]);
}
public static void kthsmall(double[] arr, int k){
selection(arr, 0, arr.length -1, k);}
I would really appreciate if anyone could tell me where my mistakes are.
Related
Attempting to implement QuickSort using Hoare Partition Scheme, but I am running into a problem where changing the index of the pivot causes overflow, regardless of array size. Code:
public void quickSort(int[] l, int min, int max){
if (min < max){
int p = partition(l, min, max);
quickSort(l, min, p);
quickSort(l, p+1, max);
}
}
public int partition(int[] l, int min, int max){
int pivot = l[min];
int i = min - 1;
int j = max +1;
while(true){
do{
i++;
}while(l[i] < pivot);
do{
j--;
}while(l[j] > pivot);
if (i >= j) {
return j;
}
//Swap
int temp = l[i];
l[i] = l[j];
l[j] = temp;
}
}
This implementation chooses the low-index (named min here) as the pivot element, and this works just fine. However, changing the pivot element to any other index, causes a StackOverflow Error regardless of the size of the array that is being sorted. (Error refers to line 3, where partition() is called) I would preferably have the pivot element chosen at random within the (min,max) range. What is causing this?
EDIT:
The array used is generated as follows:
public static int[] generateRandomArray(int size, int lower, int upper){
int[] random = new int[size];
for (int i = 0; i < random.length; i++) {
int randInt = ThreadLocalRandom.current().nextInt(lower, upper+1);
random[i] = randInt;
}
return random;
}
In one of the Overflow cases I used this:
genereateRandomArray(10, 0, 9);
For some concrete examples, running the code above but changing the pivot element to say, l[max-1] or l[min+1], l[min+2] etc gives StackOverflow on my end.
The solution to my problem was as user MBo pointed out to swap the pivot element to the first index of the array, as the algorithm itself relies on the pivot being on index 0. This is what I had overlooked.
(int i = min - 1; is correct however, and stays that way.)
We can see that at the first step i becomes equal to min, comparison of pivot element with itself fails and increment does not occur more:
int pivot = l[min];
int i = min - 1;
...
do{
i++;
}while(l[i] < pivot);
Exclude pivot element from comparison (int i = min;) and exchange it with partition one (seems l[j]) at the end
Using the middle value for pivot is working for me. Here is a complete example:
public static void quickSort(int[] l, int min, int max){
if (min < max){
int p = partition(l, min, max);
quickSort(l, min, p);
quickSort(l, p+1, max);
}
}
public static int partition(int[] l, int min, int max){
int pivot = l[(min+max)/2];
int i = min - 1;
int j = max + 1;
while(true){
do{
i++;
}while(l[i] < pivot);
do{
j--;
}while(l[j] > pivot);
if (i >= j) {
return j;
}
int temp = l[i];
l[i] = l[j];
l[j] = temp;
}
}
public static int[] generateRandomArray(int size, int lower, int upper){
int[] random = new int[size];
for (int i = 0; i < random.length; i++) {
int randInt = ThreadLocalRandom.current().nextInt(lower, upper+1);
random[i] = randInt;
}
return random;
}
public static void main(String[] args) {
int[] A = generateRandomArray(10, 0, 9);
long bgn, end;
bgn = System.currentTimeMillis();
quickSort(A, 0, A.length-1);
end = System.currentTimeMillis();
for(int i = 1; i < A.length; i++){
if(A[i-1] > A[i]){
System.out.println("failed");
break;
}
}
System.out.println("milliseconds " + (end-bgn));
}
I am (for homework, if that influences how the answers are given) supposed to be writing a class for quicksort to sort an array from a txt or dat file provided by the user. I present the user with an menu option in the driver class to select sort type and then pass the array to the quicksort class for sorting, after which it is returned back to the driver class to be written to a file. I believe the return is operating correctly, as I get the unsorted array printed to the file, however it only appears to be making one pass through the quicksort loop (determined by only one printing of the "Last Loop" string to the console.
The addition bits of code are part of the assignment, allowing a user to select Insertion sort to complete the sort if only 50 or 100 items remain in the unsorted array based on user choice. I have tried running this program commenting those two if statements out and still the program does not correctly sort. I am certain the input is read correctly as I have an addition HeapSort class that does sort correctly, and insertion sort returns correctly if that option is selected and the file size is less than 50 or 100 ints. I cannot seem to get the QuickSort of a larger file to trigger though.
class Quicksort{
int partition(int arr[], int lb, int ub){
int i = lb, j = ub;
int temp;
int pivot = arr[0];
while (i <= j) {
while (arr[i] < pivot){
i++;
}while(arr[j] > pivot){
j--;
}if (i <= j){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}return i;
}
int[] quicksort(int arr[], int lb, int ub, boolean insertLarge, boolean insertSmall){
if (insertLarge && arr.length <= 100){
System.out.println("Large");
insertionSort(arr);
}
else if (insertSmall && arr.length <= 50){
System.out.println("Small");
insertionSort(arr);
}
else{
int[] intArrCopy = new int[arr.length-1];
for (int z = 1; z < intArrCopy.length; z++){
intArrCopy[z-1] = arr[z];
}
System.out.println("Last Loop");
int index = partition(arr, lb, ub);
if (lb < index-1){
quicksort(intArrCopy, lb, index-1, insertLarge, insertSmall);
}if (index < ub){
quicksort(intArrCopy, index, ub, insertLarge, insertSmall);
}
}return arr;
}
public static void insertionSort(int x[]){
int h, k, y;
for (k=1; k < x.length; k++){
y = x[k];
for (h = k-1; h>=0 && y < x[h]; h--){
x[h+1] = x[h];
}x[h+1] = y;
}
}
}
And how it is being called from driver class:
private static void processChoice(int choice, int[] intArr){
switch (choice){
case 1:
p = intArr.length;
output = new int[p];
startTime = System.nanoTime();
output = quick.quicksort(intArr, intArr[0], intArr[p-1], insertLarge, insertSmall);
endTime = System.nanoTime();
System.out.println("Time Taken: "+(endTime-startTime)+" nanoseconds.");
break;
}
}
Assuming a hybrid sort, try something like this:
void quicksort(int arr[], int lb, int ub){
if ((ub-lb) < 100){ // < 32 may be fastest
System.out.println("Large");
insertionSort(arr, lb, ub);
return;
}
int index = partition(arr, lb, ub);
if (lb < index-1)
quicksort(arr, lb, index-1);
if (index < ub)
quicksort(arr, index, ub);
}
public static void insertionSort(int x[], int lb, int ub){
int h, k, y;
for (h = lb+1; h <= ub; h++){
y = x[h];
for (k = h; k > lb && x[k-1] > y; k--)
x[k] = x[k-1];
x[k] = y;
}
}
Sorry, beginner here.This is what I have right now:
public class MergeSort
{
public static void main(String[] args)
{
int[] arr = {3, 5, 2, 4, 1};
sort(arr, 0, arr.length - 1);
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i] + " ");
}
}
private static void sort(int[] arr, int lo, int hi)
{
if(lo >= hi)
{
return;
}
int mid = (lo + hi)/2;
sort(arr, lo, mid);
sort(arr, mid + 1, hi);
int size = hi - lo + 1;
int[] temp = new int[size]; //new array to merge into
merge(arr, temp, lo, mid + 1, hi);
for(int i = 0; i < size; i++)
{
arr[i + lo] = temp[i];
}
}
private static void merge(int[] arr, int[] temp, int lower, int mid, int upper)
{
int tempIndex = 0;
int leftLo = lower;
int leftHi = mid - 1;
int rightLo = mid;
int rightHi = upper;
while(leftLo <= leftHi && rightLo <= rightHi)
{
if(arr[leftLo] < arr[rightLo])
{
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
}
else
{
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
}
}
}
I know it's the merge function that is not working, because right now it prints out only the smallest element and the rest as 0's. I think it has something to do with needing another while loop to copy the array, but I don't know how to write that, or even the purpose of it, as right now it seems that the array is being merged into the temp array in a correct order. Why is it only printing the first element correctly? Thanks.
In merge, you copy values as long as leftLo and rightLo both haven't reached their limit yet. Typically one of them reaches early. Then you need to copy the remaining values of the other one. You can copy the remaining elements by adding these two loops:
while (leftLo <= leftHi) {
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
}
while (rightLo <= rightHi) {
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
That is, the complete method becomes:
private static void merge(int[] arr, int[] temp, int lower, int mid, int upper) {
int tempIndex = 0;
int leftLo = lower;
int leftHi = mid - 1;
int rightLo = mid;
int rightHi = upper;
while (leftLo <= leftHi && rightLo <= rightHi) {
if (arr[leftLo] < arr[rightLo]) {
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
} else {
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
}
while (leftLo <= leftHi) {
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
}
while (rightLo <= rightHi) {
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
}
I've been banging my head on the table on this one.
I need to create an n sized array that is optimized for QuickSort Partition. It will be used to demonstrate the growth of QuickSort's best case. I know that for best case, QuickSort must select a pivot that divides the array in half for every recursive call.
I cannot think of a way to create an n-sized optimized array to test. Any help would be greatly appreciated.
Here is the algorithm in Java.
public class QuickSort {
private int length;
private void quickSort(int[] a, int p, int r) {
if (p < r) {
int q = partition(a, p, r);
quickSort(a, p, q - 1);
quickSort(a, q + 1, r);
}
}
private int partition(int[] a, int p, int r) {
int x = a[r];
int i = p - 1;
for (int j = p; j < r; j++) {
if (a[j] <= x) {
i++;
exchange(a, i, j);
}
}
exchange(a, i + 1, r);
return i + 1;
}
public void exchange(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
QuickSort(int[] a) {
if (a == null || a.length == 0) {
return;
}
length = a.length;
quickSort(a, 0, length - 1);
}
}
I know this is an old question, but I had the same question and finally developed a solution. I'm not a Java programmer, so don't blame me for Java code issues, please. I assumed that the quicksort algorithm always takes the first item as a pivot when partitioning.
public class QuickSortBestCase
{
public static void generate(int[] arr, int begin, int end)
{
int count = end - begin;
if(count < 3)
return;
//Find a middle element index
//This will be the pivot element for the part of the array [begin; end)
int middle = begin + (count - 1) / 2;
//Make the left part best-case first: [begin; middle)
generate(arr, begin, middle);
//Swap the pivot and the start element
swap(arr, begin, middle);
//Make the right part best-case, too: (middle; end)
generate(arr, ++middle, end);
}
private static void swap(int[] arr, int i, int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
private static void fillArray(int[] arr)
{
for(int i = 0; i != arr.length; ++i)
arr[i] = i + 1;
}
private static void printArray(int[] arr)
{
for(int item : arr)
System.out.print(item + " ");
}
public static void main(String[] args)
{
if(args.length == 0)
return;
int intCount = Integer.parseInt(args[0]);
int[] arr = new int[intCount];
//We basically do what quicksort does in reverse
//1. Fill the array with sorted values from 1 to arr.length
fillArray(arr);
//2. Recursively generate the best-case array for quicksort
generate(arr, 0, arr.length);
printArray(arr);
}
}
This program produces the same output for the array of 15 items, as described here: An example of Best Case Scenario for Quick Sort. And in case someone needs a solution in C++:
template<typename RandomIterator,
typename Compare = std::less<typename RandomIterator::value_type>>
void generate_quicksort_best_case_sorted(RandomIterator begin, RandomIterator end)
{
auto count = std::distance(begin, end);
if (count < 3)
return;
auto middle_index = (count - 1) / 2;
auto middle = begin + middle_index;
//Make the left part best-case first
generate_quicksort_best_case_sorted(begin, middle);
//Swap the pivot and the start element
std::iter_swap(begin, middle);
//Make the right part best-case, too
generate_quicksort_best_case_sorted(++middle, end);
}
template<typename RandomIterator,
typename Compare = std::less<typename RandomIterator::value_type>>
void generate_quicksort_best_case(RandomIterator begin, RandomIterator end)
{
{
auto current = begin;
RandomIterator::value_type value = 1;
while (current != end)
*current++ = value++;
}
generate_quicksort_best_case_sorted(begin, end);
}
I am implementing a quicksort algorithm and have succesfully partitioned the input array around a pivot. The problem is, I am confused in how to recursively sort the 1st part and 2nd part of the array (i.e specifying the range) using the same input array.
Below is my implementation
class QuickSort {
int i;
int l = 0;
public void quicksort(int A[], int n) {
if (n == 1) {
return;
} else {
partition(A, 0, n);
//----Confused as from this point
quicksort(A, A[i]);
//Recursively sort both parts of the array
}
}
public int partition(int A[], int l, int r) {
int p = A[l];//Choose pivot
i = l + 1;
//Partition around A through P
for (int j = i; j < r; j++) {
if (A[j] < p) {
swap(A, i, j);
++i;
}
}
swap(A, l, i - 1 );
return i;
}
public void swap(int A[], int i, int j) {
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
public void display(int A[]){
for (int i = 0; i < A.length; i ++){
System.out.print(A[i] + " ");
}
}
}
class QuickSortApp{
public static void main(String args[]){
QuickSort quick = new QuickSort();
int A[] = {6,2,7,8,4,3,5};
quick.quicksort(A, A.length);
quick.display(A);
}
}
Please, I would also appreciate being corrected on any other inefficencies in my algorithm. Thanks
Change your quicksort() signature to quicksort(int[] A, int begin, int end)
Since, you actually did the sorting inside partition(). What I would do is this:
if (end-begin <= 1) {
return;
} else {
int pivot = partition(A, begin, end);
quicksort(A, begin, pivot);
quicksort(A, pivot, end);
}
Create a wrapper for the quicksort call with the signature you have which calls another one like quicksort(A, i, j) and your call from the wrapper will be quicksort(A, 0, n).