Quick Sort. Handling Duplicates - java

Can someone show me how to handle duplicates in QuickSort with my function here?
private static int[] quickSort(int[] input, int left, int right) {
int mid = (left + right) / 2;
int i = left;
int j = right;
if((right - left) < 1) {
return new int[]{};
}
while(i <= j) {
while((input[i] < input[mid])) {
i++;
}
while((input[j] > input[mid])) {
j--;
}
if(i <= j) {
int temp = input[i];
input[i] = input[j];
input[j] = temp;
i++;
j--;
}
}
if(j > left) {
input = quickSort(input, left, j);
}
if(i < right) {
input = quickSort(input, i, right);
}
return input;
}

The code has several problems.
"return new int[]{};" This will generate new arrays and consumes memory unnecessarily. You may return "input".
For the first inner loops, you may want to have "input[i] <= input[mid] && i < mid". This will handle duplicates.
There are some well-tested code here: Quick Sort

Related

Analysis of Hoare's Partioning

I have an assignment to draw a graph and analyze the Hoare's partitioning algorithm and compare theoretical and practical worst-case scenario values. It says that I need a count variable to keep track of critical steps (i.e comparison and swapping). I wrote this code for Hoare's partitioning.
public static int partition(Comparable[] a, int left, int right) {
Comparable pivot = a[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true) {
do {
i++;
} while (a[i].compareTo(pivot) < 0);
do {
j--;
} while (a[j].compareTo(pivot) > 0);
if (i < j) {
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
} else {
return j;
}
}
}
Now I am confused about where to add the count variable. I know that count variable will be incremented in if (i < j) line. But do I also need to increment count in both do-while loops to keep track of comparisons?
So, the updated code will be like this?
public static int partition(Comparable[] a, int left, int right) {
int count = 0;
Comparable pivot = a[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true) {
do {
count++;
i++;
} while (a[i].compareTo(pivot) < 0);
do {
count++;
j--;
} while (a[j].compareTo(pivot) > 0);
if (i < j) {
count++;
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
} else {
return j;
}
}
}
Or like this?
public static int partition(Comparable[] a, int left, int right) {
int count = 0;
Comparable pivot = a[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true) {
do {
i++;
} while (a[i].compareTo(pivot) < 0);
do {
j--;
} while (a[j].compareTo(pivot) > 0);
if (i < j) {
count++;
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
} else {
return j;
}
}
}

Why is this test for my quick sort failing?

