about Quick Sort - java

I have written this code but it will print these stack traces in the console please help me thanks! (Aslo "p" and "q" are the first and last index of our array ,respectively)
public class JavaQuickSort {
public static void QuickSort(int A[], int p, int q) {
int i, last = 0;
Random rand = new Random();
if (q < 1) {
return;
}
**swap(A, p, rand.nextInt() % (q+1));**
for (i = p + 1; i <= q; i++) {
if (A[i] < A[p]) {
swap(A, ++last, i);
}
}
swap(A, p, last);
QuickSort(A, p, last - 1);
QuickSort(A, last + 1, q);
}
private static void swap(int[] A, int i, int j) {
int temp;
temp = A[i];
**A[i] = A[j];**
A[j] = temp;
}
public static void main(String[] args){
int[] A = {2,5,7,3,9,0,1,6,8};
**QuickSort(A, 0,8 );**
System.out.println(Arrays.toString(A));
}
}
the Stack traces :
run:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -3
at JavaQuickSort.swap(JavaQuickSort.java:38)
at JavaQuickSort.QuickSort(JavaQuickSort.java:22)
at JavaQuickSort.main(JavaQuickSort.java:45)
Java Result: 1
BUILD SUCCESSFUL (total time: 2 seconds)
I also bold those statements that cause these stack traces. like ==> ** ...**
EDITED:
public class JavaQuickSort {
public static void QuickSort(int arr[],int lo,int hi) {
int n = arr.length;
if(n<=1)
return;
**int r = partition(arr);**
**QuickSort(arr,lo , r-1);**
QuickSort(arr, r+1, hi);
}
private static void swap(int[] A, int i, int j) {
int temp;
temp = A[i];
**A[i] = A[j];**
A[j] = temp;
}
public static int partition(int arr[]){
int i, last = 0;
Random rand = new Random();
int n = arr.length;
if (n <= 1)
return arr[0];
**swap(arr, 0, rand.nextInt(n));**
for (i =1; i <n; i++) {
if (arr[i] < arr[0]) {
swap(arr, ++last, i);
}
}
swap(arr, 0, last);
return last;
}
public static void main(String[] args){
int[] A = {2,5,7,3,9,0,1,6,8};
**QuickSort(A, 0,8 );**
System.out.println(Arrays.toString(A));
}
}
I have edited my post for being more understandable also it will print these stack traces and I bold the lines that cause these stack traces!!!
the stack traces :
run:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at JavaQuickSort.swap(JavaQuickSort.java:27)
at JavaQuickSort.partition(JavaQuickSort.java:39)
at JavaQuickSort.QuickSort(JavaQuickSort.java:19)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.main(JavaQuickSort.java:52)
Java Result: 1
BUILD SUCCESSFUL (total time: 2 seconds)
please help me thanks
EDITED :
I have written this code with paying attention to the forst answer of this post but it wont sort my array!!!
public class JavaQuickSort {
public static void QuickSort(int arr[], int lo, int hi) {
if (hi > lo) {
Random rand = new Random();
int pivotIndex = lo + rand.nextInt(hi-lo+1);
int pivotnewIndex = partition(arr, lo, hi,pivotIndex);
QuickSort(arr, lo, pivotnewIndex - 1);
QuickSort(arr, pivotnewIndex + 1, hi);
}
}
private static void swap(int[] A, int i, int j) {
int temp;
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
public static int partition(int arr[],int lo,int hi,int pivotIndex)
{
int pivotValue = arr[pivotIndex];
swap(arr, hi, pivotIndex);
int storeIndex = lo;
for(int i = lo;i<hi;i++){
if (arr[i]<=pivotValue)
swap(arr, storeIndex, i);
storeIndex = storeIndex ++;
}
swap(arr, storeIndex, hi);
return storeIndex;
}
public static void main(String[] args) {
int[] A = {2, 5, 7, 3, 9, 0, 1, 6, 8};
QuickSort(A, 0, 8);
System.out.println(Arrays.toString(A));
}
}
the output:
run:
[2, 9, 3, 8, 0, 6, 7, 1, 5]
BUILD SUCCESSFUL (total time: 2 seconds)
I need your help really I confused !!!

Some problems with your code are:
Don't create a new Random instance for one time uses. Just create it once, store it in say a static field, and then use it to generate many random numbers for your application.
When creating a random index for the pivot, it must lie between p and q inclusive.
Use Random.nextInt(int n) overload to generate a random number in a given range
Perhaps use lo and hi instead of p and q, and int[] arr instead of int A[]
You can get the length of an array with its .length member
As for the quicksort algoritm itself, it doesn't look like anything I've seen before. I recommend studying the standard imperative implementation and adapting that.
See also
Wikipedia/Quicksort
Update
Unfortunately you have mixed up the pseudocodes from Wikipedia somewhat. You want to adapt this algorithm:
function partition(array, left, right, pivotIndex)
pivotValue := array[pivotIndex]
swap array[pivotIndex] and array[right] // Move pivot to end
storeIndex := left
for i from left to right - 1 // left ≤ i < right
if array[i] ≤ pivotValue
swap array[i] and array[storeIndex]
storeIndex := storeIndex + 1
swap array[storeIndex] and array[right] // Move pivot to its final place
return storeIndex
procedure quicksort(array, left, right)
if right > left
select a pivot index //(e.g. pivotIndex := left+(right-left)/2)
pivotNewIndex := partition(array, left, right, pivotIndex)
quicksort(array, left, pivotNewIndex - 1)
quicksort(array, pivotNewIndex + 1, right)
Note that the above algorithm selects the middle element of the subarray between left and right for the pivot index. Since you want a randomized quicksort, you want to choose a random index between left and right inclusive, so the formula needs to be changed as follows:
pivotIndex := left + (random number between 0 and right-left inclusive)
So, for example, if left = 5 and right = 7, then you want pivotIndex to be 5 + x where x is a random number between 0 and 7-5=2 inclusive.
Since Random.nextInt(n) has an exclusive upper bound, translating this to Java would be something like:
int pivotIndex = lo + rand.nextInt(hi - lo + 1);
Final correction
You've made a common mistake for beginners here:
// HORRIBLE FORMATTING! Don't do this!
if (arr[i]<=pivotValue)
swap(arr, storeIndex, i);
storeIndex = storeIndex ++;
If you noticed the pseudocode above, both statements are supposed to be part of the if body, but the above code, when properly indented and with braces added, is really this:
// PROPER FORMATTING! Reveals bug instantly!
if (arr[i]<=pivotValue) {
swap(arr, storeIndex, i);
}
storeIndex = storeIndex ++;
The fix, therefore is to move the storeIndex increment to the if body like this:
// Corrected according to pseudocode!
if (arr[i]<=pivotValue) {
swap(arr, storeIndex, i);
storeIndex = storeIndex ++;
}
Or you can also just do:
// Nice and clear!
if (arr[i]<=pivotValue) {
swap(arr, storeIndex++, i);
}
The lessons from this latest update is:
Always indent your code properly
A good IDE can make this a breeze, you really have no excuse
Make a habit of putting braces on if statements, even when they're not strictly necessary
Many subtle bugs are due to omission of braces
Use post/pre-increments sensibly
Like many things, they can be abused beyond comprehension, but used appropriately and idiomatically they lead to concise and readable code
One last thing
When I mentioned .length of arrays, I meant that instead of this:
int[] A = {2, 5, 7, 3, 9, 0, 1, 6, 8};
QuickSort(A, 0, 8); // BAD! Don't hardcode array length!
You should do this:
int[] arr = {2, 5, 7, 3, 9, 0, 1, 6, 8};
QuickSort(arr, 0, arr.length - 1); // GOOD!

The random generator produces positive and negative values. That's why eventually you call swap with a negative q value.

swap(A, p, rand.nextInt() % (q+1));
The random integer generated may be below p, clearly you would want it to be something between p and q.

As this is not homework, and I'd take the homework category to encompass self-learning, you should just use the standard Arrays.sort(int[]).

Related

How can I return the number of pairs that do not satisfy the sorting condition from largest to smallest using merge sort?

I have a function public static int countBaad(int[] hs) that takes in an input array and I'm supposed to find how many numbers are smaller than the ones ahead of it.
For instance,
if hs = [7,3,5,4,1] the answer would be 2 because the pairs that violate the order are 3 and 5 and 3 and 4, since 3 is smaller than them and should've been ahead of them.
if hs = [8,5,6,7,2,1] the answer would be 3 because 5 is smaller than 6 and 7, giving us 2, and since 6 is also smaller than 7, we would get a total of 3 wrong pairs
Here is my current code using the merge sort approach:
public static int countBaad(int[] hs){
return mergeSort(hs, hs.length);
}
public static int mergeSort(int[] a, int n) {
if (n < 2) {
return n;
}
int mid = n / 2;
int[] l = new int[mid];
int[] r = new int[n - mid];
for (int i = 0; i < mid; i++) {
l[i] = a[i];
}
for (int i = mid; i < n; i++) {
r[i - mid] = a[i];
}
mergeSort(l, mid);
mergeSort(r, n - mid);
return merge(a, l, r, mid, n - mid);
}
public static int merge(int[] a, int[] l, int[] r, int left, int right) {
int size = 0;
int i = 0, j = 0, k = 0;
while (i < left && j < right) {
if (l[i] <= r[j]) {
a[k++] = l[i++];
size++;
}
else {
a[k++] = r[j++];
size++;
}
}
while (i < left) {
a[k++] = l[i++];
size++;
}
while (j < right) {
a[k++] = r[j++];
size++;
}
return size;
}
This code gives me the incorrect output after I put in arrays
hs = [7,3,5,4,1] returns 5
hs = [8,5,6,7,2,1] returns 6
What am I doing wrong here, can anyone please correct me?
What your code is currently doing is attempting a sort and then simply returning the size of the sorted array (big surprise, given the aptly named size variable).
Basically you are sorting in descending order and your specification calls for the result to be how many numbers were smaller than those appearing later in the array.
However, in merge you are actually adding to size regardless of their values.
Then, you're only returning the 'size' result of the final merge, not that of the sorting steps required inbetween.
And finally, perhaps the elephant in the room, is that you're performing a (unnecessary) sort as a side effect, but ignoring it completely.
Long story short, the code is too complicated and error prone for what it is supposed to do.
Here's a simple double for loop that achieves the desired outcome:
public static int countBaad(int[] hs){
int count = 0;
for(int i = 0; i < hs.length; i++) {
for(int j = i+1; j < hs.length; j++) {
//compare the i'th position with all subsequent positions
int current = hs[i];
int other = hs[j];
if(current < other) {
System.out.println("Found bad number pair: ("+current+","+other+")");
count++;
}
}
}
return count;
}
System.out.println(countBaad(new int[]{7,3,5,4,1}));
//prints:
//Found bad number pair: (3,5)
//Found bad number pair: (3,4)
//2
System.out.println(countBaad(new int[]{8,5,6,7,2,1}));
//prints:
//Found bad number pair: (5,6)
//Found bad number pair: (5,7)
//Found bad number pair: (6,7)
//3
This is much more succinct and free from side effects.
Edit:
Fixing the mergeSort code, with extra sysout logging to illustrate the algorithm:
public static int mergeSort(int[] a, int n) {
if(n==1) {
//No sorting required, so the result should be 0.
return 0;
}
int mid = n / 2;
int[] l = new int[mid];
int[] r = new int[n - mid];
//'splitting the array' loops are just arraycopy, so
// should use the native implementation:
System.arraycopy(a, 0, l, 0, mid);
if(n - mid >= 0) System.arraycopy(a, mid, r, 0, n - mid);
//add the results from all merges, not just the last one
int result = 0;
result += mergeSort(l, mid);
result += mergeSort(r, n - mid);
result += merge(a, l, r); //there is no need to pass in the array lengths
return result;
}
public static int merge(int[] a, int[] l, int[] r) {
System.out.println("Merging "+Arrays.toString(l)+" and "+Arrays.toString(r));
int size = 0;
int lIdx = 0, rIdx = 0, aIdx = 0;
while (lIdx < l.length && rIdx < r.length) {
if (l[lIdx] >= r[rIdx]) {
a[aIdx++] = l[lIdx++];
//size++; //no: left was already bigger than right
}
else {
//take from the right.
//This number is bigger than all the numbers remaining on the left.
for(int tempIdx = lIdx;tempIdx<l.length;tempIdx++) {
//this loop is for illustration only
System.out.println(" Found bad pair: (" + l[tempIdx] + "," + r[rIdx] + ")");
}
size+=l.length-lIdx;
a[aIdx++] = r[rIdx++];
}
}
//while (lIdx < left) { //NOTE that you had this condition incorrectly reversed resulting in bad merge
// a[aIdx++] = l[lIdx++];
// size++; //no, no comparisons are taking place here
//}
//while (rIdx < right) { //NOTE that you had this condition incorrectly reversed, resulting in bad merge
// a[aIdx++] = r[rIdx++];
// size++; //no, no comparisons are taking place here
//}
//we can also replace the above two loops with arraycopy
// which will perform better on large arrays
if(lIdx < left) {
System.arraycopy(l, lIdx, a, aIdx, l.length-lIdx);
}
if(rIdx < right) {
System.arraycopy(r, rIdx, a, aIdx, r.length-rIdx);
}
return size;
}
Since you value performance, you should use System.arraycopy where possible. I have also renamed the loop variables to make the code easier to understand.
System.out.println(countBaad(new int[]{7,3,5,4,1}));
//prints:
//Merging [7] and [3]
//Merging [4] and [1]
//Merging [5] and [4, 1]
//Merging [7, 3] and [5, 4, 1]
// Found bad pair: (3,5)
// Found bad pair: (3,4)
//2
System.out.println(countBaad(new int[]{8,5,6,7,2,1}));
//prints:
//Merging [5] and [6]
// Found bad pair: (5,6)
//Merging [8] and [6, 5]
//Merging [2] and [1]
//Merging [7] and [2, 1]
//Merging [8, 6, 5] and [7, 2, 1]
// Found bad pair: (6,7)
// Found bad pair: (5,7)
//3
Edit #2
To remove the side effects (sort) from this method, the input array can be copied, for example with a simple call to Arrays.copyOf(hs, hs.length); and passing in the result instead of the original.

java.lang.IllegalArgumentException in QuickSort method [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I am following the following pseudocode:
function quicksort(array)
if length(array) > 1
pivot := select any element of array
left := first index of array
right := last index of array
while left ≤ right
while array[left] < pivot
left := left + 1
while array[right] > pivot
right := right - 1
if left ≤ right
swap array[left] with array[right]
left := left + 1
right := right - 1
quicksort(array from first index to right)
quicksort(array from left to last index)
but when I try to implement it in java, I insert code:
import java.util.Arrays;
public class ALGQuickSort {
public static void main(String[] args) {
int[] array = {6, 3, 4, 8, 9, 10, 1};
quickSort(array);
System.out.println(Arrays.toString(array));
}
public static void quickSort(int[] array) {
int pivot = array[array.length - 1];
if (array.length > 1) {
int left = 0;
int right = array.length - 1;
while (left <= right) {
while (array[left] < pivot) {
left++;
}
while (array[right] > pivot) {
right--;
}
if (left <= right) {
swap(array[left], array[right]);
right--;
left++;
}
int[] array1 = Arrays.copyOfRange(array, 0, right);
int[] array2 = Arrays.copyOfRange(array, left, array.length - 1);
quickSort(array1);
quickSort(array2);
}
}
}
public static void swap(int a, int b) {
int aux = a;
a = b;
b = aux;
}
}
the system shows me the following error on the screen:
Exception in thread "main" java.lang.IllegalArgumentException: 5 > 4
at java.util.Arrays.copyOfRange(Arrays.java:3591) at
alg.quicksort.ALGQuickSort.quickSort(ALGQuickSort.java:43) at
alg.quicksort.ALGQuickSort.quickSort(ALGQuickSort.java:44) at
alg.quicksort.ALGQuickSort.main(ALGQuickSort.java:21)
C:\Users\Alex\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53:
Java returned: 1
the error is in the line:
int[] array2 = Arrays.copyOfRange(array, left, array.length - 1);
someone can help me pls?
Improvements/corrections to your quick-sort algorithm:
Correct your swap method: The swap method that you are using will not work.
The recursive call should be outside the for loop: Your pseudo-code is correct, but in your implementation it is not the case.
Place the Pivot at its correct position and then have a recursive call on sub-arrays that now (for-sure) should not contain the pivot element. After the while loop, you are sure that right+1 == left (think on it a bit and you will understand why this is true). Now swap the array[left] with pivot element and give a recursive call on 2 different sub-arrays (left sub-array to the pivot is beginIndex..right and right sub-array to the pivot is left+1..endIndex where I assume that you need to sort array[beginIndex..endIndex])
Avoid copying: Its better to avoid copying a part of the array (instead you can pass the startIndex and the endIndex to denote the sub-array you want to sort)
Use Randomized Quick sort: It would also be better if you don't always select the last element as your pivot (what you can do here is just before starting to sort select any random element and then swap it with the last element of your array. Using this strategy you don't have to make much changes in your existing code) This selection of random element as pivot is much better. See this link for more details.
Make swap method private: Not relevant to the algorithm, but it is good if you make the swap method private as you might not intend to call it from outside of this class.
If you intend to swap the elements of array with index say index1 and index2, then the following code will work:
public static void swap(int[] array, int index1, int index2) {
int aux = array[index1];
array[index1] = array[index2];
array[index2] = aux;
}
The following is the final code with all the above suggested changes:
public static void main(String[] args) {
int[] array = {6, 30, 7, 23, 4, 8, 9, 10, 1, 90};
quickSort(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));
}
public static void quickSort(int[] array, int beginIndex, int endIndex) {
// System.out.println("called quick sort on the following : " + beginIndex + " " + endIndex);
int arrayLength = endIndex - beginIndex + 1;
int pivot = array[endIndex];
if (arrayLength > 1) {
int left = beginIndex;
int right = endIndex - 1;
while (left <= right) {
// System.out.println(left + " " + right);
while (left <= endIndex && array[left] < pivot) {
left++;
}
while (right >= beginIndex && array[right] > pivot) {
right--;
}
if (left <= right) {
swap(array, left, right);
right--;
left++;
}
}
swap(array, left, endIndex); // this is crucial, and you missed it
if (beginIndex < right) {
quickSort(array, beginIndex, right);
}
if (left + 1 < endIndex) {
quickSort(array, left + 1, endIndex);
}
}
}
private static void swap(int[] array, int index1, int index2) {
int aux = array[index1];
array[index1] = array[index2];
array[index2] = aux;
}
Firstly, the two lines
int[] array1 = Arrays.copyOfRange(array, 0, right);
int[] array2 = Arrays.copyOfRange(array, left, array.length - 1);
are wrong. You must not copy the arrays. This will prevent the Quicksort algorithm from working since you sort the temporary copies in your recursive calls. The sorted sub arrays will be discarded afterwards.
Another bug in your program raises the exception: the third parameter of Arrays.copyOfRange is exclusive. I.e. it will not copy elements from to to but from to to - 1. But you already subtracted one from the upper bound, and in Quicksort it might happen that one of the sub arrays has zero size. In this case the range to copy becomes negative. That's the exception.
There is a third bug: you swapped the first two lines of the pseudo code. This will crash on empty arrays.
Finally, you cannot implement the Quicksort algorithm in this way as shown in the pseudo code because Java (unlike e.g. C) does not support array slices.
You need to modify the algorithm in a way that you split it into two methods:
One method to recursively sort an array slice:
Note that I replaced the array start and end by from and to.
public static void quickSort(int[] array, int from, int to) {
if (array.length <= 1)
return;
int pivot = array[to];
int left = from;
int right = to;
while (left <= right) {
while (array[left] < pivot)
left++;
while (array[right] > pivot)
right--;
if (left <= right) {
swap(array[left], array[right]);
right--;
left++;
}
quickSort(array, from, right);
quickSort(array, left, to);
}
}
And a second method to sort an entire array that calls the above one for the entire array.
public static void quickSort(int[] array) {
return quickSort(array, 0, array.length - 1);
}

