How to keep parameters unchanged in recursion? - java

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.

Related

Recursive Sorting: quick sort syntax

The programs should sort a set of a number using a quick sort. Use Data Sets Starting at 10,000,000 randomly generated numbers. I've been learning about quicksort. Another, I finished merge sort. However, the quicksort is syntax. I don't know why it's syntax.
public static void main(String[] args)throws Exception{
for(int n = 1; n<=100; n++)
{// begin for
int size = 10000000; //change this num to change the size of the random array 10,000,000
int[] a = new int[size];
int[] temp = new int[a.length]; //temporary array, it's empty.
//fill the array with random integers
for(int i = 0; i< a.length; i++)
a[i] = (int)(Math.random()*100000 + 1);
long startTime = System.nanoTime();
quickSort(a, temp, 0,(a.length - 1));
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.printf("%12.8f %n", (double)duration/100000000);
}// End for
}// end main
public static void quickSort(int[] a, int[] temp, int startIndex, int endIndex)
{// begin quickSort
int pivotIndex; //the index of pivot returned by the quciksort partition
if(startIndex < endIndex)
{//Begin if
pivotIndex = partition(a, temp, startIndex, endIndex);
quickSort(a, temp, startIndex, pivotIndex -1);
quickSort(a, temp, pivotIndex+1, endIndex);
}//End if
}//end quickSort
public static int partition(int[] a, int[] temp, int startIndex, int endIndex) {
int pivotIndex;
int pivot;
int midIndex = startIndex;
pivotIndex = (startIndex + endIndex) / 2;
pivot = a[pivotIndex];
swap(a, temp, pivotIndex, endIndex);
for (int i = startIndex; i < endIndex; i++) {
if (a[i] < pivot) ;
{
swap(a, temp, i, midIndex);
midIndex = midIndex + 1;
}
}
swap(a, temp, midIndex, endIndex);
return midIndex;
}// end partition
public static void swap(int[]a, int[]temp, int first, int second)
{//Begin swap
for(int i = first; i <= second; i++){
temp[i] = a[i];
}
int c;
c = a[first];
a[first] = a[second];
a[second] = c;
}//End swap
public static void writeLines(int[] a, String fileName) throws Exception
{ // Begin writeLines(int[] a, String fileName)
// create a File class object with the given file name
java.io.File out = new java.io.File(fileName);
// Create a PrintWriter output stream and link it to the File object
java.io.PrintWriter outfile = new java.io.PrintWriter(out);
// write the elements of an int array, separated by spaces
for (int i = 0; i < a.length; i++)
{ // Begin for (int i = 0; i < a.length; i++)
outfile.print(a[i] + " ");
} // End for (int i = 0; i < a.length; i++)
// print a newline at the end of the list of integers
outfile.println();
outfile.close();
} // End writeLines(int[] a, String fileName) throws Exception
}//end quickSORT
I got syntax. It said quick.partition and quick.quickSort.
In simple terms, Hoare's quicksort works by:
having an array of data
picking some middle element
logically dividing the array into 3 categories:
everything to the left of the middle element - we'll call this the lower section
the middle element
everything to the right of the middle element - we'll call this the upper section
remembering the value at the middle element
starting from the very left of the lower section looking for a value that is greater than the middle element, or in other words it tries to find something that shouldn't be in the lower section because it is too big
when it finds one it starts from the right hand side of the upper section looking for an element that is "too small for the upper section"
if values are found that meet these criteria, they're swapped over
this happens repeatedly until everything that is too big for the lower section has been transferred into the upper section and everything too small for the upper section has been transferred into the lower section
the result is a "partly sorted" array; everything to the left of the middle element is smaller than it and everything to the right of the middle element is larger than it
the algorithm starts over, this time treating just the lower section as if it was the entire array, then it does the same with the upper section
by taking the whole, then halving it, then quartering it, then... you reach a point where you're just making sure that a bunch of pairs or single elements are sorted, and then that's all done
Your algorithm uses something like a mix of the Hoare and Lomuto partitioning strategies but mostly more like Lomuto. There are various strategies for partitioning (choosing how to divide the array into sections) depending on things like how the data container works (Hoare's strategy requires containers that can be randomly accessed or accessed from each end, like a double linked list, Lomuto only needs a data container to be readable in one direction) but quicksort's basic algorithm is this notion that you split everything into two piles of "want smaller values" and "want larger values", swap things repeatedly between the piles until "smaller" and "larger" really is true, then do the process again first to the smaller pile, then the larger pile. If you keep going and dividing the work then you reach a point where everything is sorted.
If you're still struggling with it, maybe consider instead: If you think of it like sorting a large amount of words that only use the characters A and Z, into alphabetical order you might have a strategy of putting everything into piles: 2 piles of those that start with A and those that start with Z. Then you make 4 piles, those that start AA, AZ, ZA and ZZ. Then you move to 8 piles of three letters AAA, AAZ, AZA, AZZ, ZAA, ZAZ, ZZA and ZZZ (which is probably appropriate if you're asleep by now 🤪). This is also a "divide and conquer strategy"; you'll reach a point where you've divided the piles so much that they each only contain one thing, then if you gather piles up in order (and if you've been careful when you laid them out they will already be in order) then they are sorted
Oh, and your code has an error in that you've added a semicolon after an if, which creates an empty statement (makes the if useless), and you also don't seem to use the temp array you carry around. It's like this algo was hoping to be a blend of two different sort strategies but is unfinished

Mergesort & recursion confusion/code not working

I've been at this for a couple days, reading many pseudocode and watching videos to explain recursion and mergesort. I understand mergesort and somewhat understand recursion -- except for when it applies to arrays as is in my code below. I did some debugging and it appears that my program is not sorting correctly regardless of the out of bounds error. I am very lost and would appreciate any help you can offer!
Questions:
1) what does it mean for a recursion on an array? Does it create a sub array that is held by the original array? -- if that makes sense.
2) why is my code running into a out of bounds error even though I followed a tutorial to the t and also set the k value after every pass. Specifically the issue is being encountered.
Here's the code:
public class Merge {
public static void main(String[] args) {
}
static void mergeSort(int arr[]){
int r = arr.length - 1;
Merge.sort(arr,0,r);
System.out.println(arr);
}
static void sort(int arr[], int p, int r){
if(p<r){
int q = (p+r)/2;
sort(arr,p,q);
sort(arr,q+1,r);
merge(arr,p,q,r);
}
}
static void merge(int arr[], int p, int q, int r){
int n1 = q-p+1;
int n2 = r-q;
int L[] = new int[n1];
int R[] = new int[n2];
for(int i = 0; i< n1; i++){
L[i] = arr[i];
}
for(int j = 0; j< n2; j++){
R[j] = arr[q+1+j];
}
int i = 0, j = 0;
int k = 1;
while(i<n1 && j<n2){
if(L[i]<= R[j]){
arr[k] = L[i];
i++;
}
else{
arr[k] = R[j];
j++;
}
k++;
}
while(i<n1){
arr[k] = L[i];
i++;
k++;
}
Error occurs here --> while(j<n2){
arr[k] = R[j];
k++;
}
}
}
Thank you for the help!
edit: Just wanted to say how greatful I am for the amazing replies on this post, thank you so much for your time.
To be honest I don't think your sentence 'recursion on an array' makes any sense.
Your code has one array arr which gets sorted. Your merge method is supposed to be sorting parts of this array, but every time it is called it has the same whole array object. There are no sub-arrays; it's just up to this method to sort the relevant part of this one array. If this method isn't doing what it's supposed to do, then problems will occur.
Let's take a closer look at the loop where you are getting an error:
while(j<n2){
arr[k] = R[j];
k++;
}
Suppose we get to this loop with j < n2. What happens?
We enter the loop because j < n2, so we copy R[j] to arr[k] and then increment k. We go back to the top of the loop, we find j is still less than n2 because neither variable has changed, so we copy R[j] to arr[k] and increment k again. We got back to the top of the loop, find j is still less than n2 and go round again. And so on, and so on, until eventually k falls off the end of arr and we get an ArrayIndexOutOfBoundsException.
In this part of mergesort we are trying to copy into arr the contents of R that haven't already been merged into arr, but we forgot to increment j. So, to fix this loop, increment j as well as k:
while(j<n2){
arr[k] = R[j];
j++;
k++;
}
Note that the previous loop, the one beginning with while(i<n1), increments i and k. This change now makes the two loops look more similar to one another.
So, we run our code again, and what happens? We still get an ArrayIndexOutOfBoundsException. Clearly we haven't solved the problem yet, but have we made any progress at all if we're just getting the same error?
The intention of the merge method is to merge the subarrays of arr from positions p to q inclusive and from positions q+1 to r inclusive. If the two subarrays are sorted, then after merging the whole subarray of arr from p to r will be sorted.
However, when we write the values back into arr, we start at index 1. Is this correct? Suppose arr has length 2, p = 0, q = 0 and r = 1. We have two elements to sort. Where does the first one get written to, and where does the second?
The answer is the first one gets written to arr[1], and your code throws an exception because it attempts to write the second to arr[2], which does not exist.
You want k to start from the start of the subarray you are sorting. You want k to start from p.
So replace the line
int k = 1;
with
int k = p;
We try again, and now we find the code no longer throws an exception but prints something unintelligible like [I#65fb1cd. Annoyingly, this is how Java prints arrays by default. To fix this, add the line import java.util.Arrays; to your file and replace the line
System.out.println(arr);
with
System.out.println(Arrays.toString(arr));
Your code should now print out a list of numbers when it runs.
However, we now see that our code isn't sorting the array correctly. I asked it to sort the values 8, 1, 4, 9 and it came back with 1, 1, 8, 9. The 1 has been duplicated and the 4 has disappeared.
Recall once again that the intention of the merge method is to sort arr from p to r onwards. Take a careful look at what values are being copied from the array into L and R:
for(int i = 0; i< n1; i++){
L[i] = arr[i];
}
for(int j = 0; j< n2; j++){
R[j] = arr[q+1+j];
}
Notice any difference between these two loops, apart from the fact that one uses j instead of i, n2 instead of n1 and R instead of L?
Note that when you copy into R, you are copying values from position q+1 onwards. These are the values in the second sorted subarray. But when you are copying into L, you are copying values from position 0 onwards. This isn't necessarily where the first sorted subarray begins. That of course starts from p.
Replace the first of these loops with:
for(int i = 0; i< n1; i++){
L[i] = arr[p+i];
}
Finally, we run the code and find that we now have a working mergesort program.
Let's break your question down a bit - specifically, what does recursion mean? You can think of it like a loop - it performs an operation on itself until it reaches a stop condition. Take for example, a for loop
for(int i = 0; i < 2; i++)
will perform the operation until it reaches the case where variable i is no longer less than 2. Likewise, recursively
void methodLoop(int input){
int i = input;
if(i < 2){
methodLoop(i+1);
}
else{
System.out.println("Base case reached! I is no longer less than 2!");
}
}
Performs a similar operation, just with recursion instead!
What does this mean for arrays? It depends. What you've touched upon in your question is a concept called multidimentional arrays - arrays within arrays. These work like normal arrays, it's just an array that contains another array in each one of its indexes - these are instantiated as follows
String[][] multidimensionalarray = new array[4][4]
To visualize such a concept, it might be easier to think of it as a coordinate grid, with the indexes being the coordinate places and the value at that index containing information about that place. For example, assuming the multidimensional array has been filled with data like so, it might look like:
4 a b c d
3 e f g h
2 i j k l
1 m n o p
1 2 3 4
and then the value of multidimensionarray[2][3] would return the string k!

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;
}

