trouble-shooting my quicksort algorithm in java? - java

while trouble-shooting my method for quicksort algorithm, this problem popped up;
consider this simple example of printing an array elements individually:
public static void print1(int[] arr, int start, int end){
for (int k = start; k<end;k++){
System.out.println(arr[k]);
}
}
public static void print2(int[] arr, int start, int end){
for (int k=start; k<=end;k++){
System.out.println(arr[k]);
}
}
In the main, if I call:
public static void main(String[] args) {
int[] data2 = new int[]{10,11,9,7,5};
print1(data2,0,data2.length);
System.out.println("___________");
print2(data2,0,data2.length-1);
}
both prints the same content, which is well and good;
(Note, I pass data2.length and data2.length-1 as arguments to my print1 and print2 methods and the for-loop in each method changes accordingly)
Now comes the problem with quicksort regime:
consider this code:
public static void quickSort2(int[] arr, int i, int j){
if (i<j){
int part =partition(arr, i, j);
quickSort2(arr, i, part-1);
quickSort2(arr, part+1, j);
}
}
public static int partition(int[] arr, int start, int end){
int pivot = arr[start];
int i = start;
for (int j = start+1;j<=end;j++){ // ******
if (arr[j]<pivot){
i++;
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
arr[start]=arr[i];
arr[i]=pivot;
return i;
}
In the main;
public static void main(String[] args) {
//int[] data = new int[]{5,10,1,9,4,4,8,3,6,2,7,4};
int[] data = new int[]{3,4,1,2};
int[] data2 = new int[]{10,11,9,7,5};
System.out.print("Unsorted array data ");
display(data);
System.out.print("\nUnsorted array data2 :");
display(data2);
quickSort2(data, 0, data.length-1);
System.out.println("_____________");
quickSort2(data2,0,data2.length-1);
System.out.print("\nSorted array data: ");
display(data);
System.out.print("\nSorted array data2:");
display(data2);
}
works fine and good;
now If I change the call to quickSort(data, 0, data.length) and change the for-loop line (marked in the code with *******) to for (int j = start+1;j<end;j++), I get entirely different result:
i.e: first two elements are not sorted, but rest rest of it is sorted.
I get
Sorted array data: [2, 1, 3, 4]
Sorted array data2:[5, 9, 7, 10, 11]
I need some help

While calling the first partition in your quicksort function you will also have to change:
quickSort2(arr, i, part-1);
to
quickSort2(arr, i, part);
This is because you are not using the last index (end). Hence your quicksort will be incomplete since you are not placing the last element before the pivot even if it has a lower value than pivot.

After a quick look, I think there are two separate bugs - I discovered one for each of your quickSort2() calls in 'main`. Each was triggered because of the nature of the array you were trying to sort.
Bug 1: at line 9 in your main, you call:
quickSort(data, 0, data.length);
So, this is when you try to quickSort() {3,4,1,2}. The error occurs on the first recursive call to quickSort2(), the exact call with its arguments being
quickSort2({2,1,3,4}, 0, 1);
which then calls partition() with the same parameters
partition({2,1,3,4}, 0, 1);
This is where the error occurs. Look at the terms of your for-loop:
for (int j = start+1; j< end;j++)
j will immediately be set to 1. End already equals 1. 1 < 1 is false, so the loop never triggers.
Solution: just get rid of the +1 when assigning j a value. Your loop will look like this:
for (int j = start; j< end;j++)
The initial case where i == j is will never trigger the if-statement so I believe it should be fine.
Bug 2: at line 11 in main, you call:
quickSort2(data2,0,data2.length-1);
The eventual error occurs at the same place as it did in the first bug - in partition() during the first recursive call to quickSort2(), which ends up having these parameters:
partition({5,9,7,10,11}, 0, 2);
The if-statement in your for-loop is never triggered, because 5 is the pivot and 9 and 7 are both larger. Thus i returns as zero, and this half of the array is never sorted again.
As for a quick solution to this, I can't say there is one I can readily think of. You'd have to alter the logic of your sort.
Some notes/pointers that may help you out.
Quicksort algorithms, in general, aren't typically called all the way down the array. There is usually a base case in which sorting switches over to a different algorithm. Example, you have only 5 objects left to sort on this call. That's not that many, so let's switch to something simpler, such as insertion sort. Even though insertion sort is generally less efficient on large collections, when you get down to only having 5 or fewer objects to rearrange, it's not such a big deal.
Quicksort implementations also typically don't call the very first object in the collection the pivot. This is a bad method in general, as what if your list was already sorted, or reverse sorted? The time complexity would be pretty mediocre ( O(N2)). Instead, there is a method called "Median-Of-Three", which not only just might solve the second bug you have, but give you a better time-complexity.

quickSort(data, 0, data.length-1);
//...
quickSort2(data2,0,data2.length-1);
I see the two quickSort is not the same, maybe you have a another function named quickSort you forget?Or just wrong written?

had a look at the code added some prints and found out, that after your modification is in the second run your pivot element 0, what is obviously wrong. I added some numbers to be the
unsorted array data this:
3 4 1 2 7 5 6
3 4 1 2 7 5 6
pivot: 2
2 1 3 4 7 5 6
pivot: 0
2 1 3 4 7 5 6
2 1 3 4 7 5 6
2 1 3 4 7 5 6
pivot: 3
2 1 3 4 7 5 6
2 1 3 4 7 5 6
pivot: 6
2 1 3 4 6 5 7
pivot: 4
2 1 3 4 6 5 7
2 1 3 4 6 5 7
2 1 3 4 6 5 7
Sorted array data:
2 1 3 4 6 5 7
package quicksort;
public class Quicksort {
public static void main(String[] args) {
int[] data = new int[]{3,4,1,2,7,5,6};
System.out.print("Unsorted array data:\n");
display(data);
quickSort2(data,0,data.length);
System.out.print("\nSorted array data:\n");
display(data);
}
public static void quickSort2(int[] arr, int i, int j){
display(arr);
if (i<j){
int part =partition(arr, i, j);
quickSort2(arr, i, part-1);
quickSort2(arr, part+1, j);
}
}
public static int partition(int[] arr, int start, int end){
int pivot = arr[start];
//System.out.println(pivot);
int i = start;
int temp;
for (int j = start;j<end;j++){ // ******
if (arr[j]<pivot){
i++;
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
arr[start]=arr[i];
arr[i]=pivot;
System.out.println("pivot: "+i);
return i;
}
public static void display(int[] data){
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");
}
System.out.println("\n");
}}

Related

How to compare two consective numbers of an array in java

I am trying to compare two consecutive numbers of an array and print the largest one in every iteration. I have written below code but its giving unexpected responses. Can somebody please check and help me understand the mistake.
PS: I just started learning java.
public class Test {
public static void main(String [] args){
int[] list = {6,0,4,2,9};
int current;
for(int j=0; j<list.length-1; j++){
if(list[j]>list[j+1]){
current = j;
}else{
current = j+1;
}
System.out.print(current + " ");
}
}
}
Expected response: 6 4 4 9
Actual response: 0 2 2 4
Ok so your code is just returning j which here is used as the number in youy for loop.
In your if you're using j as an index to get the value in list matching that index; but in the rest of your for loop you're setting current to the value of j and not the value of list at the index j.
What you'll want to print is the value of list[j] and not j.
In fact, if you look closely [0 2 2 4] are the indexes of [6 4 4 9]

How to reverse slice of array java

I've been attempting to reverse a slice of a list in java.
The equivalent in python (though perhaps not the best way - I don't care about this, just want to get my point across) would be:
myList = [0,1,2,3,4,5,6,7,8,9,10]
reverseSlice = myList[2:6]
reverseSlice.reverse()
myList[2:6] = reverseSlice
When I tried to manually implement this in Java, I tried this:
public static int[] reverse(int[] x,int a1, int a2) {
a1--;
int[] rArr = new int[a2-a1+1];
for (int ind = a1; ind<a2; ind++) {
rArr[a2-ind-1] = x[ind];
}
for (int ind = a1; ind<a2; ind++) {
x[ind] = rArr[ind];
}
return x;
}
However, when I run this:
int[] cows1 = new int[]{0,1,2,3,4,5,6,7,8,9,10};
cows1 = reverse(cows1,2,6);
for (int i : cows1) {
System.out.print(i + " ");
}
I get 0 4 3 2 1 0 6 7 8 9 10 .
I'm really confused how I got this, as in my function, I don't introduce new values, so the "0" shouldn't have appeared.
My Question: Why is my code returning values that are not in the list?
The 0 value which is coming might be because of accessing an uninitialized value from rArr array. For the purpose of reversing, an extra array is not required. You can simply swap the values from the starting index to the end index. I have tested the following code and it gives correct output
public class Solution2 {
public static void main(String args[])
{
int[] cows1 = new int[]{0,1,2,3,4,5,6,7,8,9,10};
cows1 = reverse(cows1,2,6);
// both indices are inclusive
for (int i : cows1) {
System.out.print(i + " ");
}
}
public static int[] reverse(int[] x,int a1, int a2) {
for (int i = 0; i < (a2-a1+1)/2; i++) {
swap(x,a1+i,a2-i);
}
return x;
}
private static void swap(int[] x, int i, int j) {
// System.out.println("values swappeed are "+ x[i] + " " + x[j]);
int temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
and the output comes as
0 1 6 5 4 3 2 7 8 9 10
The problem seems to be the first line of your reverse function: a1--; not preserving the original value for your start index.
The method call
The purpose of the reverse function is to reverse only the portion of the array bound by variable values a1 and a2. On a side note, never use meaningless names like that. Better names for these variables could've been startIndex and endIndex or similar naming (a1 and a2 don't really mean anything).
For values (2, 6), the reverse function should extract the portion of the array starting at index 2 and ending on index 6. I am assuming the end index is not inclusive, so it should only grab values at index 2, 3, 4, and 5. However, the first thing the method does is decrement the start index, so it actually starts at index 1.
From there, the function successfully reverse the values located at these indices [4,3,2,1]. The question is now, what happens to the value at index 5? Let's see what this part of the code does new int[a2-a1+1]. The value of a2 is 6 and the new value of a1 is 1 because it was decremented in the first line. That means that your new array is of size [6-1+1] which is 6. That seems to be incorrect. Only 4 values are required to be reversed and the array is one element too big. The last index of this array is defaulted to integer value of 0.
The swap
First loop:
for (int ind = a1; ind<a2; ind++) { // starts with a1 = 1 and a2 = 6
rArr[a2-ind-1] = x[ind]; // rArr[4] = x[1]: first iteration is off by 1
}
Second loop:
for (int ind = a1; ind<a2; ind++) { // a1 = 1, a2 = 6 (starts at 1 and ends at 5 (rArr doesn't have an index 5)
x[ind] = rArr[ind]; // first iteration: x[1] = rArr[1], last iteration x[5] = rArr[5] -> this last index is accessible because array was too big and was initialized to a primitive `int` default value (0).
}
The solution
Make sure your start and end indices are preserved.
Allocate the temp array (if needed) using the original start and end indices for your calculation.
Inside the reverse() method, your first line should've been:
int[] rArr = new int[a2-a1]; // allocates an array of size 4 (6-2)
From here, if the rest of the method is wrong, you will not see a zero anywhere in the reversed array. At worst, you would've seen an offset problem if your start index was erroneously calculated. And that, I think, would've been easy to spot and fix.
If you are new at programming, and even if you are more of an "expert", you should always work out these problems on paper (or on a board) before you attempt to code and walk through your logic. You will be amazed as to how many logic errors you will catch before you code.

Dynamic Programming-RodCutting

I've recently started learning algorithms.
I tried to implement the classic Rod cutting problem using a Dynamic programming approach. I'm unable to get the correct output. Here is my code:
public class RodCuttingProblem {
public static void main(String[] args) {
int len=5;
int prices[]={2,5,7,3};
rodCuttingImplementation(prices,len);
}
public static void rodCuttingImplementation(int prices[],int len){
prices=reAdjustPrice(prices);
System.out.println("");
for(int i=0;i<prices.length;i++){
System.out.print(prices[i]+" ");
}
System.out.println(" ");
int dp[][]=new int[prices.length+1][len+1];
for(int i=1;i<prices.length;++i){
for(int j=1;j<=len;++j){
if(i<=j){
dp[i][j]=Math.max(dp[i-1][j], prices[i]+dp[i][j-1]);
}
else{
dp[i][j]=dp[i-1][j];
}
System.out.print(dp[i][j]+" ");
}
System.out.println();
}
System.out.println("Optimal Profit : "+dp[prices.length-1][len]);
}
static int[] reAdjustPrice(int prices[]){
int[] newPrices=new int[prices.length+1];
newPrices[0]=0;
for(int i=0;i<prices.length;i++){
newPrices[i+1]=prices[i];
}
return newPrices;
}
}
Output:
2 4 6 8 10
2 7 12 17 22
2 7 14 21 28
2 7 14 21 28
Optimal Profit : 28
As per my understanding, the output should be 12.
I really tried to understand what your algorithm is supposed to do, but I cannot, thus I cannot find out what the problem is
some concerns:
reAdjustPrice is doing very weird shift, I suppose you want to indicate that length is always >= 1 and thus index in array cannot be 0, it could work, though I suggest you to think in terms of 0-based arrays
you have array with 4 prices, but want to split rod with length 5. Usually this is done by appending 0 at the end of array, ie initial price for length 5 is 0
array dp - it has 2 dimensions, but maximal price for every length is 1 value, so you need 1 value for each step, this is for sure one dimensional array
Here is "canonical" implementation of rod splitting algorithm:
static int maxPrice(final int len, final int[] prices) {
// if len > prices.length, adjust it to have zeroes at the end
for (int i = 0; i < len; i++)
for (int j = 0; j < ((i + 1) >>> 1); j++)
prices[i] = Math.max(prices[i], prices[j] + prices[i - j - 1]);
return prices[len - 1];
}
note: this variant modifies input array, to decrease memory usage, but its very easy to modify it

Java sorting error, printing out an array of 0s rather than the values of the array

So I'm making a simple sort program but the sort methods return 0s rather than the numbers of the array being sorted. Perhaps someone could shed a bit of light as to why this is happening. The numbers themselves are taken from a .txt file and then sorted in numerical order.
import java.io.File;
import java.util.Scanner;
public class quickSort{
public static void main(String[] args) throws Exception {
File infile = new File("input.txt");
Scanner input = new Scanner(infile);
int[] data = new int[100];
int x, count;
count = 0;
while (input.hasNext()){
x = input.nextInt();
data[count++] = x;
}
System.out.println("Array before Sort");
for (int i = 0; i < count; i++){
System.out.printf(" %d", data[i]);
if ((i+1)%7==0) System.out.println();
}
quicksort(data, count);
System.out.println("\n\nArray after quickSort");
for (int i = 0; i < count; i++){
System.out.printf(" %d", data[i]);
if ((i + 1)%7==0) System.out.println();
}
System.out.println();
}
public static void quicksort(int[] data, int count) {
quicksortHelper(data, 100, data.length - 1);
}
Changing the code between 0 and 100 above this line modifies the output a bit.
protected static void quicksortHelper(int[] data, int bottom, int top){
if (bottom < top) {
int midpoint = partition(data,bottom,top);
quicksortHelper(data, bottom, midpoint -1 );
quicksortHelper(data, midpoint + 1, top);
}
}
protected static int partition(int[] data, int bottom, int top){
int pivot = data[top];
int firstAfterSmall = bottom;
for (int i = bottom ; i < top; i++){
if (data[i] <= pivot) {
swap(data, firstAfterSmall, i);
firstAfterSmall++;
}
}
swap(data, firstAfterSmall, top);
return firstAfterSmall;
}
protected static void swap(int[] data, int i, int j){
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
Any help is appreciated, my brain is about to explode messing around with this.
Sample output:
Array before sort
1 5 3 2 9 10 100
4 6 5
array after quicksort
1 6 3 2 9 10 100
4 6 5
If the numbers mentioned above are changed to 0 then the "array after quicksort" is printed in all 0s, like such
array after quicksort
0 0 0 0 0 0 0
0 0 0
In your quicksort method, you call quicksortHelper with the arguments (100, data.length - 1). This will sort the last element of the array, which is not that helpfull. Change it to :
quicksortHelper(data, 0, count);
In terms of always printing 0, without futher insight, can't really help beyond checking the data in input.txt and making sure it has exactly 100 ints in it etc, otherwise your array values are not being initialised.
Although I assume this is for an assignment, rather than reinvent the wheel, you can always call Arrays.sort to accomplish this task in the future.
Edit:
The second argument should be count rather than data.length - 1, else, you are sorting the array which will contain ~ 90 0's in in then only printing out the first 10, which after sorting the entire thing will all be 0's. Thus, you only want to sort the first count items.
You could use Arrays.sort(data) to sort instead of having all that messy code :)

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