Lexicographic Quick Sort - java

I try to sort an array of Strings in lexicographical order by using the quick sort algorithm in java. The array is read in via the terminal using a Scanner and saved in an ArrayList. This ArrayList is later converted to an array where I (try to) apply the quick sort algorithm on.
I have two methods:
private static void sortA(String[] s, int start, int end) {
if (end > start) {
int pivot = partition(s, start, end);
sortA(s, start, pivot - 1);
sortA(s, pivot + 1, end);
}
}
private static int partition(String[] s, int start, int end) {
String pivot = s[end];
int left = start;
int right = end;
String temp = "";
do {
while ((s[left].compareTo(pivot) <= 0) && (left < end))
left++;
while ((s[right].compareTo(pivot) > 0) && (right > start))
right--;
if (left < right) {
temp = s[left];
s[left] = s[end];
s[right] = temp;
printRow(s);
}
} while (left < right);
temp = s[left];
s[left] = s[end];
s[end] = temp;
return left;
}
The code seems to randomly work fine and then suddenly not. For example the array {"java", "application", "system"} sorts fine to {"application", "java", "system"}. The array {"library", "content", "bin"} sorts to {"bin", "library", "contents"}, which is not the lexicographic order. Of course a computer does not work randomly so there must be something wrong with my code. I tried to work out an example on paper but then I'll find something completely wrong. However I based my code on an quick sort implementation of sorting a double array so I don't think I made a big reasoning error.
Thanks in advance.

You are splitting the array in the wrong way:
The correct splitting is "pivot-1" "pivot"
sortA(s, start, pivot-1);
sortA(s, pivot, end);

Related

I'm trying to implement merge sort but the output i get is the same array

