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
}
}
Related
I am trying to implement the median of medians algorithm in Java. The algorithm shall determine the median of a set of numbers. I tried to implement the pseudo code on wikipedia:
https://en.wikipedia.org/wiki/Median_of_medians
I am getting a buffer overflow and don't know why. Due to the recursions it's quite difficult to keep track of the code for me.
import java.util.Arrays;
public class MedianSelector {
private static final int CHUNK = 5;
public static void main(String[] args) {
int[] test = {9,8,7,6,5,4,3,2,1,0,13,11,10};
lowerMedian(test);
System.out.print(Arrays.toString(test));
}
/**
* Computes and retrieves the lower median of the given array of
* numbers using the Median algorithm presented in the lecture.
*
* #param input numbers.
* #return the lower median.
* #throw IllegalArgumentException if the array is {#code null} or empty.
*/
public static int lowerMedian(int[] numbers) {
if(numbers == null || numbers.length == 0) {
throw new IllegalArgumentException();
}
return numbers[select(numbers, 0, numbers.length - 1, (numbers.length - 1) / 2)];
}
private static int select(int[] numbers, int left, int right, int i) {
if(left == right) {
return left;
}
int pivotIndex = pivot(numbers, left, right);
pivotIndex = partition(numbers, left, right, pivotIndex, i);
if(i == pivotIndex) {
return i;
}else if(i < pivotIndex) {
return select(numbers, left, pivotIndex - 1, i);
}else {
return select(numbers, left, pivotIndex + 1, i);
}
}
private static int pivot(int numbers[], int left, int right) {
if(right - left < CHUNK) {
return partition5(numbers, left, right);
}
for(int i=left; i<=right; i=i+CHUNK) {
int subRight = i + (CHUNK-1);
if(subRight > right) {
subRight = right;
}
int medChunk = partition5(numbers, i, subRight);
int tmp = numbers[medChunk];
numbers[medChunk] = numbers[(int) (left + Math.floor((double) (i-left)/CHUNK))];
numbers[(int) (left + Math.floor((double) (i-left)/CHUNK))] = tmp;
}
int mid = (right - left) / 10 + left +1;
return select(numbers, left, (int) (left + Math.floor((right - left) / CHUNK)), mid);
}
private static int partition(int[] numbers, int left, int right, int idx, int k) {
int pivotVal = numbers[idx];
int storeIndex = left;
int storeIndexEq = 0;
int tmp = 0;
tmp = numbers[idx];
numbers[idx] = numbers[right];
numbers[right] = tmp;
for(int i=left; i<right; i++) {
if(numbers[i] < pivotVal) {
tmp = numbers[i];
numbers[i] = numbers[storeIndex];
numbers[storeIndex] = tmp;
storeIndex++;
}
}
storeIndexEq = storeIndex;
for(int i=storeIndex; i<right; i++) {
if(numbers[i] == pivotVal) {
tmp = numbers[i];
numbers[i] = numbers[storeIndexEq];
numbers[storeIndexEq] = tmp;
storeIndexEq++;
}
}
tmp = numbers[right];
numbers[right] = numbers[storeIndexEq];
numbers[storeIndexEq] = tmp;
if(k < storeIndex) {
return storeIndex;
}
if(k <= storeIndexEq) {
return k;
}
return storeIndexEq;
}
//Insertion sort
private static int partition5(int[] numbers, int left, int right) {
int i = left + 1;
int j = 0;
while(i<=right) {
j= i;
while(j>left && numbers[j-1] > numbers[j]) {
int tmp = numbers[j-1];
numbers[j-1] = numbers[j];
numbers[j] = tmp;
j=j-1;
}
i++;
}
return left + (right - left) / 2;
}
}
Confirm n (in the pseudo code) or i (in my code) stand for the position of the median? So lets assume our array is number = {9,8,7,6,5,4,3,2,1,0}. I would call select{numbers, 0, 9,4), correct?
I don't understand the calculation of mid in pivot? Why is there a division by 10? Maybe there is a mistake in the pseudo code?
Thanks for your help.
EDIT: It turns out the switch from iteration to recursion was a red herring. The actual issue, identified by the OP, was in the arguments to the 2nd recursive select call.
This line:
return select(numbers, left, pivotIndex + 1, i);
should be
return select(numbers, pivotIndex + 1, right, i);
I'll leave the original answer below as I don't want to appear to be clever than I actually was.
I think you may have misinterpreted the pseudocode for the select method - it uses iteration rather than recursion.
Here's your current implementation:
private static int select(int[] numbers, int left, int right, int i) {
if(left == right) {
return left;
}
int pivotIndex = pivot(numbers, left, right);
pivotIndex = partition(numbers, left, right, pivotIndex, i);
if(i == pivotIndex) {
return i;
}else if(i < pivotIndex) {
return select(numbers, left, pivotIndex - 1, i);
}else {
return select(numbers, left, pivotIndex + 1, i);
}
}
And the pseudocode
function select(list, left, right, n)
loop
if left = right then
return left
pivotIndex := pivot(list, left, right)
pivotIndex := partition(list, left, right, pivotIndex, n)
if n = pivotIndex then
return n
else if n < pivotIndex then
right := pivotIndex - 1
else
left := pivotIndex + 1
This would typically be implemented using a while loop:
private static int select(int[] numbers, int left, int right, int i) {
while(true)
{
if(left == right) {
return left;
}
int pivotIndex = pivot(numbers, left, right);
pivotIndex = partition(numbers, left, right, pivotIndex, i);
if(i == pivotIndex) {
return i;
}else if(i < pivotIndex) {
right = pivotIndex - 1;
}else {
left = pivotIndex + 1;
}
}
}
With this change your code appears to work, though obviously you'll need to test to confirm.
int[] test = {9,8,7,6,5,4,3,2,1,0,13,11,10};
System.out.println("Lower Median: " + lowerMedian(test));
int[] check = test.clone();
Arrays.sort(check);
System.out.println(Arrays.toString(check));
Output:
Lower Median: 6
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13]
I am implementing the quick-select algorithm to get the kth element in an array, and I am stuck at a place where I don't know how to resolve. Here is my code that doesn't work:
public static void main (String[] args) {
int[] arr = new int[]{7,6,5,4,3,2,1};
int k = 4;
quickSort(arr, 0, arr.length - 1, k);
return arr[k];
}
private static void quickSelect(int[] nums, int start, int end, int k) {
if (start < end) {
int partitionIndex = getPartitionIndex(nums, start, end);
if (partitionIndex == k) {
return;
}
quickSelect(nums, start, partitionIndex - 1, k);
quickSelect(nums, partitionIndex + 1, end, k);
}
}
private int getPartitionIndex(int[] nums, int start, int end) {
int pivot = nums[end];
int index = start;
for (int i = start; i <= end; i++) {
int current = nums[i];
if (current < pivot) {
swap(nums, index, i);
index++;
}
}
swap(nums, index, end);
return index;
}
private void swap(int[] nums, int i, int j) {
if (i == j) {
return;
}
nums[i] = nums[i] ^ nums[j];
nums[j] = nums[i] ^ nums[j];
nums[i] = nums[i] ^ nums[j];
}
Sure, if I remove these lines:
if (partitionIndex == k) {
return;
}
It becomes quicksort and works fine. And I understand why it's not working, it is since the array I am getting from 0 to k might not be sorted as I return at the above condition. But I am not able to find the right conditions where I sort only the first k elements in the array and leave out the rest, so that I don't do any extra work. I've looked at some implementations online and spent some time on the above, but not able to figure it out, so reaching out for help.
If k < partitionIndex, only check the left partition, else only check the right partition.
if (k < partitionIndex)
quickSelect(nums, start, partitionIndex - 1, k);
else
quickSelect(nums, partitionIndex + 1, end, k);
I'm implementing a recursive quicksort however I'm receiving stackoverflow and not sure where the bug lies :(
I'm sorting 1 million ints from 10-50.
I works for sizes less than 1 million like 100 thousand etc.
public Quicksort(int NUM_TESTS, int NUM_ELEMENTS){
num_tests = NUM_TESTS;
num_elements = NUM_ELEMENTS;
}
private void start(){
for (int i = 0; i < num_tests; i++){
int[] d1 = dataGeneration(num_elements);
qSortRecursive(d1,0,d1.length-1);
}
}
public static void main(String args[]){
Quicksort q = new Quicksort(1,1000000);
q.start();
}
private int[] dataGeneration(int n) {
int[] d1 = new int[n];
for (int i = 0; i < n; i++){
d1[i] = (int)(Math.random() * ((50 - 10) + 1) + 10);
}
return d1;
}
private void qSortRecursive(int[] data, int left, int right){
if(left < right){
int pivot = partition(data,left,right);
qSortRecursive(data,left,pivot-1);
qSortRecursive(data,pivot+1,right);
}
}
private int partition(int[] data, int left, int right){
int pivot = left ;
left++;
while (left <= right){
while (left <= right && data[left] <= data[pivot]) {
left++;
}
while (left <= right && data[right] >= data[pivot]){
right--;
}
if (left < right){
swap(data,left,right);
left++;
right--;
}
}
if (data[right] <= data[pivot]){
if (data[right] != data[pivot]){
swap(data,right,pivot);
}
pivot = right;
}
return pivot;
}
private void swap(int[] data, int i, int j){
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private void qSortRecursive(int[] data, int left, int right){
while (left < right){
int pivot = partition(data,left,right);
if (pivot - left < right - pivot){
qSortRecursive(data, left, pivot - 1);
left = pivot + 1;
} else {
qSortRecursive(data, pivot + 1, right);
right = pivot - 1;
}
}
Performing a tail call by reducing number of recursion solved my problem, thanks for help everyone :)
You can try to rewrite the algorithm without recursion. Well, you remove recursion by adding your own stack and in that case you can have available the entire memory, not just size of stack.
Something like: http://alienryderflex.com/quicksort/
I'm trying to implement the randomized selection algorithm that returns the K-th largest element in an array. The algorithm in the code below works when pivot is always set to equal the first element in the array. How do I get the code to work such that it finds the K-th largest using a randomly generated pivot point ?
import java.util.Random;
public class RandomizedKSelection {
private static Random generator = new Random();
public static int partition(int[] A, int start, int end) {
// start = generator.nextInt(end); This Line breaks the code
int pivot = A[start];
int pivotPosition = start++;
while (start <= end) {
// scan for values less than the pivot
while ((start <= end) && (A[start] < pivot)) {
start++;
}
// scan for values greater than the pivot
while ((end >= start) && (A[end] >= pivot)) {
end--;
}
if (start > end) {
// swap the end uncoformed
// element with the pivot
swap(A, pivotPosition, end);
}
else {
// swap unconformed elements:
// start that was not lesser than the pivot
// and end that was not larger than the pivot
swap(A, start, end);
}
}
return end;
}
#SuppressWarnings("unused")
// iterative version
private static int orderStatistic(int[] A, int k, int start, int end) {
int pivotPosition = partition(A, start, end);
while (pivotPosition != k - 1) {
if (k - 1 < pivotPosition) {
end = pivotPosition - 1;
}
else {
start = pivotPosition + 1;
}
pivotPosition = partition(A, start, end);
}
return A[k - 1];
}
public static int kthLargest(int[] A, int k) {
return orderStatistic(A, A.length - k + 1, 0, A.length - 1);
}
public static void swap(int[] A, int i, int j){
int temp = A[i];
A[i]= A[j];
A[j] = temp;
}
}
I've got a `partition implementation from wikibooks. I changed your code to use 0-based indices (you can find examples for 0-based indices more easily), you can wrap them if you like (see kthLargest1Based). Small randomized test arguments the validity of the algorithm.
import java.util.Arrays;
import java.util.Random;
public class RandomizedKSelection {
private static Random generator = new Random();
private static int partition(int[] array, int begin, int end) {
int index = begin + generator.nextInt(end - begin + 1);
int pivot = array[index];
swap(array, index, end);
for (int i = index = begin; i < end; ++ i) {
if (array[i] <= pivot) {
swap(array, index++, i);
}
}
swap(array, index, end);
return (index);
}
// iterative version
private static int orderStatistic(int[] A, int k, int start, int end) {
int pivotPosition = partition(A, start, end);
while (pivotPosition != k) {
if (k < pivotPosition) {
end = pivotPosition - 1;
} else {
start = pivotPosition + 1;
}
pivotPosition = partition(A, start, end);
}
return A[k];
}
public static int kthLargest(int[] A, int k) {
return orderStatistic(A, A.length - k - 1, 0, A.length - 1);
}
public static int kthLargest1Based(int[] A, int k) {
return kthLargest(A, k - 1);
}
public static int kthLargestSafe(int[] A, int k) {
Arrays.sort(A);
return A[A.length - k - 1];
}
public static void swap(int[] A, int i, int j) {
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
public static void main(String[] args) {
Random random = new Random();
for (int i = 0; i < 1000000; i++) {
int[] A = new int[1 + random.nextInt(1000)];
int max = 1 + random.nextInt(2 * A.length);
for (int j = 0; j < A.length; j++) {
A[j] = random.nextInt(max);
}
int k = random.nextInt(A.length);
if (RandomizedKSelection.kthLargest(A, k) != RandomizedKSelection.kthLargestSafe(A, k)) {
System.out.println("BUG");
}
}
}
}
I am trying to make a merge sort method, but it keeps on giving the wrong sorts. Where do I have change to make it actually sort the array? What part of the code has to be different? Thank you for your time.
public static void mergeSort(int[] array, int left, int lHigh, int right, int rHigh) {
int elements = (rHigh - lHigh +1) ;
int[] temp = new int[elements];
int num = left;
while ((left <= lHigh) && (right <= rHigh)){
if (a[left] <= array[right]) {
temp[num] = array[left];
left++;
}
else {
temp[num] = array[right];
right++;
}
num++;
}
while (left <= right){
temp[num] = array[left]; // I'm getting an exception here, and is it because of the num???
left += 1;
num += 1;
}
while (right <= rHigh) {
temp[num] = array[right];
right += 1;
num += 1;
}
for (int i=0; i < elements; i++){
array[rHigh] = temp[rHigh];
rHigh -= 1;
}
EDIT: now the mergeSort doesn't really sort the numbers, can someone tell me where it specifically is? especially when I print the "Testing merge sort" part.
First of all, I'm assuming this is academic rather than practical, since you're not using a built in sort function. That being said, here's some help to get you moving in the right direction:
Usually, one can think of a merge sort as two different methods: a merge() function that merges two sorted lists into one sorted list, and mergeSort() which recursively breaks the list into single element lists. Since a single element list is sorted already, you then merge all the lists together into one big sorted list.
Here's some off-hand pseudo-code:
merge(A, B):
C = empty list
While A and B are not empty:
If the first element of A is smaller than the first element of B:
Remove first element of A.
Add it to the end of C.
Otherwise:
Remove first element of B.
Add it to the end of C.
If A or B still contains elements, add them to the end of C.
mergeSort(A):
if length of A is 1:
return A
Split A into two lists, L and R.
Q = merge(mergeSort(L), mergeSort(R))
return Q
Maybe that'll help clear up where you want to go.
If not, there's always MergeSort at wikipedia.
Additional:
To help you out, here are some comments inline in your code.
public static void mergeSort(int[] array, int left, int lHigh, int right, int rHigh) {
// what do lHigh and rHigh represent?
int elements = (rHigh - lHigh +1) ;
int[] temp = new int[elements];
int num = left;
// what does this while loop do **conceptually**?
while ((left <= lHigh) && (right <= rHigh)){
if (a[left] <= a[right]) {
// where is 'pos' declared or defined?
temp[pos] = a[left];
// where is leftLow declared or defined? Did you mean 'left' instead?
leftLow ++;
}
else {
temp[num] = a[right];
right ++;
}
num++;
}
// what does this while loop do **conceptually**?
while (left <= right){
// At this point, what is the value of 'num'?
temp[num] = a[left];
left += 1;
num += 1;
}
while (right <= rHigh) {
temp[num] = a[right];
right += 1;
num += 1;
}
// Maybe you meant a[i] = temp[i]?
for (int i=0; i < elements; i++){
// what happens if rHigh is less than elements at this point? Could
// rHigh ever become negative? This would be a runtime error if it did
a[rHigh] = temp[rHigh];
rHigh -= 1;
}
I'm purposefully being vague so you think about the algorithm. Try inserting your own comments into the code. If you can write what is conceptually happening, then you may not need Stack Overflow :)
My thoughts here are that you are not implementing this correctly. This is because it looks like you're only touching the elements of the array only once (or close to only once). This means you have a worst case scenario of O(N) Sorting generally takes at least O(N * log N) and from what I know, the simpler versions of merge sort are actually O(N^2).
More:
In the most simplistic implementation of merge sort, I would expect to see some sort of recursion in the mergeSort() method. This is because merge sort is generally defined recursively. There are ways to do this iteratively using for and while loops, but I definitely don't recommend it as a learning tool until you get it recursively.
Honestly, I suggest taking either my pseudo-code or the pseudo-code you may find in a wikipedia article to implement this and start over with your code. If you do that and it doesn't work correctly still, post it here and we'll help you work out the kinks.
Cheers!
And finally:
// Precondition: array[left..lHigh] is sorted and array[right...rHigh] is sorted.
// Postcondition: array[left..rHigh] contains the same elements of the above parts, sorted.
public static void mergeSort(int[] array, int left, int lHigh, int right, int rHigh) {
// temp[] needs to be as large as the number of elements you're sorting (not half!)
//int elements = (rHigh - lHigh +1) ;
int elements = rHigh - left;
int[] temp = new int[elements];
// this is your index into the temp array
int num = left;
// now you need to create indices into your two lists
int iL = left;
int iR = right;
// Pseudo code... when you code this, make use of iR, iL, and num!
while( temp is not full ) {
if( left side is all used up ) {
copy rest of right side in.
make sure that at the end of this temp is full so the
while loop quits.
}
else if ( right side is all used up) {
copy rest of left side in.
make sure that at the end of this temp is full so the
while loop quits.
}
else if (array[iL] < array[iR]) { ... }
else if (array[iL] >= array[iR]) { ... }
}
}
public class MergeSort {
public static void main(String[] args) {
int[] arr = {5, 4, 7, 2, 3, 1, 6, 2};
print(arr);
new MergeSort().sort(arr, 0, arr.length - 1);
}
private void sort(int[] arr, int lo, int hi) {
if (lo < hi) {
int mid = (lo + hi) / 2;
sort(arr, lo, mid); // recursive call to divide the sub-list
sort(arr, mid + 1, hi); // recursive call to divide the sub-list
merge(arr, lo, mid, hi); // merge the sorted sub-lists.
print(arr);
}
}
private void merge(int[] arr, int lo, int mid, int hi) {
// allocate enough space so that the extra 'sentinel' value
// can be added. Each of the 'left' and 'right' sub-lists are pre-sorted.
// This function only merges them into a sorted list.
int[] left = new int[(mid - lo) + 2];
int[] right = new int[hi - mid + 1];
// create the left and right sub-list for merging into original list.
System.arraycopy(arr, lo, left, 0, left.length - 1);
System.arraycopy(arr, mid + 1, right, 0, left.length - 1);
// giving a sentinal value to marking the end of the sub-list.
// Note: The list to be sorted is assumed to contain numbers less than 100.
left[left.length - 1] = 100;
right[right.length - 1] = 100;
int i = 0;
int j = 0;
// loop to merge the sorted sequence from the 2 sub-lists(left and right)
// into the main list.
for (; lo <= hi; lo++) {
if (left[i] <= right[j]) {
arr[lo] = left[i];
i++;
} else {
arr[lo] = right[j];
j++;
}
}
}
// print the array to console.
private static void print(int[] arr) {
System.out.println();
for (int i : arr) {
System.out.print(i + ", ");
}
}
}
Here's another!
private static int[] mergeSort(int[] input){
if (input.length == 1)
return input;
int length = input.length/2;
int[] left = new int[length];
int[] right = new int[input.length - length];
for (int i = 0; i < length; i++)
left[i] = input[i];
for (int i = length; i < input.length; i++)
right[i-length] = input[i];
return merge(mergeSort(left),mergeSort(right));
}
private static int[] merge(int[] left, int[] right){
int[] merged = new int[left.length+right.length];
int lengthLeft = left.length;
int lengthRight = right.length;
while (lengthLeft > 0 && lengthRight > 0){
if (left[left.length - lengthLeft] < right[right.length - lengthRight]){
merged[merged.length -lengthLeft-lengthRight] = left[left.length - lengthLeft];
lengthLeft--;
}else{
merged[merged.length - lengthLeft-lengthRight] = right[right.length - lengthRight];
lengthRight--;
}
}
while (lengthLeft > 0){
merged[merged.length - lengthLeft] = left[left.length-lengthLeft];
lengthLeft--;
}
while (lengthRight > 0){
merged[merged.length - lengthRight] = right[right.length-lengthRight];
lengthRight--;
}
return merged;
}
static void mergeSort(int arr[],int p, int r) {
if(p<r) {
System.out.println("Pass "+k++);
int q = (p+r)/2;
mergeSort(arr,p,q);
mergeSort(arr,q+1,r);
//System.out.println(p+" "+q+" "+r);
merge(arr,p,q,r);
}
}
static void merge(int arr[],int p,int q,int r) {
int temp1[],temp2[];
//lower limit array
temp1 = new int[q-p+1];
//upper limit array
temp2 = new int[r-q];
for(int i=0 ; i< (q-p+1); i++){
temp1[i] = arr[p+i];
}
for(int j=0; j< (r-q); j++){
temp2[j] = arr[q+j+1];
}
int i = 0,j=0;
for(int k=p;k<=r;k++){
// This logic eliminates the so called sentinel card logic mentioned in Coreman
if(i!= temp1.length
&& (j==temp2.length || temp1[i] < temp2[j])
) {
arr[k] = temp1[i];
// System.out.println(temp1[i]);
i++;
}
else {
//System.out.println(temp2[j]);
arr[k] = temp2[j];
j++;
}
}
}
>
Merge Sort Using Sentinel
This codes works perfectly fine.
public void mergeSort(int a[], int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
mergeSort(a, low, mid);
mergeSort(a, mid + 1, high);
merge(a, low, mid, high);
}
}
public void merge(int a[], int low, int mid, int high) {
int n1 = mid - low + 1;// length of an array a1
int n2 = high - mid; // length of an array a2
int a1[] = new int[n1 + 1];
int a2[] = new int[n2 + 1];
int lowRange = low;
for (int i = 0; i < n1; i++) {
a1[i] = a[lowRange];
lowRange++;
}
for (int j = 0; j < n2; j++) {
a2[j] = a[mid + j + 1];
}
a1[n1] = Integer.MAX_VALUE; // inserting sentinel at the end of array a1
a2[n2] = Integer.MAX_VALUE; // inserting sentinel at the end of array a2
int i = 0;
int j = 0;
int k = low;
for (k = low; k <= high; k++) {
if (a1[i] >= a2[j]) {
a[k] = a2[j];
j++;
} else {
a[k] = a1[i];
i++;
}
}
if (a2.length >= a1.length) {
for (int ab = k; ab < a2.length; ab++) {
a[k] = a2[ab];
k++;
}
} else if (a1.length >= a2.length) {
for (int ab = k; ab < a1.length; ab++) {
a[k] = a1[ab];
k++;
}
}
}
Here's another alternative:
public class MergeSort {
public static void merge(int[]a,int[] aux, int f, int m, int l) {
for (int k = f; k <= l; k++) {
aux[k] = a[k];
}
int i = f, j = m+1;
for (int k = f; k <= l; k++) {
if(i>m) a[k]=aux[j++];
else if (j>l) a[k]=aux[i++];
else if(aux[j] > aux[i]) a[k]=aux[j++];
else a[k]=aux[i++];
}
}
public static void sort(int[]a,int[] aux, int f, int l) {
if (l<=f) return;
int m = f + (l-f)/2;
sort(a, aux, f, m);
sort(a, aux, m+1, l);
merge(a, aux, f, m, l);
}
public static int[] sort(int[]a) {
int[] aux = new int[a.length];
sort(a, aux, 0, a.length-1);
return a;
}
}
Here is a simple merge sort algorithm in Java:
Good Tip: Always use int middle = low + (high-low)/2 instead of int middle = (low + high)/2.
public static int[] mergesort(int[] arr) {
int lowindex = 0;
int highindex = arr.length-1;
mergesort(arr, lowindex, highindex);
return arr;
}
private static void mergesort(int[] arr, int low, int high) {
if (low == high) {
return;
} else {
int midIndex = low + (high-low)/2;
mergesort(arr, low, midIndex);
mergesort(arr, midIndex + 1, high);
merge(arr, low, midIndex, high);
}
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] left = new int[mid-low+2];
for (int i = low; i <= mid; i++) {
left[i-low] = arr[i];
}
left[mid-low+1] = Integer.MAX_VALUE;
int[] right = new int[high-mid+1];
for (int i = mid+1; i <= high; i++) {
right[i-mid-1] = arr[i];
}
right[high - mid] = Integer.MAX_VALUE;
int i = 0;
int j = 0;
for (int k = low; k <= high; k++) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
}
}
package com.sortalgo;
import java.util.Arrays;
public class MyMSort {
private static void merge(int[] array, int[] result, int low, int mid, int high) {
int k =low, i=low; int j=mid+1;
while(i<=mid && j<=high) {
if(array[i]<= array[j]) {
result[k++]=array[i++];
}else {
result[k++]=array[j++];
}
}
while(i<=mid) {
result[k++]=array[i++];
}
while(j<=high) {
result[k++]=array[j++];
}
for(i=low;i<=high;i++) {
array[i]=result[i];
}
}
private static void mergeSort(int[] array, int[] result, int low, int high) {
if(high == low) {
return ;
}
int mid = (low + high)/2;
mergeSort(array,result, low, mid );
mergeSort(array,result, mid+1, high );
merge(array, result, low, mid, high);
}
public static void main(String[] args) {
int[] array = {8,4,3,12,25,6,13,10};
int[] result = new int[array.length];
mergeSort(array, result, 0, array.length-1 );
for(int i=0; i<=array.length-1;i++) {
System.out.println(array[i]);
}
}
}