Quickselect implementation not working

I am trying to write code to determine the n smallest item in an array. It's sad that I am struggling with this. Based on the algorithm from my college textbook from back in the day, this looks to be correct. However, obviously I am doing something wrong as it gives me a stack overflow exception.
My approach is:
Set the pivot to be at start + (end-start) / 2 (rather than start+end/2 to prevent overflow)
Use the integer at this location to be the pivot that I compare everything to
Iterate and swap everything around this pivot so things are sorted (sorted relative to the pivot)
If n == pivot, then I think I am done
Otherwise, if I want the 4 smallest element and pivot is 3, for example, then I need to look on the right side (or left side if I wanted the 2nd smallest element).
-
public static void main(String[] args) {
int[] elements = {30, 50, 20, 10};
quickSelect(elements, 3);
}
private static int quickSelect(int[] elements2, int k) {
return quickSelect(elements2, k, 0, elements2.length - 1);
}
private static int quickSelect(int[] elements, int k, int start, int end) {
int pivot = start + (end - start) / 2;
int midpoint = elements[pivot];
int i = start, j = end;
while (i < j) {
while (elements[i] < midpoint) {
i++;
}
while (elements[j] > midpoint) {
j--;
}
if (i <= j) {
int temp = elements[i];
elements[i] = elements[j];
elements[j] = temp;
i++;
j--;
}
}
// Guessing something's wrong here
if (k == pivot) {
System.out.println(elements[pivot]);
return pivot;
} else if (k < pivot) {
return quickSelect(elements, k, start, pivot - 1);
} else {
return quickSelect(elements, k, pivot + 1, end);
}
}
Edit: Please at least bother commenting why if you're going to downvote a valid question.
This won't fix the issue, but there are several problems with your code :
If you do not check for i < end and j > start in your whiles, you may run into out of bounds in some cases
You choose your pivot to be in the middle of the subarray, but nothing proves that it won't change position during partitioning. Then, you check for k == pivot with the old pivot position, which obviously won't work
Hope this helps a bit.
Alright so the first thing I did was rework how I get my pivot/partition point. The shortcoming, as T. Claverie pointed out, is that the pivot I am using isn't technically the pivot since the element's position changes during the partitioning phase.
I actually rewrote the partitioning code into its own method as below. This is slightly different.
I choose the first element (at start) as the pivot, and I create a "section" in front of this with items less than this pivot. Then, I swap the pivot's value with the last item in the section of values < the pivot. I return that final index as the point of the pivot.
This can be cleaned up more (create separate swap method).
private static int getPivot(int[] elements, int start, int end) {
int pivot = start;
int lessThan = start;
for (int i = start; i <= end; i++) {
int currentElement = elements[i];
if (currentElement < elements[pivot]) {
lessThan++;
int tmp = elements[lessThan];
elements[lessThan] = elements[i];
elements[i] = tmp;
}
}
int tmp = elements[lessThan];
elements[lessThan] = elements[pivot];
elements[pivot] = tmp;
return lessThan;
}
Here's the routine that's calls this:
private static int quickSelect(int[] elements, int k, int start, int end) {
int pivot = getPivot(elements, start, end);
if (k == (pivot - start + 1)) {
System.out.println(elements[pivot]);
return pivot;
} else if (k < (pivot - start + 1)) {
return quickSelect(elements, k, start, pivot - 1);
} else {
return quickSelect(elements, k - (pivot - start + 1), pivot + 1, end);
}
}

