Need help understanding recursion with array sorts - java

My task is to write a recursive method called quadSort that splits an array into 4 parts which are sorted by quadSort then the first two (A and B) are merged into one array (X) and the second two (C and D) are merged into one (Y) then those two are merged into one. The quadSort should call quadSort() 4 times (once for each part). My problem is that I have the base case completed but I can't figure out how to write the recursive portion of the method. Can anyone help me understand how to go about this or show me an example? Thanks in advance.
Edit: Here is my attempt
public static void quadSort(int array[], int index, int length){
for (int i = 1; i<array.length; i++){
if(array[i] <= 1000){
for(i = 1; i<array.length;i++){ //Start point for the insertion sort
int key = array[i];
int j = i-1;
while((i>-1) && (array[j] > key)){
array [j+1] = array[j];
i--;
}
array[j+1] = key;
} //End insertion sort
}
else{
int split = (array[i])/4;
}
}
return;
}

This is a weirdly modified mergeSort, where rather than recursing all the way until you get 1-length sub-arrays, and only then starting to merge, you recurse until the sub-array length is 1/4 of the original array length, sort that with whatever sorting algo you'd prefer (quicksort?) and then merge it back up. It is not clear what it is expected if the array does not have at least 4 elements. You can adjust it to whatever you need it to do in that case.
Using pseudocode it would be something like this:
quadSort(array, l, r):
m = array.length/2 - 1
//First checking if it is the base case
// i.e. l and r define one quarter of the array
if((l == 0 AND r < m) OR //First quarter
(l > 0 AND r == m) OR //Second quarter
(l == m+1 AND r < array.length - 1) OR //Third quarter
(l > m+1 AND r == array.length - 1)) //Fourth quarter
quicksort(array, l, r) //Base case
else
//Not the base case, hence we proceed to further split the array
//and recurse on quadsort, before proceeding to merge
m = (r+l)/2
quadsort(array, l, m)
quadsort(array, m+1, r)
merge(array, l, m , r)
merge(array, l, m, r):
//Standard merge procedure from mergesort

Related

Generic QuickSort causing StackOverflowError

This is a practice problem in the Java book I'm working through. Basically, the goal is to sort an array of generic-type elements in increasing order using compareTo.
I'm trying to use QuickSort to do it. Here's my code:
public static <T extends Comparable<? super T>>
void sort (T[] arr)
{
// If arr is null, empty,
// or only has 1 element,
// it is already sorted
if (arr == null || arr.length == 0
|| arr.length == 1)
return;
// Call overloaded sort method
sort(arr, 0, arr.length - 1);
}
// HELPER METHOD FOR SORT METHOD
public static <T extends Comparable<? super T>>
void sort(T[] arr, int left, int right)
{
// To be used while sorting
int i = left;
int j = right;
// Check if left and
// right indices are out
// of order. If they are,
// nothing can be done
if (right <= left)
return;
// Middle element used
// as pivot
T pivot = arr[(left + (right - left)) / 2];
// temp will be used
// to swap elements later
T temp;
// QuickSort algorithm
while (i <= j)
{
// Look for values on
// the left greater than
// the pivot and values
// on the right smaller
// than the pivot
// When you find both, swap them
while (arr[i].compareTo(pivot) < 0)
{
i++;
}
while (arr[j].compareTo(pivot) > 0)
{
j--;
}
// Check that i hasn't
// passed j already
if (i <= j)
{
// Swap the items
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
// Move both indices
// to their next position
i++;
j--;
}
}
// Recursive calls to the
// sort method
if (left < j)
sort(arr, left, j);
if (i < right)
sort(arr, i, right);
}
The trouble is, when I test this using the following array:
String[] wordArray = {"Droplet", "Blueberry", "Elephant",
"Fate", "Apple", "Coconut", "Darjeeling"};
I get a StackOverflowError at the following line:
while (arr[i].compareTo(pivot) < 0)
And then a bunch of repeating ones at this line:
sort(arr, i, right);
The repeating errors at the line above tells me it might have something to do with infinite recursion happening, but I don't know why it would be.
I also don't know why it's throwing the error at the while loop line... it looks like the logic I used in comparing arr[i] to the pivot is okay?
The line to choose middle element for pivot should be:
T pivot = arr[left + (right - left) / 2];
the current code is effectively using T pivot = arr[right / 2], where the index right / 2 could be less than left, resulting in a pivot value that doesn't exist within the range left to right.
Consider the case where a pivot value less than all of the element values in the range left to right is used. This could cause the first loop to advance i past right or even past the end of the array, which could cause stack overflow or segmentation fault.