I'm trying to implement quicksort using the median of three algo and it fails a unit test I wrote related to a small partition. I changed my earlier partition and now it passes one of the tests it used to fail, but still fails the one at the bottom:
My code is:
public class QuickSort {
static void swap(int[] A, int i, int j) {
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
static final int LIMIT = 15;
static int partition(int[] a, int left, int right){
int center = (left + right) / 2;
if(a[center] < (a[left]))
swap(a,left,center);
if(a[right-1] < a[left])
swap(a,left,right);
if(a[right-1] < a[center])
swap(a,center,right);
swap(a,center,right - 1);
return a[right - 1];
}
static void quicksort(int[] a, int left, int right){
if(left + LIMIT <= right){
int pivot = partition(a,left,right);
int i = left;
int j = right - 1;
for(;;){
while(a[++i] < pivot){}
while(a[--j] > pivot){}
if(i < j)
swap(a,i,j);
else
break;
}
swap(a,i,right - 1);
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
else{
insertionSort(a);
}
}
public static void insertionSort(int[] a){
int j;
for(int p = 1; p < a.length; p++){
int tmp = a[p];
for(j = p; j > 0 && tmp < a[j-1]; j--)
a[j] = a[j-1];
a[j] = tmp;
}
}
}
This test fails:
public void smalltest() throws Exception {
int[] Arr_orig = {3,9,8,2,4,6,7,5};
int[] Arr = Arr_orig.clone();
int pivot = QuickSort.partition(Arr, 0, Arr.length);
for (int i = 0; i != pivot; ++i)
assertTrue(Arr[i] <= Arr[pivot]); //fails!
for (int i = pivot + 1; i != Arr.length; ++i)
assertTrue(Arr[pivot] < Arr[i]);
}
There is a conflict between using right-1 and right in this sequence:
if(a[right-1] < a[left])
swap(a,left,right);
You need to decide if right is going to be the index to the last element, or 1 + index to the last element (an "ending" index).
There may be other problems, but this is the first one I noticed.

QuickSort Java implementation issue

I'm trying to implement a QuickSort algorithm as a homework at the university, but I just fail to understand where there is a mistake in my code. I suppose it's a logical mistake, I think I swap my pivot wrongly. I could really use some help, thank you in advance. There is the code:
public class QuickSort {
private int [] array;
public QuickSort(int [] array){
this.array = array;
}
public void sort(){
partition(0, array.length - 1);
}
public void partition(int start, int end){
if (end - start < 2){
return;
}
int pivot_index = end;
int i = start;
int j = end - 1;
while (i < j){
//both elements are not in the right place
if(array[i] > array[pivot_index] && array[j] <= array[pivot_index]){
swap(array, i, j);
i++;
j--;
}
//the element on the left is not in place
else if (array[i] > array[pivot_index] && array[j] > array[pivot_index]){
j--;
}
//the element on the right is not in place
else if (array[i] < array[pivot_index] && array[j] < array[pivot_index]){
i++;
}
//both elements are in place
else {
i++;
j--;
}
}
if (array[i] > array[pivot_index]){
swap(array, pivot_index, i);
pivot_index = i;
}
partition(start, pivot_index - 1);
partition(pivot_index + 1, end);
}
private static void swap(int [] tab, int index1, int index2){
int temp = tab[index1];
tab[index1] = tab[index2];
tab[index2] = temp;
}
}
Solution one
The idea is iterating through the array to check whether the current element is smaller than the last one (the pivot), if yes swap with the first one that is not (index is lastSmaller + 1).
private void partition(int start, int end) {
if (start >= end) {
return;
}
int lastSmallest = start - 1;
for (int i = start; i < end; i++) {
if (array[i] < array[end])
swap(++lastSmallest, i);
}
swap(++lastSmallest, end);
partition(start, lastSmallest - 1);
partition(lastSmallest + 1, end);
}
Solution two
I think this is what you want to implement. Iterate through the array, skip all the elements in good place on the left and right. Swap the two in wrong place.
private void partition(int start, int end) {
if (end <= start) {
return;
}
int k = end;
int i = start;
int j = end - 1;
while (i < j) {
// left is in place
while (i < j && array[i] < array[k]) {
i++;
}
// right is in place
while (i < j && array[j] >= array[k]) {
j--;
}
// both are not good
if (i < j) {
// swap
int temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
}
// swap left and pivot
if (array[i] >= array[k]) {
int temp = array[i];
array[i] = array[k];
array[k] = temp;
}
partition(start, i - 1);
partition(i + 1, end);
}
The reason why your solution does not work is that when you find both are not in place, you swap them and continue to partition the rest of the array. However, you can not guarantee what you have swapped does not violate the rule. Therefore, you need to skip all the elements in place on both side first then swap.

Java Natural Merge Sort implementation

I can be considered new in Java . I ' ve been struggling with one question which was given to us by our professor in our school. I am supposed to make a natural merge sort algorithm which has to find two sorted sub-arrays everytime and merge them.(which is a version of bottom up merge sort). But i am stuck here is my code
public class NaturalMergeMine {
private static Comparable[] aux;
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i += 1) {
if (a[i - 1].compareTo(a[i]) < 0) {
return false;
}
}
return true;
}
private static void sort(Comparable[] a, int lo, int hi) {
int i = lo;
int j = 0;
int mid = 0;
int az = 0;
while (true) {
i = 0;
System.out.println("outter");
while (i < a.length) {
System.out.println("inner 1");
if (i == a.length - 1) {
break;
} else if (a[i].compareTo(a[i + 1]) < 0) {
break;
}
i++;
}
j = i + 1;
while (j < a.length) {
System.out.println("inner 2");
if (j == a.length - 1) {
break;
} else if (a[j].compareTo(a[j + 1]) < 0) {
break;
}
j++;
}
mid = lo + (j - lo) / 2;
Merge(a, lo, mid, j);
lo = 0;
if (isSorted(a)) {
break;
}
}
}
public static void Merge(Comparable[] a, int lo, int mid, int hi) {
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
for (int k = lo; k <= hi; k++) {
if (i > mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (aux[i].compareTo(aux[j]) < 0) {
a[k] = aux[j++];
} else {
a[k] = aux[i++];
}
}
}
public static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
public static void main(String[] args) {
Integer[] arr = {6, 4, 5, 7, 8, 3, 2, 1};
sort(arr);
show(arr);
}
}
what happens is that it is not merging correctly and it goes in an infinite loop in the outter loop ,which is because it is not sorted properly. Is there anyone who can advice me a better way or can tell me the mistake i make here. Thanks in advance.
The issue is in the mid calculation, im not quite sure why you do that,but if the mid is less than i in you merge method you wont reach the fault case so you will stuck in discovering it without solving, for each run you need to pick up two arrays to sort, so instead of mid you can insert i to the merge method so you start the merging from the fault in.
public class NaturalMergeMine {
private static Comparable[] aux;
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i += 1) {
if (a[i - 1].compareTo(a[i]) > 0) {//changed operator to greater than
return false;
}
}
return true;
}
private static void sort(Comparable[] a, int lo, int hi) {
int i = lo;
int j = 0;
int mid = 0;
int az = 0;
while (true) {
i = 0;
System.out.println("outter");
while (i < a.length) {
System.out.println("inner 1");
if (i == a.length - 1) {
break;
} else if (a[i].compareTo(a[i + 1]) > 0) {//changed operator to greater than
break;
}
i++;
}
j = i + 1;
while (j < a.length) {
System.out.println("inner 2");
if (j == a.length - 1) {
break;
} else if (a[j].compareTo(a[j + 1]) > 0) {//changed operator to greater than
break;
}
j++;
}
// mid = lo + (j - lo) / 2;
Merge(a, lo, i, j);
lo = 0;
if (isSorted(a)) {
break;
}
}
}
public static void Merge(Comparable[] a, int lo, int mid, int hi) {
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
for (int k = lo; k <= hi; k++) {
if (i > mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (aux[i].compareTo(aux[j]) > 0) {//changed the operator to greater than
a[k] = aux[j++];
} else {
a[k] = aux[i++];
}
}
}
public static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
public static void main(String[] args) {
Integer[] arr = {6, 4, 5, 7, 8, 3, 2, 1};
sort(arr);
show(arr);
}
}
Amer Qarabsa's answer fixes the problems with the original code. Below are some alternate examples that are a bit more optimized, scanning the array for pairs of ascending sequences, rather than starting over at the beginning each time. If the array was reversed, then the first pass creates runs of 2, the next pass runs of 4, ... . With the original version, the run size would only increase by one on each loop. The example code below uses open intervals (last index is end of array instead of last element of array).
public class jsortns {
private static Comparable[] aux;
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
int i;
int j;
int k;
while (true) { // merge pass
i = 0;
while(true) { // find, merge pair of runs
j = i; // find left run
while (++j < a.length) {
if (a[j-1].compareTo(a[j]) > 0)
break;
}
if(j == a.length){ // if only one run left
if(i == 0) // if done return
return;
else // else end of merge pass
break;
}
k = j; // find right run
while (++k < a.length) {
if (a[k-1].compareTo(a[k]) > 0){
break;
}
}
Merge(a, i, j, k); // merge runs
i = k;
if(i == a.length) // if end of merge pass, break
break;
}
}
}
// merge left and right runs
// ll = start of left run
// rr = start of right run == end of left run
// ee = end of right run
public static void Merge(Comparable[] a, int ll, int rr, int ee) {
int i = ll;
int j = rr;
int k;
for (k = ll; k < ee; k++)
aux[k] = a[k];
k = ll;
while(true){
// if left element <= right element
if (aux[i].compareTo(aux[j]) <= 0) {
a[k++] = aux[i++]; // copy left element
if(i == rr){ // if end of left run
while(j < ee) // copy rest of right run
a[k++] = aux[j++];
return; // and return
}
} else {
a[k++] = aux[j++]; // copy right element
if(j == ee){ // if end of right run
while(i < rr){ // copy rest of left run
a[k++] = aux[i++];
}
return; // and return
}
}
}
}
No copy back version, instead merges back and forth between two arrays, and only copies at the end if the sorted data ends up in the wrong array.
public class jsortns {
private static Comparable[] b; // temp array
private static Comparable[] o; // original array reference
private static Comparable[] t; // used to swap a, b
public static void sort(Comparable[] a) {
o = a; // save ref to a
b = new Comparable[a.length]; // allocate temp array
int i;
int j;
int k;
while (true) { // merge pass
i = 0;
while(true) { // find, merge pair of runs
j = i; // find left run
while (++j < a.length) {
if (a[j-1].compareTo(a[j]) > 0)
break;
}
if(j == a.length){ // if only one run left
if(i != 0){ // if not done
while(i < j){ // copy run to b
b[i] = a[i];
i++;
}
break; // break to end merge pass
} else { // else sort done
if(a != o){ // if a not original a, copy
for(i = 0; i < a.length; i++)
b[i] = a[i];
}
return;
}
}
k = j; // find right run
while (++k < a.length) {
if (a[k-1].compareTo(a[k]) > 0){
break;
}
}
Merge(a, b, i, j, k); // merge left, right into b
i = k;
if(i == a.length) // break if end pass
break;
}
t = a; // swap a and b (references)
a = b;
b = t;
}
}
// merge left and right runs from a[] to b[]
// ll = start of left run
// rr = start of right run == end of left run
// ee = end of right run
public static void Merge(Comparable[] a, Comparable[] b, int ll, int rr, int ee) {
int i = ll;
int j = rr;
int k = ll;
while(true){
// if left element <= right element
if (a[i].compareTo(a[j]) <= 0) {
b[k++] = a[i++]; // copy left element
if(i == rr){ // if end of left run
while(j < ee) // copy rest of right run
b[k++] = a[j++];
return; // and return
}
} else {
b[k++] = a[j++]; // copy right element
if(j == ee){ // if end of right run
while(i < rr){ // copy rest of left run
b[k++] = a[i++];
}
return; // and return
}
}
}
}

Counting Loops and Comparisons

I need to count the number of loops and comparisons which occur over four different sorting methods. I am using the Selection, Bubble, Insertion, and Quick sort methods. Ideally, I would just place a int such as loopCounter and ++ it every time it loops/compares. Although, being quite new to all of this, I am having trouble distinguishing when I need to include such counters. As you will be able to see in the following code, I attempted to create multiple counters. Although, I think only the selection counters are correct so far.
Additionally, I am required to count the number of times that a value is shifted. In other words, how many times integers are swapped.
Any help with this would be extremely appreciated!
Thanks
ArrayList<Integer> list = new ArrayList<Integer>();
//Counters for Selection Sort
int loopCounter = 0;
int compCounter = 0;
//Counters for Bubble Sort
int loopCounter2 = 0;
int compCounter2 = 0;
//Counters for Insertion Sort
int loopCounter3 = 0;
int compCounter3 = 0;
//Counters for Quick Sort
int loopCounter4 = 0;
int compCounter4 = 0;
public void selectionSort(Integer[] a) {
for(int i = 0; i < a.length; i++) {
int smallestValue = a[i];
int smallestIndex = i;
if(ascButton.isSelected()){
for(int j = i+1; j < a.length; j++) {
if (smallestValue > a[j]) {
smallestValue = a[j];
smallestIndex = j;
loopCounter++;
compCounter++;
}
}
a[smallestIndex] = a[i];
a[i] = smallestValue;
} else if(desButton.isSelected()){
for(int j = i+1; j < a.length; j++) {
if (smallestValue < a[j]) {
smallestValue = a[j];
smallestIndex = j;
loopCounter++;
compCounter++;
}
}
a[smallestIndex] = a[i];
a[i] = smallestValue;
}
}
}
public void bubbleSort(Integer[] a) {
int temp;
for (int i = a.length - 1; i > 0; i--) {
if(ascButton.isSelected()) {
for(int j = 0; j < i; j++) {
loopCounter2++;
compCounter2++;
if(a[j] > a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
} else if(desButton.isSelected()) {
for(int j = 0; j < i; j++) {
loopCounter2++;
compCounter2++;
if(a[j] < a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
}
public void insertionSort(Integer[] a) {
for(int i = 1; i < a.length; i++) {
loopCounter3++;
compCounter3++;
int temp = a[i];
int j = i - 1;
if(ascButton.isSelected()) {
while(j >= 0 && a[j] > temp) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = temp;
} else if(desButton.isSelected()) {
while(j >= 0 && a[j] < temp) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = temp;
}
}
}
public void quickSort(Integer[] a, int left, int right) {
int i = left;
int j = right;
int temp;
int pivot = a[(left + right)/2];
while(i <= j) {
if(ascButton.isSelected()) {
while(a[i] < pivot)
i++;
while(a[j] > pivot)
j--;
} else if(desButton.isSelected()) {
while(a[i] > pivot)
i++;
while(a[j] < pivot)
j--;
}
if(i <= j) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
}
}
if(left < j) {
quickSort(a,left,j);
}
if(i < right) {
quickSort(a, i, right);
}
}
With counters, you simply want to "count" when you want to program to count something. So if you don't understand your own code, then it will be difficult to know when you want to "count" something. I suggest you figure out when a swap is happening, when that is happening in your code, that is when you want to do some sort of:
swapCount++;//Each time a swap happens increment by 1
iterationCount++//A full pass has happened increment by 1
Note: above just because a full pass has happened in many sort, which you probably know, does not mean it is sorted, it is just saying it has done 1 pass.
I'm not sure if this theory will help you any. Give me some feedback on what your still having trouble with and I'll see if I can change my answer to better reflect what your looking for.
As #Andreas suggested, your loop and comparison counters are in place correctly.
As far as the swap counter is concerned, think of it this way - you cannot swap without a temp variable. As a result, whenever a temp variable is involved you want to increase your swap counter.
As an example, for your quicksort, it would look like this:
public void quickSort(Integer[] a, int left, int right) {
int i = left;
int j = right;
int temp;
int pivot = a[(left + right)/2];
while(i <= j) {
if(ascButton.isSelected()) {
while(a[i] < pivot)
i++;
while(a[j] > pivot)
j--;
} else if(desButton.isSelected()) {
while(a[i] > pivot)
i++;
while(a[j] < pivot)
j--;
}
if(i <= j) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
swapCounterForQuickSort++;
}
}
if(left < j) {
quickSort(a,left,j);
}
if(i < right) {
quickSort(a, i, right);
}
}
Follow the same logic for your other sorts.
Also, some general suggestions:
Always name variables so that they tell you what they are used for. Rather than loopCounter1 try using loopCounterForSelectionSort and so on. Don't be afraid of long variable names. Information is power!
Make your functions as short and reusable as possible. For example, you swap integers a lot in your code. Maybe you can just copy the swap code and paste it into a swapIntegers() function. Then everytime you just call this function when you want to swap! Also, notice how this makes your swap counter question easier to answer since you can put a counter in the swap method to do the counting for you. (Although be aware since multiple methods will call the swap counter so you may want to pass it as an argument etc.)

Categories