I have some questions about my code. I've marked it with ---><--- down below.
public class Main {
public static void main(String[] args) {
int[] arr = {5, 4, 3, 2, 1, 4, 5, 6, 7, 8, 10};
int[] aux = new int[arr.length];
sort(arr, aux, 0, arr.length - 1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
public static void sort(int[] arr, int[] aux, int low, int high) {
// what does these lines do? --->
if (low >= high) {
return;
}
int mid = low + (high - low) / 2; // why cant it just be high - low / 2
//<--- These lines
sort(arr, aux, low, mid); //sorts left side
sort(arr, aux, mid + 1, high); //sorts right side
merge(arr, aux, low, mid, high); //merges the two sides
}
public static void merge(int[] arr, int[] aux, int low, int mid, int high) {
for (int k = low; k <= high; k++) {
aux[k] = arr[k];
}
//copies the array into an aux array
int i = low; //counter for the left side
int j = mid + 1; //counter for the right side
for (int k = low; k <= high; k++) {
if (i > mid) { //if i > mid meaning that if the left side of the array is empty then use the right side
arr[k] = aux[j++];
}
else if (j > high) { //if j > high then right side of array has been used so use left
arr[k] = aux[i++];
}
else if (aux[i] <= aux[j]) { //if value of left side is <= value of right then bring leftside value up to original array
arr[k] = aux[i++];
}
else { //value of right side is <= value of left so bring rightside value up to original array
arr[k] = aux[j++];
}
}
}
}
This is a part of the merge from GeeksForGeeks
void merge(int arr[], int l, int m, int r) {
/* Create temp arrays */
// These lines --->
int L[] = new int [n1];
int R[] = new int [n2];
//<---
}
With G4Geek's temp arrays:
Is it getting created every single time merge is called?
Does the memory just stay there?
Is the current code that I have better practice?
Thanks.
I have a quick sort method which sorts elements in ascenidng order but seem to keep getting a stackoverflowerror.
For some reason it is showing the error on the while loop, when the logic makes sense to me.
Here is the code for the quick sort class:
public T[] sort(T[] arr, int left, int right)
{
int l = left;
int r = right;
if (right <= left)
return null;
//Find the pivot in the middle
T pivot = arr[(left + (right - left)) / 2];
T temp;
while (l <= r)
{
// check values on left are bigger than the pivot
while (arr[l].compareTo(pivot) < 0)
{
l++;
}
// check if values are smaller than the pivot
while (arr[r].compareTo(pivot) > 0)
{
r--;
}
// l and r have gone past each other swap them
if (l <= r)
{
//swap process
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// left pointer goes up 1
// right pointer goes down 1
l++;
r--;
}
}
if (left < r)
sort(arr, left, r);
if (l < right)
sort(arr, l, right);
return arr;
}
The error seems to be pointing to
//Find the pivot in the middle
T pivot = arr[(left + (right - left)) / 2];
I then seem to be getting many occuring errors.
I believe you are calculating Pivot incorrectly
You should use T pivot = arr[left + (right - left) / 2];
Below is working quick sort program using middle element as pivot:
public void quickSort(T arr[],int left, int right){
int low =left, high = right;
int pivot = arr[left + (right - left) / 2];
while(low<=high){
while (arr[low] < pivot) {
low++;
}
while (arr[high] > pivot) {
high--;
}
if (low <= high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
low++;
high--;
}
if (left < high) {
quickSort(arr,left, high);
}
if (low < high) {
quickSort(arr,low, right);
}
}
}
Hope it helps !!
Stream API way:
T[] result = Arrays.stream(a)
.skip(left)
.limit(right - left)
.sorted((o1, o2) -> {{you logic of comparing}})
.toArray(String[]::new);
the difference in what you get only sorted part of an array. So you should concatenate them after if it is necessary.
You have a typo in that line, yes. T pivot = arr[(left + (right - left)) / 2];
Due to the extra parentheses, everything is divided by 2. It should be
T pivot = arr[left + (right - left) / 2];
A couple stylistical remarks:
as this is an in-place sort, returning T[] is not really necessary
T temp could be moved into the swap-block
Putting it together:
import java.util.Arrays;
public class QuickSort<T extends Comparable<? super T>> {
public void sort (T[] arr)
{
if (arr == null || arr.length <= 1)
return;
sort(arr, 0, arr.length - 1);
}
public void sort(T[] arr, int left, int right)
{
int l = left;
int r = right;
if (right <= left)
return;
//Find the pivot in the middle
T pivot = arr[(left + (right - left)/2)];
while (l <= r)
{
// check values on left are bigger than the pivot
while (arr[l].compareTo(pivot) < 0)
{
l++;
}
// check if values are smaller than the pivot
while (arr[r].compareTo(pivot) > 0)
{
r--;
}
// l and r have gone past each other swap them
if (l <= r)
{
//swap process
T temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// left pointer goes up 1
// right pointer goes down 1
l++;
r--;
}
}
if (left < r)
sort(arr, left, r);
if (l < right)
sort(arr, l, right);
}
public static void main(String args[])
{
Integer[] numbers=new Integer[] {3,2,5,4,1};
System.out.println(Arrays.asList(numbers));
new QuickSort<Integer>().sort(numbers);
System.out.println(Arrays.asList(numbers));
}
}
Output:
[3, 2, 5, 4, 1]
[1, 2, 3, 4, 5]
I'm running this and I am being told it would not run fast enough. What is a good way to increase the speed of this running class? I am guessing I would need to change my nested while loops. That is the only thing I can think of. The if statements should all be linear...
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
public class QSortLab {
static int findpivot(Comparable[] A, int i, int j) {
return (i + j) / 2;
}
static <E> void swap(E[] A, int p1, int p2) {
E temp = A[p1];
A[p1] = A[p2];
A[p2] = temp;
}
static void quicksort(Comparable[] A, int i, int j) { // Quicksort
int pivotindex = findpivot(A, i, j); // Pick a pivot
swap(A, pivotindex, j); // Stick pivot at end
int k = partition(A, i, j-1, A[j]);
swap(A, k, j); // Put pivot in place
if ((k-i) > 1) quicksort(A, i, k-1); // Sort left partition
if ((j-k) > 1) quicksort(A, k+1, j); // Sort right partition
}
static int partition(Comparable[] A, int left, int right, Comparable pivot) {
while (left <= right) { // Move bounds inward until they meet
while (A[left].compareTo(pivot) < 0) left++;
while ((right >= left) && (A[right].compareTo(pivot) >= 0)) right--;
if (right > left) swap(A, left, right); // Swap out-of-place values
}
return left; // Return first position in right partition
}
}
What do you mean you need to change your nested while loops? Quick Sort is defined by those features. Removing wouldn't function properly.
As for optimization, by default it should be known that primitives vs objects tend to be different. E.g. primitives on stack/heap to keep stack small & heap stores object with refs able to be on stack.
So let's test some stuff
primitive quick sort (from here)
Integer quick sort (same code as above, but with Integer class)
Your original posted code
Your original posted code (w/ several edits)
Here's the entire code I used.
import java.util.Random;
public class App {
public static final int ARR_SIZE = 1000;
public static final int TEST_ITERS = 10000;
public static Random RANDOM = new Random();
public static void main(String[] args) {
int[] a = new int[ARR_SIZE];
Integer[] b = new Integer[ARR_SIZE];
Integer[] c = new Integer[ARR_SIZE];
Integer[] d = new Integer[ARR_SIZE];
long sum = 0, start = 0, end = 0;
for (int i = 0; i < TEST_ITERS; ++i) {
for (int j = 0; j < ARR_SIZE; ++j)
a[j] = RANDOM.nextInt();
start = System.nanoTime();
quickSort(a, 0, a.length - 1);
end = System.nanoTime();
sum += (end - start);
}
System.out.println((sum / TEST_ITERS) + " nano, qs avg - 'int'");
sum = 0;
for (int i = 0; i < TEST_ITERS; ++i) {
for (int j = 0; j < ARR_SIZE; ++j)
b[j] = RANDOM.nextInt();
start = System.nanoTime();
quickSort(b, 0, b.length - 1);
end = System.nanoTime();
sum += (end - start);
}
System.out.println((sum / TEST_ITERS) + " nano, qs avg - 'Integer'");
sum = 0;
for (int i = 0; i < TEST_ITERS; ++i) {
for (int j = 0; j < ARR_SIZE; ++j)
c[j] = RANDOM.nextInt();
start = System.nanoTime();
quicksort(c, 0, c.length - 1);
end = System.nanoTime();
sum += (end - start);
}
System.out.println((sum / TEST_ITERS) + " nano, qs avg - 'Comparable' (SO user code)");
sum = 0;
for (int i = 0; i < TEST_ITERS; ++i) {
for (int j = 0; j < ARR_SIZE; ++j)
d[j] = RANDOM.nextInt();
start = System.nanoTime();
qs_quicksort(d, 0, d.length - 1);
end = System.nanoTime();
sum += (end - start);
}
System.out.println((sum / TEST_ITERS) + " nano, qs avg - 'Comparable' (SO user code - edit)");
for (int i = 0; i < ARR_SIZE; ++i) {
final int n = RANDOM.nextInt();
a[i] = n;
b[i] = n;
c[i] = n;
d[i] = n;
}
quickSort(a, 0, a.length - 1);
Integer[] aConv = new Integer[ARR_SIZE];
for (int i = 0; i < ARR_SIZE; ++i)
aConv[i] = a[i];
quickSort(b, 0, b.length - 1);
quicksort(c, 0, c.length - 1);
qs_quicksort(d, 0, d.length - 1);
isSorted(new Integer[][] { aConv, b, c, d });
System.out.println("All properly sorted");
}
public static void isSorted(Integer[][] arrays) {
if (arrays.length != 4) {
System.out.println("error sorting, input arr len");
return;
}
for (int i = 0; i < ARR_SIZE; ++i) {
int val1 = arrays[0][i].compareTo(arrays[1][i]);
int val2 = arrays[1][i].compareTo(arrays[2][i]);
int val3 = arrays[2][i].compareTo(arrays[3][i]);
if (val1 != 0 || val2 != 0 || val3 != 00) {
System.out.printf("Error [i = %d]: a = %d, b = %d, c = %d", i, arrays[0][i], arrays[1][i], arrays[2][i], arrays[3][i]);
break;
}
}
}
public static int partition(int arr[], int left, int right) {
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
return i;
}
public static void quickSort(int arr[], int left, int right) {
int index = partition(arr, left, right);
if (left < index - 1)
quickSort(arr, left, index - 1);
if (index < right)
quickSort(arr, index, right);
}
public static int partition(Integer[] arr, int left, int right) {
int i = left, j = right;
Integer pivot = arr[(left + right) / 2];
while (i <= j) {
while (arr[i].compareTo(pivot) < 0)
i++;
while (arr[j].compareTo(pivot) > 0)
j--;
if (i <= j) {
Integer temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
return i;
}
public static void quickSort(Integer[] arr, int left, int right) {
int index = partition(arr, left, right);
if (left < index - 1)
quickSort(arr, left, index - 1);
if (index < right)
quickSort(arr, index, right);
}
static int findpivot(Comparable[] A, int i, int j)
{
return (i+j)/2;
}
static <E> void swap(E[] A, int p1, int p2) {
E temp = A[p1];
A[p1] = A[p2];
A[p2] = temp;
}
static void quicksort(Comparable[] A, int i, int j) { // Quicksort
int pivotindex = findpivot(A, i, j); // Pick a pivot
swap(A, pivotindex, j); // Stick pivot at end
int k = partition(A, i, j-1, A[j]);
swap(A, k, j); // Put pivot in place
if ((k-i) > 1) quicksort(A, i, k-1); // Sort left partition
if ((j-k) > 1) quicksort(A, k+1, j); // Sort right partition
}
static int partition(Comparable[] A, int left, int right, Comparable pivot) {
while (left <= right) { // Move bounds inward until they meet
while (A[left].compareTo(pivot) < 0) left++;
while ((right >= left) && (A[right].compareTo(pivot) >= 0)) right--;
if (right > left) swap(A, left, right); // Swap out-of-place values
}
return left; // Return first position in right partition
}
static <E> void qs_swap(E[] A, int p1, int p2) {
E temp = A[p1];
A[p1] = A[p2];
A[p2] = temp;
}
static void qs_quicksort(Comparable[] A, int i, int j) { // Quicksort
int pivotindex = (i+j)/2;
qs_swap(A, pivotindex, j); // Stick pivot at end
int k = qs_partition(A, i, j-1, A[j]);
qs_swap(A, k, j); // Put pivot in place
if ((k-i) > 1) qs_quicksort(A, i, k-1); // Sort left partition
if ((j-k) > 1) qs_quicksort(A, k+1, j); // Sort right partition
}
static int qs_partition(Comparable[] A, int left, int right, Comparable pivot) {
while (left <= right) { // Move bounds inward until they meet
while (A[left].compareTo(pivot) < 0) left++;
while ((right >= left) && (A[right].compareTo(pivot) >= 0)) right--;
if (right > left) { qs_swap(A, left, right); // Swap out-of-place values
left++; right--;}
}
return left; // Return first position in right partition
}
}
This produces the output:
56910 nano, qs avg - 'int'
69498 nano, qs avg - 'Integer'
76762 nano, qs avg - 'Comparable' (SO user code)
71846 nano, qs avg - 'Comparable' (SO user code - edit)
All properly sorted
Now, breaking down the results
The 'int' vs 'Integer' shows great diff when simply using primitives vs non-primitives (I'm sure at some points in the code there may be boxing but hopefully not in critical spots ;) - please edit this if so). The 'int' vs 'Integer' uses same code with exception of 'int' 'Integer'. See the following four method signatures that are used in this comparison, 'int'
public static int partition(int arr[], int left, int right)
public static void quickSort(int arr[], int left, int right)
and 'Integer'
public static int partition(Integer[] arr, int left, int right)
public static void quickSort(Integer[] arr, int left, int right)
respectively.
Then there are the method signatures related to the original code you posted,
static int findpivot(Comparable[] A, int i, int j)
static <E> void swap(E[] A, int p1, int p2)
static void quicksort(Comparable[] A, int i, int j)
static int partition(Comparable[] A, int left, int right, Comparable pivot)
and the modified ones,
static <E> void qs_swap(E[] A, int p1, int p2)
static void qs_quicksort(Comparable[] A, int i, int j)
static int qs_partition(Comparable[] A, int left, int right, Comparable pivot)
As you can see, in the modified code, findpivot was removed directly and replaced into the calling spot in quicksort. Also, the partition method gained counters for left and right respectively. left++; right--;
And finally, to ensure these 4 variations of quicksort actually did the sole purpose, sort, I added a method, isSorted() to check the validity of the same generated content and that it's sorted accordingly based on each of the 4 different sorts.
In conclusion, I think my edits may have saved a portion of time/nanoseconds, however I wasn't able to achieve the same time as the Integer test. Hopefully I've not missed anything obvious and edits are welcome if need be. Cheers
Well, I couldn't tell from testing whether this makes any difference at all because the timer on my machine is terrible , but I think most of the work in this algo is done with the swap function, so thinking about how to make that in particular more efficient, maybe the function call/return itself consumes cycles, and perhaps the creation of the temp variable each time the function is called also takes cycles, so maybe the code would be more efficient if the swap work was done in line. It was not obvious though when I tested on my machine as the nanotimer returned results +/- 20% each time I ran the program
public class QSort2 {
static int findpivot(Comparable[] A, int i, int j) {
return (i + j) / 2;
}
static Comparable temp;
static void quicksort(Comparable[] A, int i, int j) { // Quicksort
int pivotindex = findpivot(A, i, j); // Pick a pivot
// swap(A, pivotindex, j); // Stick pivot at end
temp = A[pivotindex];
A[pivotindex] = A[j];
A[j] = temp;
int k = partition(A, i, j - 1, A[j]);
//swap(A, k, j); // Put pivot in place
temp = A[k];
A[k] = A[j];
A[j] = temp;
if ((k - i) > 1) quicksort(A, i, k - 1); // Sort left partition
if ((j - k) > 1) quicksort(A, k + 1, j); // Sort right partition
}
static int partition(Comparable[] A, int left, int right, Comparable pivot) {
while (left <= right) { // Move bounds inward until they meet
while (A[left].compareTo(pivot) < 0) left++;
while ((right >= left) && (A[right].compareTo(pivot) >= 0)) right--;
if (right > left) {
//swap(A, left, right);} // Swap out-of-place values
temp = A[left];
A[left] = A[right];
A[right] = temp;
}
}
return left; // Return first position in right partition
}
}
Firstly (as the question title implies) I'm not looking for why the bellow partitioning method doesn't work, rather a modification to it so that it will work for the following input:
int[] a = {1,2,8,4,5,7};
Here's the partition method along with some other stuff:
static int[] swap (int[] a,int i,int j){
int t = a[i];
a[i] = a[j];
a[j] = t;
return a;
}
static int partition (int[] a,int l,int r){
int i = l;
int j = r;
int v = a[l];
while (true) {
while (a[i] < v) {
if (i == r) break;
i++;
}
while (a[j] > v) {
if (j == l) break;
j--;
}
if (i >= j) break;
a = swap(a,i,j);
}
a = swap(a, l, j);
return j;
}
void sort(int[] a,int l,int r){
int j = partition(a, l, r);
sort(a, l, j-1);
sort(a, j+1, r);
}
public static void main(String[] args) {
int[] a = {1,2,8,4,5,7};
System.out.println(partition(a,0,5));
}
Output:
0
The output is the index of the pivot returned from the partition method. 0, as the index of the pivot, makes sense in terms of the definition, i.e. everything left of the pivot is smaller and everything right of the pivot is larger, but clearly runs into a problem in sort namely:
sort(a, l, j-1);
where you have the right pointer being negative (j-1 = 0-1 = -1). My question again being is there a modification to the above method(s) that will maintain the definition (everything left of the pivot is smaller and everything right of the pivot is larger) and not run into the problem in sort.
The missing part is the line
if ( l >= r ) return;
in the beginning of the sort method. This is actually the recursion stop step so it is necessary to have it anyway to prevent endless recursion. But besides that, it also solves your problem, because if you call sort(0,-1) then -1 is less than 0, so it prevents further processing of that index.
I try to find k-th minimum element using my code, but can't fix an error in my code.
When it try to make partitioning for [0, 0, 2] with pivot = 0 it's looping.
import java.util.Arrays;
public class OrderStat {
public static void main(String[] args) {
int[] uA = {13, 32, 28, 17, 2, 0, 14, 34, 35, 0};
System.out.println("Initial array: " + Arrays.toString(uA));
int kth = 3; // We will try to find 3rd smallest element(or 2nd if we will count from 0).
int result = getKthSmallestElement(uA, 0, uA.length - 1, kth - 1);
System.out.println(String.format("The %d smallest element is %d", kth, result));
System.out.println("-------------------------------------");
Arrays.sort(uA);
System.out.println("Sorted array for check: " + Arrays.toString(uA));
}
private static int getKthSmallestElement(int[] uA, int start, int end, int kth) {
int l = start;
int r = end;
int pivot = uA[start];
System.out.println("===================");
System.out.println(String.format("start=%d end=%d", start, end));
System.out.println("pivot = " + pivot);
//ERROR HERE: When we will work with [0, 0, 2] part of array with pivot = 0 it will give us infinite loop;
while (l < r) {
while (uA[l] < pivot) {
l++;
}
while (uA[r] > pivot) {
r--;
}
if (l < r) {
int tmp = uA[l];
uA[l] = uA[r];
uA[r] = tmp;
}
}
System.out.println("After partitioning: " + Arrays.toString(uA) + "\n");
if (l < kth)
return getKthSmallestElement(uA, l + 1, end, kth);
else if (l > kth)
return getKthSmallestElement(uA, start, l - 1, kth);
return uA[l];
}
}
Explain me, please, how to fix this problem.
After swapping
if (l < r) {
int tmp = uA[l];
uA[l] = uA[r];
uA[r] = tmp;
}
you need to move l and r (or at least one of them, to make any progress) to the next position (++l; --r;). Otherwise, if both values are equal to the pivot, you loop infinitely.
A correct partitioning that is also usable in a quicksort would be
// make sure to call it only with valid indices, 0 <= start <= end < array.length
private int partition(int[] array, int start, int end) {
// trivial case, single element array - garbage if end < start
if(end <= start) return start;
int pivot = array[start]; // not a good choice of pivot in general, but meh
int left = start+1, right = end;
while(left < right) {
// move left index to first entry larger than pivot or right
while(left < right && array[left] <= pivot) ++left;
// move right index to last entry not larger than pivot or left
while(right > left && array[right] > pivot) --right;
// Now, either
// left == right, or
// left < right and array[right] <= pivot < array[left]
if (left < right) {
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
// move on
++left;
--right;
}
}
// Now left >= right.
// If left == right, we don't know whether array[left] is larger than the pivot or not,
// but array[left-1] certainly is not larger than the pivot.
// If left > right, we just swapped and incremented before exiting the loop,
// so then left == right+1 and array[right] <= pivot < array[left].
if (left > right || array[left] > pivot) {
--left;
}
// Now array[i] <= pivot for start <= i <= left, and array[j] > pivot for left < j <= end
// swap pivot in its proper place in the sorted array
array[start] = array[left];
array[left] = pivot;
// return pivot position
return left;
}
Then you can find the k-th smallest element in an array
int findKthSmallest(int array, int k) {
if (k < 1) throw new IllegalArgumentException("k must be positive");
if (array.length < k) throw new IllegalArgumentException("Array too short");
int left = 0, right = array.length-1, p;
--k; // 0-based indices
while(true) {
p = partition(array, left, right);
if (p == k) return array[p];
if (p < k) {
left = p+1;
k -= left;
} else {
right = p-1;
}
}
// dummy return, never reached
return 0;
}