Algorithm to partition the array using the pivot element

I was trying to solve following programming exercise from some java programming book
Write method that partitions the array using the first element, called a pivot. After the partition, the elements in the list are rearranged so that all the elements before the pivot are less than or equal to the pivot and the elements after the pivot are greater than the pivot. The method returns the index where the pivot is located in the new list. For example, suppose the list is {5, 2, 9, 3, 6, 8}. After the partition, the list becomes {3, 2, 5, 9, 6, 8}. Implement the method in a way that takes at most array.length comparisons.
I've implemented solution, but it takes much more than array.length comparisons.
The book itself has solution, but unfortunately it's just plain wrong (not working with some inputs). I've seen the answer to this similar question, and understood "conquer" part of Quicksort algorithm, but in this algorithm values are partitioned using mid-value, but in my case using of 1st array value as a pivot is required.
This is the pivot routine from the linked answer (adapted from source here).
int split(int a[], int lo, int hi) {
// pivot element x starting at lo; better strategies exist
int x=a[lo];
// partition
int i=lo, j=hi;
while (i<=j) {
while (a[i]<x) i++;
while (a[j]>x) j--;
if (i<=j) swap(a[i++], a[j--]);
}
// return new position of pivot
return i;
}
The number of inter-element comparisons in this algorithm is either n or n+1; because in each main loop iteration, i and j move closer together by at exactly c units, where c is the number of comparisons performed in each of the inner while loops. Look at those inner loops - when they return true, i and j move closer by 1 unit. And if they return false, then, at the end of the main loop, i and j will move closer by 2 units because of the swap.
This split() is readable and short, but it also has a very bad worst-case (namely, the pivot ending at either end; follow the first link to see it worked out). This will happen if the array is already sorted either forwards or backwards, which is actually very frequent. That is why other pivot positions are better: if you choose x=a[lo+hi/2], worst-case will be less common. Even better is to do like Java, and spend some time looking for a good pivot to steer clear from the worst case. If you follow the Java link, you will see a much more sophisticated pivot routine that avoids doing extra work when there are many duplicate elements.
It seem that the algorithm (as taken from "Introduction to algorihtm 3rd ed") can be implemented as follows (C++) should be similar in Java`s generics:
template <typename T> void swap_in_place(T* arr, int a, int b)
{
T tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
template <typename T> int partition(T* arr, int l, int r)
{
T pivot = arr[r];
int i = l-1;
int j;
for(j=l; j < r; j++) {
if (arr[j] < pivot /* or cmp callback */) {
// preincrement is needed to move the element
swap_in_place<T>(arr, ++i, j);
}
}
// reposition the pivot
swap_in_place(arr, ++i, j);
return i;
}
template <typename T> void qsort(T* arr, int l, int r)
{
if ( l < r ) {
T x = partition<T>(arr, l, r);
qsort(arr, l, x-1);
qsort(arr, x+1, r);
}
}
However, its a simple pseudocode implementation, I dont know if it`s the best pivot to pick from. Maybe (l+r)/2 would be more proper.
Pretty simple solution with deque:
int [] arr = {3, 2, 5, 9, 6, 8};
Deque<Integer> q = new LinkedBlockingDeque<Integer>();
for (int t = 0; t < arr.length; t++) {
if (t == 0) {
q.add(arr[t]);
continue;
}
if (arr[t] <= arr[0])
q.addFirst(arr[t]);
else
q.addLast(arr[t]);
}
for (int t:q) {
System.out.println(t);
}
Output is:
2
3
5 <-- pivot
9
6
8
There is video that I made on Pivot based partition I explained both the methods of patitioning.
https://www.youtube.com/watch?v=356Bffvh1dA
And based on your(the other) approach
https://www.youtube.com/watch?v=Hs29iYlY6Q4
And for the code. This is a code I wrote for pivot being the first element and it takes O(n) Comparisons.
void quicksort(int a[],int l,int n)
{
int j,temp;
if(l+1 < n)
{
int p=l;
j=l+1;
for(int i=l+1;i<n;++i)
{
if(a[i]<a[p])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
j++;
}
}
temp=a[j-1];
a[j-1]=a[p];
a[p]=temp;
quicksort(a,l,j);
quicksort(a,j,n);
}
}
The partition function below works as follow:
The last variable points to the last element in the array that has not been compared to the pivot element and can be swapped.
If the element directly next to the pivot element is less than the pivot
element. They are swapped.
Else if the pivot element is less than the next element, the nextelement is swapped with the element whose index is the last variable.
static int partition(int[] a){
int pivot = a[0];
int temp, index = 0;
int last = a.length -1;
for(int i = 1; i < a.length; i++){
//If pivot > current element, swap elements
if( a[i] <= pivot){
temp = a[i];
a[i] = pivot;
a[i-1] = temp;
index = i;
}
//If pivot < current elmt, swap current elmt and last > index of pivot
else if( a[i] > pivot && last > i){
temp = a[i];
a[i] = a[last];
a[last] = temp;
last -= 1;
i--;
}
else
break;
}
return index;
}