Need help understanding recursion with array sorts

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

(Java) Heapsort - Implementation not sorting half elements?

I've wrote two different implementations of heapsort today and both have given me the same results:
Object i: 18
Object i: 11
Object i: 10
Object i: 9
Object i: 8
Object i: 3
Object i: 7
Object i: 1
Object i: 4
Now I've checked my code with this page here; and believe one of my implementations is exactly the same as the pseudo-code would suggest, whilst the other is very similar to one of the Java implementations.
I'd like to stress the fact that I've actually wrote two different versions, and checked them against implementations I can find! So I am really stumped at the moment! I've stepped through it with a debugger a few times - but have probably must've something along the way? I even made a debugging function which just looped through the list and outputted the contents using System.out.println() - but this still didn't help a great deal!
The algorithm is working on a List - and I have done nothing to optimise it at this stage; it merely is a bit of an experiment at the moment. I have working implementations of QuickSort, BubbleSort & Insertion Sort - but this one has left me stumped!
My first implementation is below:
public static List<Integer> execSort(List<Integer> s) {
int n = (s.size()-1);
Integer t;
for(int i = n/2; i>0; i--){
s = downheap(s, i, n);
}
while(n >= 1){
t= s.remove(0);
s.add(0, s.remove(n-1));
s.add(n-1, t);
n--;
s = downheap(s, 1, n);
}
return s;
}
public static List<Integer> downheap(List<Integer> s, int i, int n){
Integer t = s.get(i-1);
int j;
while( i <= n/2 ){
j = i*2;
if( (j<n) && (s.get(j-1) < s.get(j)))
j++;
if( t >= s.get(j-1)){
break;
} else {
/* Swap them, without using a third variable
- although with all the get()/set() methods
it would be better to have a third one, doh! */
s.set(i-1, (s.get(i-1) + s.get(j-1)));
s.set(j-1, (s.get(i-1) - s.get(j-1)));
s.set(i-1, (s.get(i-1) - s.get(j-1)));
i=j;
}
}
s.set(i-1, t);
return s;
}
You can also see them on Github as Gists:
- Implementation 1
- Implementation 2
Any ideas as to why some of the elements don't want to sort?! I'm aware that this implementation is going to be sub-optimal, that working on a List<> isn't going to be the best data structure and I should probably look at using primitive data-types as opposed to (ab)using auto-boxing... but that's for another post! I just want a working version before I try and improve it ;)
In the gist (you accidentally linked both to the same), you have a few typos
public static List<Integer> execSort(List<Integer> s) {
int start = (s.size()/2)-1;
int end = s.size()-1;
while( start >= 0){
s = sift(s, start, end);
sift takes the count as last argument, not the last index, so the argument ought to be s.size() (or end+1) instead of end.
public static List<Integer> sift(List<Integer> s, int start, int count){
int root = start;
while( ((root*2)+1) < 2 ){
That must be while(root*2+1 < count), and not < 2.
In the code you have here, you have in part the same problem (caused by an odd indexing strategy, I suspect):
for(int i = n/2; i>0; i--){
s = downheap(s, i, n);
Since you always get(i-1) resp. j-1 in downheap, you need an upper bound of s.size() or n+1 while building the initial heap.
}
while(n >= 1){
This loop should only run while n > 1, or you'll swap the smallest element out of place.
t= s.remove(0);
s.add(0, s.remove(n-1));
s.add(n-1, t);
The old root must go in the last place, that's place n, not n-1, s.add(n,t).
n--;
s = downheap(s, 1, n);
}
In downheap, the final
s.set(i-1, t);
is superfluous, you always swap t, so when that line is reached, the element at i-1 already is t.

Categories

Resources