Finding the K-th largest element in an array using a tournament tree

Below, I have designed a function tournamentTreeKSelection which simulates a tree like structure using arrays and returns the largest element in the array. For example, given an input array [10,9,8,7,6,5,4,3,2,1] the following steps are performed to return 10.
[10, 8, 6, 4, 2, -1]
[10, 6, 2, -1]
[10, 2]
[10] //Max element of array found
My goal is to now add a second parameter int k requesting that the function return the k-th largest element such that tournamentTreeKSelection(data, 2) returns 9.
I'm having a lot of difficulty in modifying my algorithm to perform this task because my assumption is that i'm going to have to keep track of all elements that the max element beats ? Any help is appreciated.
import java.util.ArrayList;
import java.util.Arrays;
public class TournamentTree {
public static int tournamentTreeKSelection(int[] data, int k) {
ArrayList<Integer> list = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
for(int i = 0; i < data.length - 1; i += 2) {
list.add(max(data[i] , data[i + 1]));
}
for(int i = 0; i < data.length - 1; i++) {
list2.add(min(data[i], data[i + 1]));
}
if(list.size() == 1) return list.get(0);
if(list.size() % 2 != 0) list.add(-1);
if(k == 1) return tournamentTreeKSelection(listToArray(list),k);
else return tournamentTreeKSelection(listToArray(list2), --k);
}
public static int max(int a, int b) {
return a > b ? a : b;
}
public static int min(int a, int b) {
return a > b ? b : a;
}
public static int[] listToArray(ArrayList<Integer> arr) {
int[] arr2 = new int[arr.size()];
for(int i = 0; i < arr.size(); i++)
arr2[i] = arr.get(i);
return arr2;
}
}
I have now modified the code but it only works for k = 1 - 8, why does it break down ? tournamentTreeKSelection(data, 9) and tournamentTreeKSelection(data, 10) return 3 when they should be returning 2 and 1 respectively.
First of all, why your code is wrong:
When the size of the list is 2 or 3, your statement list.size() == 1 will be true even if K > 1.
Why do you do min(data[i], data[i + 1]), I have a feeling you just want to remove the maximum element but what with the case
[10,1,9,2,8,3,7,4,6,5], gives after 1 iteration [1,1,2,2,3,3,4,4,5] removing possible outcomes 9, 8, 7 and 6.
Some tips
Don't do useless computations. You are calculating the two lists, while you know in front you are only going to use one of them.
Use builtin methods whenever possible, see Math.max, Math.min
Note that you know the size of the resulting array in front. There is no need to create an ArrayList which causes a lot of overhead for you. Just create an array of the resulting size. For k==1, ((data.length+1)/2) else data.length-1
Still wondering
You say your tournament tree structure is a requirement, but you are looping over it in your code as it is an array. Why? You could determine the max value from the moment K==1 in 1 loop, instead of taking half of the maxes and doing it over and over again.
Alternative approach
As already suggested the sorting approach, or the quick find methods can be used. I was thinking how you could still use your tournament tree approach. And the best I came up with is how merge sort works. I slightly edited because you only need max K elements to return.
public static int find(int[] a, int k) {
int[] max = find(a, 0, a.length - 1, k);
return max[k-1];
}
private static int[] find(int[] a, int lo, int hi, int k) {
if (hi < lo){
return new int[]{};
}
if(lo == hi){
return new int[]{a[lo]};
}
int mid = lo + (hi - lo) / 2;
int[] left = find(a, lo, mid, k);
int[] right = find(a, mid + 1, hi, k);
return merge(left, right, k);
}
private static int[] merge(int[] left, int[] right, int k) {
int[] res = new int[Math.min(k, left.length+right.length)];
int l = 0, r = 0;
for (int i = 0; i<res.length;i++) {
if (l == left.length)
res[i] = right[r++];
else if (r == right.length)
res[i] = left[l++];
else if (left[l] > right[r])
res[i] = left[l++];
else
res[i] = right[r++];
}
return res;
}