Split an array into 4 portions

I have to create a method that divides the array of integers taken as input into an array that is made in this way:
The first portion is composed of the elements which, divided by 4, generate rest 0
The second portion is composed of the elements which, divided by 4, give rest 1
The third portion is composed of the elements which, divided by 4, give remainder 2
The fourth portion is composed of the elements, divided by 4, give rest 3
For example, the following array:
[0,2,4,5,6,8,7,9,10,12,14,15,17,20,1]
must become this here:
[0,4,8,12,20,5,9,17,1,2,6,10,14,7,15]
The result I get is:
[0,4,5,8,6,2,7,9,10,12,14,15,17,20,1]
Within subsequences no matter the order of items, just be in the subsequence correct.
I wrote this method but doen't work properly, some items are out of place.
public static void separate4Colors(int[] a) {
int i = 0;
int j = 0;
int k = 0;
int h = a.length - 1;
while(k <= h) {
if(a[k] % 4 == 0) {
swap(a, k, i);
k++;
i++;
j++;
}
else if(a[k] % 4 == 1) {
swap(a, k, i);
k++;
i++;
}
else if(a[k] % 4 == 2) {
k++;
}
else {
while(h > k && a[k] % 4 == 3)
h--;
swap(a, k, h);
h--;
}
}
}
private static void swap(int[] a, int x, int y) {
int temp = a[x];
a[x] = a[y];
a[y] = temp;
}
Can someone help me fix it?
A similar exercise that I've done and that work is to divide the array into 3 portions instead that 4:
public static void separate3Colors(int[] a) {
int j = 0;
int k = a.length - 1;
int i = 0;
while(j <= k) {
if(a[j] % 3 == 0) {
swap(a, j, i);
j++;
i++;
}
else if(a[j] % 3 == 1) {
j++;
}
else {
swap(a, j, k);
k--;
}
}
}
You can do it in a single line by sorting the array using a comparator that compares numbers modulo 4. Unfortunately, it requires an array of Integer objects.
Another approach would be writing the results into a different array. You can walk the array once to determine indexes at which the values with each remainder will start, and then walk the array again to make an ordered copy:
int[] index = new int[4];
for(int n : a) {
int r = n % 4;
if (r != 3) {
index[r+1]++;
}
}
index[2] += index[1];
index[3] += index[2];
// At this point each index[k] has the position where elements
// with remainder of k will start
int[] res = new int[a.length];
for(int n : a) {
res[index[n%4]++] = n;
}
This places the re-ordered array into the res variable.
Demo.
This exercice is obviously a variant of the famous Dutch National Flag Problem by the late E. Dijkstra https://en.wikipedia.org/wiki/Dutch_national_flag_problem
and the same resolution technique can be applied (with success, of course).
Consider that, at some point while running your algorithm, the arrays has five parts. One part contains numbers you know have with remainder 0, one part for remainders 1, etc. And one part for unclassified numbers. Each step of the algorithm consists of
looking at the first unclassified number
swapping some numbers at the borders so the number falls in the right part.
So the "unsorted" zone shrinks by one unit.
Sometimes a picture helps (go figure). I chose the have the unclassified elements between the 1's and the 2's.
000000000111111uuuuuuu22222333333333
?
If remainder of the number under test is 1, just leave it there. If it is zero, swap it with the first "1". etc.
At the beginning, of course, the situation is depicted as
uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
?

