I'm a programming student and rather than post the whole assignment I'll just ask for help solving what I've tried for hours now to understand. I'm tasked with sorting an array of strings using the quicksort method. Everything else I've been tasked with as part of this problem is fine but when I tested the sorting method by printing out the String Array, it's completely jumbled up without any seeming rhyme or reason. Please help me pinpoint the error in my code, or the several glaring errors I've overlooked. The array of strings provided is this list of 65 names: http://pastebin.com/jRrgeV1E and the method's code is below:
private static void quickSort(String[] a, int start, int end)
{
// index for the "left-to-right scan"
int i = start;
// index for the "right-to-left scan"
int j = end;
// only examine arrays of 2 or more elements.
if (j - i >= 1)
{
// The pivot point of the sort method is arbitrarily set to the first element int the array.
String pivot = a[i];
// only scan between the two indexes, until they meet.
while (j > i)
{
// from the left, if the current element is lexicographically less than the (original)
// first element in the String array, move on. Stop advancing the counter when we reach
// the right or an element that is lexicographically greater than the pivot String.
while (a[i].compareTo(pivot) < 0 && i <= end && j > i){
i++;
}
// from the right, if the current element is lexicographically greater than the (original)
// first element in the String array, move on. Stop advancing the counter when we reach
// the left or an element that is lexicographically less than the pivot String.
while (a[j].compareTo(pivot) > 0 && j >= start && j >= i){
j--;
}
// check the two elements in the center, the last comparison before the scans cross.
if (j > i)
swap(a, i, j);
}
// At this point, the two scans have crossed each other in the center of the array and stop.
// The left partition and right partition contain the right groups of numbers but are not
// sorted themselves. The following recursive code sorts the left and right partitions.
// Swap the pivot point with the last element of the left partition.
swap(a, start, j);
// sort left partition
quickSort(a, start, j - 1);
// sort right partition
quickSort(a, j + 1, end);
}
}
/**
* This method facilitates the quickSort method's need to swap two elements, Towers of Hanoi style.
*/
private static void swap(String[] a, int i, int j)
{
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
Ok, i was mistaken that it would work and found your tiny mistake.
Take a look at wikipedias pseudo code
You will notice that your conditions in the while loop are causing the error
if you change (a[i].compareTo(pivot) < 0 && i <= end && j > i) and (a[j].compareTo(pivot) > 0 && j >= start && j >= i) to
(a[i].compareTo(pivot) <= 0 && i < end && j > i) and (a[j].compareTo(pivot) >= 0 && j > start && j >= i).
Thought this would help for those who seek for a string sorting algorithm based on quick sorting method.
public class StringQuickSort {
String names[];
int length;
public static void main(String[] args) {
StringQuickSort sorter = new StringQuickSort();
String words[] = {"zz", "aa", "cc", "hh", "bb", "ee", "ll"}; // the strings need to be sorted are put inside this array
sorter.sort(words);
for (String i : words) {
System.out.print(i);
System.out.print(" ");
}
}
void sort(String array[]) {
if (array == null || array.length == 0) {
return;
}
this.names = array;
this.length = array.length;
quickSort(0, length - 1);
}
void quickSort(int lowerIndex, int higherIndex) {
int i = lowerIndex;
int j = higherIndex;
String pivot = this.names[lowerIndex + (higherIndex - lowerIndex) / 2];
while (i <= j) {
while (this.names[i].compareToIgnoreCase(pivot) < 0) {
i++;
}
while (this.names[j].compareToIgnoreCase(pivot) > 0) {
j--;
}
if (i <= j) {
exchangeNames(i, j);
i++;
j--;
}
}
//call quickSort recursively
if (lowerIndex < j) {
quickSort(lowerIndex, j);
}
if (i < higherIndex) {
quickSort(i, higherIndex);
}
}
void exchangeNames(int i, int j) {
String temp = this.names[i];
this.names[i] = this.names[j];
this.names[j] = temp;
}
}
Related
public static void main(String[] args) {
int[] numbers = {20,4,7,6,1,3,9,5};
mergeSort(numbers);
}
private static void mergeSort(int[] inputArray) {
int inputLength = inputArray.length;
System.out.println(inputLength);
if (inputLength < 2)
return;
int midIndex = inputLength / 2;
int[] leftHalf = new int[midIndex];
int[] rightHalf = new int[inputLength - midIndex];
for (int i = 0; i < midIndex; i++) {
leftHalf[i] = inputArray[i];
}
for (int i = midIndex; i < inputLength; i++) {
rightHalf[i - midIndex] = inputArray[i];
}
mergeSort(leftHalf);
mergeSort(rightHalf);
merge(inputArray, leftHalf, rightHalf);
}
private static void merge(int[] inputArray, int[] leftHalf, int[] rightHalf) {
int leftSize = leftHalf.length;
int rightSize = rightHalf.length;
int i = 0, j = 0, k = 0;
while (i < leftSize && j < rightSize) {
if (leftHalf[i] <= rightHalf[i]) {
inputArray[k] = leftHalf[i];
i++;
} else {
inputArray[k] = rightHalf[j];
j++;
}
k++;
}
while (i < leftSize) { //eğer karşılaştırılmayan bir tane kalırsa diye yapılıyor yani
inputArray[k] = leftHalf[i];
i++;
k++;
}
while (j < rightSize) {
inputArray[k] = rightHalf[j];
j++;
k++;
}
}
In the mergeSort part if inputLength < 2 part of the code, we return when the length is less than 2. And last time the inputLength was 1, it becomes 2 and returns to the array [20,4].
This did not make sense to me logically. How does it get back to [20,4] when last we had [20] left?
first of all the code you have shared is flawed in the merge function part, you can find the proper code for Merge sort online, you can refer to
https://www.geeksforgeeks.org/java-program-for-merge-sort/
Now for understanding merge sort you have to understand the concept of stack (Last in First out) & recursion. In recursion the lines after the recursive call wait till the recursive call to the function has not executed completely. So in case of the
1st call Merge sort the length of the array is n and waiting for the complete execution of mergeSort(leftHalf) and mergeSort(rightHalf) both of size (n/2).
now for both the mergeSort(leftHalf) and mergeSort(rightHalf)
there will be sub left part and sub right part and this will continue till the size of the array becomes <2 and the remaining part will wait.
and after the successful execution of the smallest part it will return to the previous part from where this part was called. By this eventually this will return to the place where the function was called first.
And in case of your code both the smaller arrays are merged into the larger array so the data of the left and right sub array aren't lost.
I am supposed to write a version of the selection sort algorithm that moves the smallest number to the front of the array while simultaneously moving the largest number to the end of the array. I understand that it is basically working from both ends in so there are two sorted sub arrays, however my code seems to misplace the last number in the array. My code:
public static void dualSelectionSort(int[]a) {
for(int i=0; i<a.length-1;i++) {
int min=i;
int max=(a.length-1)-i;
for (int j=i+1;j<a.length-i;j++) {
if(a[j]<a[min]) {
min=j;
}
if(a[j]>a[max]) {
max=j;
}
}
int temp1=a[min];
int temp2=a[max];
a[min]=a[i];
a[max]=a[(a.length-1)-i];
a[i]=temp1;
a[(a.length-1)-i]=temp2;
}
}
If given the input [5,4,3,2,1], it outputs [1,2,5,3,4] instead of [1,2,3,4,5].
What am I missing?
The problem is in the inner loop.
It is starting from i+1.
So the code for comparing max is actually comparing a[1] to a[4].
you can change it as
for (int j=i;j<a.length-i;j++)
Also as you can read in comment there can be a case where with the above change the code will still not work.
This will happen because i == max and so the existing swap approach will not work.
For example:
assume array =[6,4,5],inner loop max =0,min=1.Existing swap will make it [4,6,6].
Hence,swap should be handled differently.
if(i==max) {
swap(a,max,a.length-1-i);
swap(a,min,i);
}else {
swap(a,min,i);
swap(a,max,(a.length-1)-i);
}
The complete code is as below:
public static void dualSelectionSort(int[]a) {
for(int i=0; i<a.length-1;i++) {
int min=i;
int max=(a.length-1)-i;
for (int j=i;j<a.length-i;j++) {
if(a[j]<a[min]) {
min=j;
}
if(a[j]>a[max]) {
max=j;
}
}
if(i==max) {
swap(a,max,a.length-1-i);
swap(a,min,i);
}else {
swap(a,min,i);
swap(a,max,(a.length-1)-i);
}
}
}
public static void swap(int[] a,int i,int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
In each iteration of the outer loop, you start with the first index (i) as the initial minimum index and the last index (a.length-1-i) as the initial maximum index. Then you compare both of these indices to all the indices in the range i+1 to a.length-i-1. But if the first index (i) actually contain the max value, you never compare it to a[max], so a[max] will never contain the correct value in the end of the inner loop.
Here's a suggested fix:
public static void dualSelectionSort(int[]a) {
for(int i = 0; i < a.length - i - 1; i++) {
if (a[i] > a[(a.length - 1) - i]) {
int temp = a[i];
a[i] = a[(a.length - 1) - i];
a[(a.length - 1) - i] = temp;
}
int min = i;
int max = (a.length - 1) - i;
for (int j = i + 1; j < (a.length -1 ) - i; j++) {
if(a[j] < a[min]) {
min = j;
}
if(a[j] > a[max]) {
max = j;
}
}
int temp1 = a[min];
int temp2 = a[max];
a[min] = a[i];
a[max] = a[(a.length - 1) - i];
a[i] = temp1;
a[(a.length - 1) - i] = temp2;
}
}
The three things I changed:
The outer loop can end much sooner than you end it - once i >= a.length-i-1 - since in each iteration you find the minimum and maximum value, so you reduce your remaining unsorted array by 2.
When initializing the min and max indices, make sure min index actually contains a value smaller the max index. If a[i] > a[(a.length-1)-i], swap them before setting min to i and max to (a.length-1)-i.
The inner loop should iterate j from i+1 to (a.length-1)-i-1.
I have two implementation of quick sort the first one uses a median of (fist ,middle , last ) as pivot and the second uses the middle element as pivot
the first Implementation :
public class quickMedian {
public void sort(int array[])
// pre: array is full, all elements are non-null integers
// post: the array is sorted in ascending order
{
quickSort(array, 0, array.length - 1); // quicksort all the elements in the array
}
public void quickSort(int array[], int start, int end)
{
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) // check that there are at least two elements to sort
{
if (array[start+(end-start)/2]>array[end]){
swap(array,start+(end-start)/2, end);
}
if (array[start]>array[end]){
swap(array,start, end);
}
if (array[start+(end-start)/2]>array[start]){
swap(array, start+(end-start)/2, start);
}
int pivot = array[start]; // set the pivot as the first element in the partition
while (k > i) // while the scan indices from left and right have not met,
{
while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first
i++; // element greater than the pivot
while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first
k--; // element not greater than the pivot
if (k > i) // if the left seekindex is still smaller than
swap(array, i, k); // the right index, swap the corresponding elements
}
swap(array, start, k); // after the indices have crossed, swap the last element in // the left partition with the pivot
quickSort(array, start, k - 1); // quicksort the left partition
quickSort(array, k + 1, end); // quicksort the right partition
}
else // if there is only one element in the partition, do not do any sorting
{
return; // the array is sorted, so exit
}
}
public void swap(int array[], int index1, int index2)
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
int temp = array[index1]; // store the first value in a temp
array[index1] = array[index2]; // copy the value of the second into the first
array[index2] = temp; // copy the value of the temp into the second
}
}
The second Implementation :
public class quickSort {
private int array[];
private int length;
public void sort(int[] inputArr) {
if (inputArr == null || inputArr.length == 0) {
return;
}
this.array = inputArr;
length = inputArr.length;
quickSorter(0, length - 1);
}
private void quickSorter(int lowerIndex, int higherIndex) {
int i = lowerIndex;
int j = higherIndex;
// calculate pivot number, I am taking pivot as middle index number
int pivot = array[lowerIndex+(higherIndex-lowerIndex)/2];
// Divide into two arrays
while (i <= j) {
/**
* In each iteration, we will identify a number from left side which
* is greater then the pivot value, and also we will identify a number
* from right side which is less then the pivot value. Once the search
* is done, then we exchange both numbers.
*/
while (array[i] < pivot) {
i++;
}
while (array[j] > pivot) {
j--;
}
if (i <= j) {
exchangeNumbers(i, j);
//move index to next position on both sides
i++;
j--;
}
}
// call quickSort() method recursively
if (lowerIndex < j)
quickSorter(lowerIndex, j);
if (i < higherIndex)
quickSorter(i, higherIndex);
}
private void exchangeNumbers(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
To obtain the median we need to do extra steps on each recursion which may increase the time a little bit (if the array is totally random) .
I am testing these two classes on an array of size N=10,000,000 randomly generated and I have done the test many times the first Implementation takes around 30 seconds and the second takes around 4 seconds
so this is obviously not caused by the extra overhead to get the median of three numbers .
There must be something wrong with the first implementation, what is it ?
here is the testing code :
public static void main(String[] args) {
File number = new File ("f.txt");
final int size = 10000000;
try{
// quickSort s = new quickSort();
quickMedian s = new quickMedian();
writeTofile(number, size);
int [] arr1 =readFromFile(number, size);
long a=System.currentTimeMillis();
s.sort(arr1);
long b=System.currentTimeMillis();
System.out.println("quickSort: "+(double)(b-a)/1000);
}catch (Exception ex){ex.printStackTrace();}
}
I have the following questions regarding my implementation of the code for the QuickSort Algorithm using the first element as the pivot always.
I have 2 sample files for my work and it seems that it does not work when given certain input. There appears to be a StackOverflowError on my recursion.
Is there something wrong with my code? Or is there something I'm missing out?
Input that work will be random integers x10000.
Input that will have error will be number 10000 - 1 in a decreasing manner.
Both inputs are via text files seperated by \n.
public void quickSortOne(Integer [] arr)
{
int first = 0;
int last = arr.length-1;
quickSort1(arr, first, last);
}
public void quickSort1(Integer [] arr, int first, int last)
{
if (arr == null || arr.length == 0)
return;
if (first >= last)
return;
//pick the pivot as first
//int middle = first + (last - first) / 2;
int pivot = arr[first];
//make left < pivot and right > pivot
int i = first, j = last;
while (i <= j) {
while (arr[i] < pivot) {
i++;
q1Comparison++;
}
while (arr[j] > pivot) {
j--;
q1Comparison++;
}
if (i <= j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
q1DataMove++;
}
}
//recursively sort two sub parts
if (first < j)
quickSort1(arr, first, j);
if (last > i)
quickSort1(arr, i, last);
}
You are running out of thread stack size due to default limit set by its OS. Especially for windows its very less (64KB).
Try running your app using below setting ->
java -Xss:512k myApp
You may also increase it to 1M or higher if needed. Here is the link to more info. Search for "Xss" on this page. ->
http://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html
Hope this helps!
I have created the following class to sort an array of strings.
public class StringSort {
private String[] hotelNames;
private int arrayLength;
public void sortHotel(String[] hotelArray) {
if (hotelArray.length <= 1) {
return;
}
this.hotelNames = hotelArray;
arrayLength = hotelArray.length;
quicksort(0, arrayLength - 1);
}
private void quicksort(int low, int high) {
int i = low, j = high;
String first = hotelNames[low];
String last = hotelNames[high];
String pivot = hotelNames[low + (high - low) / 2];
while( (first.compareTo(last)) < 0 ) { // first is less than last
while( (hotelNames[i].compareTo(pivot)) < 0 ) { // ith element is < pivot
i++;
}
while( (hotelNames[j].compareTo(pivot)) > 0) { // jth element is > pivot
j--;
}
if ( ( hotelNames[i].compareTo( hotelNames[j] )) <= 0 ) {
swap(i, j);
i++;
j--;
}
//recursive calls
if (low < j) {
quicksort(low, j);
}
if (i < high) {
quicksort(i, high);
}
}
}
private void swap(int i, int j) {
String temp = hotelNames[i];
hotelNames[i] = hotelNames[j];
hotelNames[j] = temp;
}
}
However in my main class (a class to test StringSort), when I do:
StringSort str = new StringSort();
String[] hotel1 = {"zzzz", "wwww", "dddd", "bbbbb", "bbbba", "aaaf", "aaag", "zzz"};
str.sortHotel(hotel1);
And then I have another method that prints out the array. However when it prints out, it outputs the hotel1 array as it is, unchanged. There is no 'sorting' happening, I'm not sure where I've gone wrong.
There are several problems in your implementation of quicksort:
First/last comparison. This code will made your quicksort not do anything as long as first element is less than the last element, regardless of any other order.
while( (first.compareTo(last)) < 0 ) { // first is less than last
Check before swap. This line is unnecessary:
if ( ( hotelNames[i].compareTo( hotelNames[j] )) <= 0 ) {
What you really want to do is see if the i is still less than j and bail out of the loop then. If not, then swap. After you finished with the partitioning loop, then make the recursive call, as long as there are more than two elements in each subarray.