I'm trying to implement merge sort but the output i get is the same array.this merge sort gives me the same output as input.plz help.i'm pretty sure the implementation is correct and i tried debugging it but couldnt figure out the mistake.
Solved: i was comparing helper[left] to helper[mid] instead of helper right. Thanks for the help people.
public class Sort {
public static void main(String[] args){
int arr[] = {3,5,6,9,0,2,4};
Sort sort = new Sort();
int i=0;
sort.MergeSort(arr);
for(i=0;i<arr.length;i++){
System.out.print(arr[i]);
}
}
private void MergeSort(int[] arr2) {
// TODO Auto-generated method stub
int helper[] = new int[arr2.length];
MergeSort(arr2,helper,0,arr2.length-1);
}
private void MergeSort(int[] arr2,int[] helper,int start, int end) {
if(start<end){
int mid = (start+end)/2;
MergeSort(arr2,helper,start,mid);
MergeSort(arr2,helper,mid+1,end);
Merge(arr2,helper,start,mid,end);
}else{
//do nothing
}
}
private void Merge(int[] arr2, int[] helper, int start, int mid, int end) {
// TODO Auto-generated method stub
int i;
for(i=start;i<=end;i++){
helper[i]=arr2[i];
}
i=start;
int left = start;
int right = mid+1;
while(left <= mid && right <= end){
if(helper[left] <= helper[mid]){
arr2[i] = helper[left];
left++;
}else{
arr2[i] = helper[right];
right++;
}
i++;
}
//move remaining of left to array
int remaining = mid-left;
int j;
for(j=0;j<=remaining;j++){
arr2[i+j]=helper[left+j];
}
}
}
Tricky one... :-)
you are always comparing the element at leftto the element at mid instead of the element at right:
while(left <= mid && right <= end){
if(helper[left] <= helper[mid]){
should be
while(left <= mid && right <= end){
if(helper[left] <= helper[right]){
Optional, but more clear:
Besides as Matt mentioned you are missing to catch up remaining values from the right arm
while (left <= mid)
arr2[i++] = helper[left++];
while (right <= end) {
arr2[i++] = helper[right++];
Edit: Mark right arm catchup as optional
I think it's that your merge() function is missing a certain case.
//move remaining of left to array
int remaining = mid-left;
int j;
for(j=0;j<=remaining;j++){
arr2[i+j]=helper[left+j];
}
takes care of if the right sub-array was exhausted first, but does not consider the case if the left sub array was exhausted first (which may lead to some unusual results). I know this wasn't quite what you were trying to ask and I will keep looking at your code to see if I can help.
Happy Coding! Leave a comment if you have any questions.

Reversing the order of sub-array using Recursion

I am trying to reverse the order of a sub-array between the indices of start and end strictly using recursion. For example, if the subarray is 1,2,3,4 , it will become 4,3,2,1.
However, I am getting the following runtime error:
java.lang.ArrayIndexOutOfBoundsException: -1
at finalExam.reverse(finalExam.java:13)
at finalExam.reverse(finalExam.java:17)
I am not sure how to fix this problem.
Thanks.
double[] reverse (double[] a, int start, int end) {
if (start == end) {return a;}
else {
a[start] = a[end];
a[end] = a[start];}
return reverse (a, start+1, end-1);
}
(Since you mention the exam is over). Here are the problems with your code:
Your check should be start >= end
Your code for swapping two numbers is incorrect.
Here is the correct solution:
public static double[] reverse (double[] a, int start, int end) {
if (start >= end) {
return a;
}
else {
// this code will swap two elements
double temp = a[start];
a[start] = a[end];
a[end] = temp;
}
return reverse (a, start+1, end-1);
}

Binary Search Java

Im working on a project in which it reads book titles in from a .txt file and puts them into an arraylist, then the arraylist is converted to an array, The user enters a number which is the books reference number, then it does a linear search and a binary search to find that book. Im just having a trouble with the code for the binary search,as i have almost no idea how to do it, heres what I have:
private void FindItActionPerformed(java.awt.event.ActionEvent evt) {
String input;
input = Input.getText();
for(int i=0; i<bookList.length; i++){
if (bookList[i].referenceNumber.equals(input)){
Output1.setText("The Book is " + bookList[i].title);
}
}
Above is the code for the linear search, which works fine. Below is what i think i need for the binary search but again, i am not sure and cannot figure it out.
int right = 0, left = bookList.length;
while(right<= left){
int middle = (right + left)/2;
if( bookList[middle].referenceNumber.equals(input)){
Output2.setText("The book is " + bookList[middle].title);
}
}
}
Here is the class and the arrays
public class Book{
String referenceNumber, title;
public Book(String _referenceNumber, String _title){
referenceNumber = _referenceNumber;
title = _title;
}
}
ArrayList <Book> Books = new ArrayList <Book> ();
Book [] bookList;
Thanks for any help you may be able to offer, This is a bit tricky for me.
I had problems when I was learning to code binary search aswell. The first thing you should know is that you do not have to do a binary search and a linear search, you only need to do one or the other. Also to do a binary search you need to sort your array ex int[] array = {1,2,3,4,5,6,7,8,9,10};
What a binary search does is it looks at the middle element in the array and tests to see if the element is greater or less than the key. If it is less everything less then the middle element is ignored (same for bigger just everything bigger is thrown away). Then a new middle element is selected and half is thrown away, and this is done until the key is found. Below is the code for sorting a int array, you would need to modify it to return books (string? or the class book you may have written)
public static boolean binarySearch(int[] array, int key){
int partition = 0;
int right = array.length - 1;
boolean found = false;
int left = 0;
while(! found && left <= right){
if(array[partition] == key){
found = true;
}
if(array[partition] > key){//key less
right = partition - 1;
partition = (right + left) / 2;
}
if(array[partition] < key){//key greater
left = partition + 1;
partition = (left + right) / 2;
}
}
return found;
}
Also here is some code for sorting an array of ints. This is a bubble sort so it is slow On^2
public int[] bubbleSort(int[] array){
int temp;
boolean keepGoing = true;
while(keepGoing == true){
keepGoing = false;
for(int i=0; i < array.length - 1; i++){
if(array[i] > array [i + 1]){ //if i < i + 1 means greatest to smallest if
// if i > i + 1 means smallest to greatest
swap(array, i, i + 1);
keepGoing = true;
}
}
}
return array;
}
The code is simple would have to modify it to sort your books the method swap is below
public int[] swap(int[] array, int i, int j){
int temp = 0;
temp = array[i];
array[i] = array[j];
array[j] = temp;
return array;
}
There is nice visualisation of binary search at
http://balance3e.com/Ch8/search.html
For example try to enter FL and watch the algorithm looking for it step by step.
You will get it quickly :)
It works like looking up a word in a dictionary... You are looking for "cat" for example,
so you open your dictionary in half and see word "man" this is lexicographicaly bigger than "cat", so you will be looking to the left from "man" = in first half of the dictionary...
Then you only repeat this process of dividing into smaller parts until you find what you were looking for.

Quicksort not completely sorting provided array

My Quicksort seems to stop before completely sorting the array, and I've stared myself blind on the code.
I wrote the algorithm according to the related chapters in Java Software Structures - Designing and Using Data Structures (3rd Edition)
Quick Sort:
private static <T extends Comparable<T>> void quickSort(T[] array,int min, int max){
int pIndex;
if (max-min > 0) {
pIndex = partition(array, min, max);
quickSort(array, min, pIndex-1);
quickSort(array, pIndex+1, max);
}
}
Partition:
private static <T extends Comparable<T>> int partition(T[] array, int min, int max) {
int left, right;
T pivot, temp;
int middle = (min+max)/2;
left = min;
right = max;
pivot = array[middle];
while (left < right) {
while (array[left].compareTo(pivot) <= 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
if (left<right) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
temp = array[min];
array[min] = array[right];
array[right] = temp;
return right;
}
The input:
An int[10] array containing the values 0 through 9, shuffled.
The quicksort-function is thus called like: quicksort(nums, 0, nums.length-1)
The output (example):
0
2
1
3
7
4
5
6
8
9
As you can see, the end product seems to be somewhat on the way to a good end-product, but it's stopping prematurely somewhere.
Update:
None of the answers provided so far (the deleted ones included) worked. If nobody is able to spot the bug, would anyone kindly redirect me to a good source for generic algorithms in Java?
I even shamefully attempted to do a pure copypaste of the Quicksort algorithm from the book mentioned above, and while it compiled and ran, it resulted in the same, "almost-correct" output as above. I then questioned whether or not it may be my input data, but nope. It is simply an Integer-array of integers, no duplicates. It's a valid candidate to my understanding.
I was able to get quick sort to sort some test arrays with the following partition function.
private static <T extends Comparable<T>> int partition(T[] array, int min, int max) {
int left, right;
T pivot, temp;
int middle = (min+max)/2;
left = min;
right = max ;
pivot = array[middle];
while (left < right) {
while (array[left].compareTo(pivot) < 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
if (left<right) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
return right;
}
All I changed was the first compareTo comparison to be less than instead of less than or equal to. This allows the pivot to move in the array. This however does mean that the array CANNOT contain duplicates. I also removed the last swap as I couldn't tell what it was doing.
The problems stem from how you deal with the pivot. It doesn't actually partition the array properly.
This also works and allows duplicates.
private static <T extends Comparable<T>> int partition(T[] array, int min, int max) {
int left, right;
T pivot, temp;
int middle = (min+max)/2;
left = min + 1;
right = max ;
pivot = array[middle];
// move partition element to min index
temp = array[min];
array[min] = array[middle];
array[middle] = temp;
while (left < right) {
while (array[left].compareTo(pivot) <= 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
if (left<right) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
// move partition element to partition index
temp = array[min];
array[min] = array[right];
array[right] = temp;
return right;
}
I looked up a copy of the book. The comment tells you what the last swap was trying to do. Which makes my fix of adding a swap at the begging to move the partition element to the min index the correct fix.
while (array[left].compareTo(pivot) <= 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
These loops are usually written:
while (array[left++].compareTo(pivot) <= 0 && left < right)
;
while (array[right--].compareTo(pivot) > 0)
;
The idea is to stop at the first element that doesn't belong in this partition.

Quicksort - recursive

I am trying to code quicksort in two ways, one in-place, and the other by using separate arrays. I'm kind of stuck on some of the logic, take a look at what I have, Thanks for the help in advance!
public List<Integer> sort(List<Integer> arr){
if(arr.length > 0)
List<Integer> ret = new ArrayList<Integer>();
ret = quickSort(arr);
return ret;
}
public List<Integer> quickSort(List<Integer> arr){
if(arr.length < 2)
return;
int pivot = arr[0];
List<Integer> left = new ArrayList<Integer>();
List<Integer> right = new ArrayList<Integer>();
for(int i = 0; i < arr.length; i++){
if(arr[i] <= pivot)
left.add(arr[i]);
else
right.add(arr[i]);
}
quickSort(left);
quickSort(right);
}
Now i'm stuck, I don't know what I would do after recursively going through both sets, mostly stuck on how would I connect them together and return a sorted list.
You need to combine left and right sequences together. You need to do it at the end of your algorithm (before the closing }). In pseudo code:
int leftpos = 0, rightpos = 0;
List newlist = new ArrayList();
for(int pos = 0; pos < arr.length; pos++)
if left[pos] < right[pos] newlist.add(left[leftpos++]);
else newlist.add(right[rightpos++]);
return newlist;
This is just a pseudo-code. You need to add code to check lengths of each array (left and right) in the for cycle.
Also I must note that this is far from quicksort. So many new array allocations make the algorithm extremely slow and that's unwelcome when sorting.
Also, right side of line 3 is redundant. You don't need to allocate anything here, as it is overwritten in the next line. I would just simply replace your lines 3-5 with this:
return quickSort(arr);
Let me have a crack at this for you.
First off, you always want to do in-place sorting unless you're working with linked lists (and even then it usually pays to convert to an array, sort in place, then convert back to a linked list -- it puts way less pressure on the garbage collector). .NET List<>s are actually expanding arrays.
Next, quicksort is really all about the pivot operation. Here's one way to do it:
// Quicksort the sub-array xs[lo..hi].
void QSort(int[] xs, int lo, int hi) {
if (hi <= lo) return; // Don't sort empty or singleton sub-arrays.
var p = [choose some pivot value from xs[lo..hi]];
var a = lo; // Invariant: x[lo..a - 1] <= p.
var z = hi; // Invariant: p < x[z + 1..hi].
while (a <= z) {
if (xs[a] <= p) a++; else Swap(xs, a, z--);
}
QSort(xs, lo, a - 1); // Sort the items <= p.
QSort(xs, z + 1, hi); // Sort the items > p.
}
void Swap(int[] xs, int i, int j) {
var tmp = xs[i];
xs[i] = xs[j];
xs[j] = tmp;
}
Simple implementation on Groovy
def qs(list) {
if (list.size() < 2) return list
def pivot = list[0]
def items = list.groupBy { it <=> pivot }.withDefault { [] }
qs(items[-1]) + items[0] + qs(items[1])
}

Categories

Resources