I'm having a bit of a problem writing a recursive function that sorts an array in java recursively . Right now it appears as though I have an infinite loop, and I can't seem to figure out where.
The primary function "rec_piv" searches from index1 to the pivot point and sorts the first half, then switches from the pivot point to the length of the array and sorts the second half. All of the comparisons are recorded by an int. [The array is of random size from 2 to 2014]
Thanks very much in advance!
public class Recursive_pivot {
private Random random_size = new Random();
private int size = random_size.nextInt(1024) + 2;
public int[] a = new int[size];
private Random elements = new Random();
/* variable for rec_piv */
public int temp=0;
public int temp2=0;
private Random rand_pivot = new Random();
private int pivot = rand_pivot.nextInt(size) + 2;
private int first_half =a[0+1];
private int second_half=a[pivot+1];
private int first_index=0; //first half of the array
private int second_index=pivot; //second half of the array
//The pivot is randomly chosen.
public int comparisons =0; //count the number of comparisons.
public void fill(){
for (int q=0; q<a.length; q++) {
/* filling the array */
a[q] = elements.nextInt(100 ) + 1;
}
}
public void rec_piv(int first_index, int second_index) {
if(first_index < pivot) {
if(first_half > a[first_index]) {
comparisons++;
temp = a[first_index];
a[first_index] = a[first_half];
a[first_half] = temp;
}
rec_piv(first_index+1, second_index);
}
if(second_index < a.length) {
if(second_half > a[second_index]) {
comparisons++;
temp2 = a[second_index];
a[second_index] = a[second_half];
a[second_half] = temp2;
}
rec_piv(first_index, second_index+1);
}
} //end of rec_piv
}//end of class.
You are trying to do a QSort here is a simple version of it.
public void quickSort(int array[], int start, int end)
{
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) // check that there are at least two elements to sort
{
int pivot = array[start];
while (k > i)
{
while (array[i] <= pivot && i <= end && k > i)
i++;
while (array[k] > pivot && k >= start && k >= i)
k--;
if (k > i)
swap(array, i, k);
}
swap(array, start, k);
quickSort(array, start, k - 1); // quicksort the left partition
quickSort(array, k + 1, end); // quicksort the right partition
}
else // if there is only one element in the partition, do not do any sorting
{
return; // the array is sorted, so exit
}
}
public void swap(int array[], int index1, int index2)
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
int temp = array[index1]; // store the first value in a temp
array[index1] = array[index2]; // copy the value of the second into the first
array[index2] = temp; // copy the value of the temp into the second
}
from this site.
http://www.mycstutorials.com/articles/sorting/quicksort
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 have some problems with java.lang.ArrayIndexOutOfBoundsException: 10,
if i set 1 instead of 0 - i will have sorted array with unsorted first element, if i set 0 - i have error
public void quicksort() {
// Recursion
quicksort(0, counter - 1);
}
Here is all my code
public class Main {
private static int comparations = 0;
private static int swaps = 0;
int[] array;
int[] a;
int counter = 0;
int size;
public void qwe() throws IOException {
Scanner scan = new Scanner(new File("input.txt")); //provide file name from outside
while(scan.hasNextInt())
{
counter++;
scan.nextInt();
}
System.out.println(counter);
Scanner scan2 = new Scanner(new File("input.txt"));
a = new int[counter];
for(int i=0;i<counter;i++)
{
a[i]=scan2.nextInt(); //fill the array with the integers
}
}
public int partition(int p, int q) {
int i = p;
int j = q + 1;
// Get the pivot element from the middle of the list
int pivot = a[p];
// Divide into two lists
do {
// If the current value from the left list is smaller then the pivot
// element then get the next element from the left list
do {
i++;// As we not get we can increase i
} while (a[i] < pivot);
// If the current value from the right list is larger then the pivot
// element then get the next element from the right list
do {
j--;// As we not get we can increase j
} while (a[j] > pivot);
// If we have found a values in the left list which is larger then
// the pivot element and if we have found a value in the right list
// which is smaller then the pivot element then we exchange the
// values.
if (i < j) {
swap(i, j);
}
} while (i < j);
// swap the pivot element and j th element
swap(p, j);
return j;
}
private void swap(int p, int j) {
// exchange the elements
int temp = a[p];
a[p] = a[j];
a[j] = temp;
swaps++;
}
public void quicksort() {
// Recursion
quicksort(0, counter - 1);
}
public void quicksort(int p, int q) {
int j;
if (p < q) {
// Divide into two lists
j = partition(p, q);
// Recursion
quicksort(p, j - 1);
quicksort(j + 1, q);
}
comparations++;
}
public void print() {
// print the elements of array
for (int i = 0; i < counter; i++) {
System.out.print(a[i] + ",");
}
System.out.println();
}
public static void main(String args[]) throws IOException {
Main q = new Main();
q.qwe();
System.out.println("Before Sort <<<<<<<<<<<<<<<<<<<<<");
q.print();
q.quicksort();
System.out.println("After Sort > > > > > > > > > > > >");
q.print();
System.out.println("Comparisons: " + comparations);
System.out.println("Swaps: " + swaps);
}
}
use the condition in partition method
while(a[i] < pivot && i<q)
instead of
while(a[i] < pivot)
because you have to stop searching bigger value than pivot when you reach at the the end
I think you have to avoid do{...} while and use while instead.
Something like:
public int partition(int p, int q) {
int i = p;
int j = q + 1;
// Get the pivot element from the middle of the list
int pivot = a[p];
// Divide into two lists
while (i < j) {
// If the current value from the left list is smaller then the pivot
// element then get the next element from the left list
while (a[i] < pivot) {
i++;// As we not get we can increase i
}
// If the current value from the right list is larger then the pivot
// element then get the next element from the right list
while (a[j] > pivot) {
j--;// As we not get we can increase j
}
// If we have found a values in the left list which is larger then
// the pivot element and if we have found a value in the right list
// which is smaller then the pivot element then we exchange the
// values.
if (i < j) {
swap(i, j);
}
}
// swap the pivot element and j th element
swap(p, j);
return j;
}
I suspect your partition code isn't correct. As swap should be done on basis of value not on index.
if (i < j) {
swap(i, j);
}
Partitioning: reorder the array so that all elements with values
less than the pivot come before the pivot, while all elements with
values greater than the pivot come after it (equal values can go
either way). After this partitioning, the pivot is in its final
position. This is called the partition operation.
Also, why are you reading same file twice can't you get the number of elements and elements in same loop ?
So I'm implement a quickselect algorithm that chooses a good pivot each time. What it does is divide the array into groups of 5, sorts each groups and finds the median. It then takes the medians of each group, groups those values up and then finds the median of medians. Here's what I have:
private static int pickCleverPivot(int left, int right, int[] A){
int index = 0;
int n = right-left;
if (n <= 5) {
Arrays.sort(A);
index = n/2;
return index;
}
int numofMedians = (int) Math.ceil(n/5);
int[] medians = new int[numofMedians];
int[] groups = new int[5];
for(int i = 0; i < numofMedians; i++) {
if (i != numofMedians - 1){
for (int j = 0; j < 5; j++){
groups[j] = A[(i*5)+j];
}
medians[i] = findMedian(groups, 5);
} else {
int numOfRemainders = n % 5;
int[] remainder = new int[numOfRemainders];
for (int j = 0; j < numOfRemainders; j++){
remainder[j] = A[(i*5)+j];
}
medians[i] = findMedian(groups, 5);
}
}
return pickCleverPivot(left, left+(numofMedians), medians);
}
public static int findMedian(int[] A, int n){
Arrays.sort(A);
if (n % 2 == 0) {
return (A[n/2] + A[n/2 - 1]) / 2;
}
return A[n/2];
}
private static int partition(int left, int right, int[] array, int pIndex){
//move pivot to last index of the array
swap(array,pIndex,right);
int p=array[right];
int l=left;
int r=right-1;
while(l<=r){
while(l<=r && array[l]<=p){
l++;
}
while(l<=r && array[r]>=p){
r--;
}
if (l<r){
swap(array,l,r);
}
}
swap(array,l,right);
return l;
}
private static void swap(int[]array, int a, int b){
int tmp = array[a];
array[a] = array[b];
array[b] = tmp;
}
So it works like it's supposed to but now I'm wondering if it's possible to get it to run in linear O(n) time. I'm currently comparing this code to just choosing a random pivot. On smaller arrays this code runs faster but on larger arrays, choosing a random pivot is faster. So is it actually possible to make this run in O(n) time or is that just in theory and if it's not possible for it to run that fast then is this method running as fast as it could.
For school I have to write a quicksort for an Array of strings. I think my partition is good but I keep getting error with the recursion.
public static int partition(String[] input, int max) {
// pivot is dividing point
// I keeps track of lesser values
// count is just a counter
// Pivots in place
int pivot = 0;
int i = 1;
int count = 1;
while (count <= max) {
if (input[count].compareTo(input[pivot]) < 0) {
input[i] = input[count];
i = i + 1;
}
if (count == max) {
input[i] = input[pivot];
input[pivot] = input[i];
}
count++;
}
return pivot;
}
public static void qSort(String[] input) {
int index = partition(input, input.length - 1);
int count = 0;
if (count < index - 1) {
partition(input, index - 1);
}
if (index + 1 < count && count < input.length) {
partition(input, input.length - 1);
}
count++;
}
Your implementation has a lot of mistakes. First of all, you're always choosing the pivot to be the element at index 0 of the array. The idea of quicksort is that you choose some pivot, partition the array around it, then recursively apply quicksort on both partitions around the chosen pivot. How do you identify the start and end of the partitions you'll call quicksort recursively on? You need to pass those as arguments to your partition method as others have mentioned in the comments, same thing for the quicksort method itself.
public void swap (String [] array, int i, int j) {
String tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static int partition (String [] array, int low, int high) {
String pivot = array[low];
int index = low+1;
for (int i = low+1; i <= high; i++) {
if (pivot.compareTo(array[i]) > 0) {
swap(array, i, index);
index++;
}
}
swap(array, low, index-1);
return index-1;
}
public static void qsort(String [] array, int low, int high) {
if (low < high) {
int p = partition(array, low, high);
qsort(array, low, p);
qsort(array, p+1, high);
}
}
Obviously this isn't the best way to choose the pivot as some arrays will make the algorithm perform very poorly. A better method would be to use a random pivot each time.
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);
}