Are the following two recursive sorting methods the same?

I've been working on selection sort and bubble sort using recursion. I've finally come up with two methods, and they worked perfectly fine. But as I took a final look at those, they look like just one one method which is selectionSortRecursive. Could you tell me the difference (or are they the same)?
public static void selectionSortRecursive(Comparable[] list, int n)
{
Comparable temp;
if ( n > 1 ){
for ( int i = 0; i < n - 1; i++ )
{
if(list[i].compareTo(list[i + 1]) > 0){
temp = list[i];
list[i] = list[i + 1];
list[i + 1] = temp;
}
}
selectionSortRecursive(list, n - 1);
}
}
public static void bubbleSortRecursive( Comparable[] list, int n)
{
Comparable tmp;
if (n >1) {
for (int i = 0; i < n - 1; i++)
{
if(list[i+1].compareTo(list[i]) < 0)
{
tmp = list[i];
list[i] = list[i+1];
list[i+1] = tmp;
}
}
bubbleSortRecursive( list, n - 1);
}
}
The only line which is different is
if(list[i].compareTo(list[i + 1]) > 0){
and
if(list[i+1].compareTo(list[i]) < 0)
and provided compareTo is implemented correctly this will do the same thing.
BTW the if(n > 1) check is redundant. And I would move tmp to the most inner scope you can.
Both sorts are bubble sorts. A bubble sort "bubbles" values to the top/right position.
A selection sort selects the lowest/highest value repeatedly, swap in the selected with the position it needs to place it. i.e. the swap would be outside the loop to find the lowest/highest.
It's both bubble sort, one bubbles the element from the top to the bottom, the other one does it the way round. Selection sort is different: it searches the smallest elements of all remaining (unsorted) elements and places it in the next slot, it does not change any other elements. Bubble sort, instead, always compares tow elements and swaps them is the first one is bigger (or smaller) than the second one - which is what you are doing.

How to keep parameters unchanged in recursion?

I am trying to implement Quick Sort algorithm, here's my code:
public class Sort {
int count = 0;
public void Partition(int A[], int l, int h) {
if ((h-l) > 1) {
count += (h-l) - 1;
int pivot = A[l];
int i = l+1;
int temp;
int j;
for (j = l + 1; j < h; j++) {
if (A[j] < pivot) { // SWAP
temp = A[i];
A[i] = A[j];
A[j] = temp;
i++;
}
// else : j++
}
temp = A[i-1];
A[i-1] = A[l];
A[l] = temp;
Partition(A, l, i-1);
Partition(A, i, A.length);
}
}
}
the code does sort the input array, but when i count the number of comparisons, it gives a number, much greater than the original number of the comparisons. I added a break point and moved a step by step into the code, and found that the problem lies in the last two lines:
Partition(A, l, i-1);
Partition(A,i, A,length);
The 'i' sent in the 2nd call, is the i resulting from the 1st call to the function Partition, and not the 'i' from the very first code. for example, the first time the code runs on the following input:
3 8 4 6 10 2 5 7 1
the output is: 1 2 3 4 6 10 9 5 7 8
and i = 3.
then the first call to Partition takes i (where i equals 3) and keeps changing the i's value, when it's finally done, the i's value is different than 3, and another wrong value is sent to the 2nd recursive call.
My question is, is there a way, so that the two calls take the same parameter i, without anyone changing it ?
Try this. Don't try to do the sort in Partition; from there, just return the index i (you have to change the return type to int, of course). Then write a different method to implement your quicksort.
public static void quicksort(int[] n, int left, int right){
if (left<right){
int pivotindex=Partition(n, left, right);
quicksort(n, left, pivotindex-1);
quicksort(n, pivotindex+1, right);}
}
I tested this with the following, and it worked perfectly.
public static void main(String[] args){
int[] n= new int[8];
n[0]=3;
n[6]=2;
n[1]=5;
n[3]=20;
quicksort(n, 0, n.length);
for (int i=0; i<n.length; i++){
System.out.print(n[i]+",");
}
}
Since Java uses pass-by-value, there is no way the first call can change the value of the parameter i, since the called method has no reference to it.
You are probably expecting the second call to Partition to be the next one. However, the first call will in turn call Partition twice more, causing the parameter values of the next execution of Partition to be different than you might expect.
Also, please start your method names with a lower-case letter.

Categories

Resources