Choosing the median as the pivot

I am trying to select the median as the pivot in this app but seems like I am doing something wrong. Any help will be greatly appreciated.
I get the first few numbers in order and then towards the end.
public class QuickSort {
public static void main(String[] args) {
int [] list = {1,3,2,4,6,5,8,7,9,0};
quickSort (list);
for (int i=0; i < list.length; i++)
System.out.print(list[i] + " ");
}
public static void quickSort(int [] list)
{
quickSort(list, 0, list.length - 1);
}
public static void quickSort (int[] list, int first, int last)
{
int size = last - first + 1;
if (size > 3){
int median1 = median(list, first, last);
int partition1 = partition(list, first, last, median1);
quickSort(list, first, partition1 - 1);
quickSort(list, partition1 + 1, last);
}
}
public static int median(int [] list, int first1, int last1){
int middle = (first1 + last1)/2;
if (list[first1] > list[middle])
swap(list, first1, middle);
if (list[first1] > list[last1])
swap(list, first1, last1);
if (list[middle] > list[last1])
swap(list, middle, last1);
swap(list, middle, last1 - 1);
return list[last1 - 1 ];
}
public static int partition(int [] list, int left, int right, long pivot) {
int leftPtr = left;
int rightPtr = right - 1;
while (true) {
while (list[++leftPtr] < pivot);
while (list[--rightPtr] > pivot);
if (leftPtr >= rightPtr)
break;
else
swap(list, leftPtr, rightPtr);
}
swap(list, leftPtr, right - 1);
return leftPtr;
}
public static void swap(int []list, int dex1, int dex2) {
int temp = list[dex1];
list[dex1] = list[dex2];
list[dex2] = temp;
}
I get some numbers in order, but not all of them.
There are usually two approaches you can use to computer programming. You can either a) Take something that works and start removing/changing stuff until you get what you like. or b) Take the smallest case you can -- make it work -- and then keep on adding more complexicity.
In this case I suggest b) (FYI b) is usuall the best approach)
If you start with int[] list = { 3, 1, }, you can see that your list doesn't get sorted correctly.
This is because your quickSort function is only sorting lists of size greater than 3. In your case this condition should probably be "size > 1" -- though there may be some more things you need to do in order to get things working.
There's lot of info about QuickSort on wikipedia:
http://en.wikipedia.org/wiki/Quicksort

Categories