Creating a QuickSort for an Array of names - java

So I am working on a project for my class and I am currently stuck on creating a QuickSort class to sort an Array of 1000 names. I have a template I am using from a previous Lab we did in class which we are supposed to base it off of; but in the lab we used an array of Integers and I am struggling with how to convert it so it will work with Strings; names. Thanks for your help or any suggestions, the code is below.
Updated post; So I made my comparison in my Name class
public int compareTo(Object other) {
int result;
if (name.equals(((Name) other).name))
result = name.compareTo(((Name) other).name);
else
result = name.compareTo(((Name) other).name);
return result;
}
And I've tried to re-work my QuickSort..I'm struggling with the swap method.
private ArrayList<Name> data;
public QuickSort(ArrayList<Name> initialValue){
data=initialValue;
}
public void sort(ArrayList<Name> namelist, int i, int j){
sort(0, data.size()-1);
}
public void sort(int from, int to){
if (from >= to)
return;
int p = partition(from, to);
sort(from, p);
sort( p + 1, to);
}
private int partition(int from, int to){
Name pivot = data.get(from);
int i = from - 1;
int j = to + 1;
while(i<j){
i++; while(data.get(i).compareTo(pivot) < 0) i++;
j--; while(data.get(j).compareTo(pivot) < 0) j--;
if(i<j) swap(i,j);
}
return j;
}
private void swap (int i, int j){
Name temp = data.get(i);
data.equals(i) = data.get(j);
data = temp;
}
particularly the "data.equals(i) = data.get(j) line and the data = temp; I am sure i'm doing something stupid and easy.
update;
private void swap (int i, int j){
Name temp = data.get(i);
data.get(j).equals(data.get(i));
data.get(j).equals(temp);
}
possibly?

Posting the code that will solve the problem would be easy but won't help you to learn the meaning of QuickSort (or another sorting algorithm).
The heart of the QuickSort is exchanging the elements here:
while(i<j){
i++; while(data[i] < pivot) i++;
j--; while(data[j] > pivot) j--;
if(i<j) swap(i,j);
}
As you can see, you're comparing the elements of the data array against the pivot variable. Since they're ints, you can easily compare them using <. Now, you have to do something similar but for Strings. Thankfully, Strings can be compared using String#compareTo method. I'll let you this implementation for String (otherwise I will present the homework assignment as mine =P).
For a more generic solution to the problem, you have two options:
Making your class implement the Comparable interface, so you will have a compareTo method. A basic
sample implementation:
public class Name implements Comparable<Name> {
#Override
public int compareTo(Name name) {
return ... //comparison logic...
}
}
Using it in your QuickSort
pivot.compareTo(...);
Using an instance of Comparator interface. You will use Comparator#compare for this. A basic sample implementation:
public class NameComparator implements Comparator<Name> {
#Override
public int compare(Name name1, Name name2) {
return ... //comparison logic...
}
}
Using it in your QuickSort
NameComparator nameComparator = new NameComparator();
nameComparator.compare(..., ...);

You can use a comparator: Comparator.compare(o1, o2) returns -1, 0 or 1 if the objects are less, equal or greater.
Strings in java are actually comparable, some sort of a companion interface:
int compareTo(T other);
API Do: http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html

Note that in string comparison is by equals method:
data.get(j) == pivot => data.get(j).equals(pivot)

You will have to set a String value to compare it to something. So by setting the pivot value to compare it to itself it will return zero. Since it is unlikely that all Strings will equal your pivot value then anything compared to pivot value will be returned as -1 OR 1. By doing this your if statement will determine which way the swap value is sent( Higher OR Lower) then your pivot value.
ObjectQuickSorter sortStrings = new ObjectQuickSorter();
sortStrings.sort(arrayHere);
class ObjectQuickSorter{
void sort(String[] array){
doQuickSort(array, 0, array.length -1);
}
private static void doQuickSort(String[] array,int start, int end){
int pivotPoint;
if(start < end){
pivotPoint = partition(array, start, end);
doQuickSort(array, start, pivotPoint -1);
doQuickSort(array, pivotPoint + 1, end);
}
}
private static int partition(String[] array, int start, int end){
String pivotValue;
int endOfLeftList;
int mid = (start + end)/2;
swap(array, start, mid);
pivotValue = array[start];
endOfLeftList = start;
for(int scan = start + 1; scan <= end; scan++){
// trying to compare pivot = string value to array[scan] position value
// doing this by setting pivot value compare to itself to return 0
// then also comparing pivot value to array[scan] to return -1, 0, 1
// if return is 0 or 1 then it ignores it
if( array[scan].compareTo(pivotValue) < array[start].compareTo(pivotValue)){
endOfLeftList++;
swap(array, endOfLeftList,scan);
}
}
swap(array, start, endOfLeftList);
return endOfLeftList;
}
private static void swap(String[] array, int a, int b){
String temp;
temp = array[a];
array[a] = array[b];
array[b] = temp;
}
}

Related

MergeSort on ArrayList only work with ArrayList(Collection<? extends E> c) not ArrayList(int initialCapacity)?

I am working on sort interval which exist on an ArrayList with its start property, the full definition of interval will show in sample code as private class.
The implementation I am using is MergeSort, and very similar to Princeton's stuff which I refer to, but problem is I find this implementation only work with creating auxiliary ArrayList aux with ArrayList(Collection<? extends E> c) and initialize it with aux.set(i, intervals.get(i)); which try to fill aux with same order of interval items from original list
I try to work it out with creating aux with ArrayList(int initialCapacity) and initialize it with aux.add(intervals.get(i)), but failed. And I still could not figure out why this way doesn't work ?
The full code which can directly used for debug:
public class Test {
private class Interval {
// Will use start property to sort
int start;
int end;
Interval() {
start = 0;
end = 0;
}
Interval(int s, int e) {
start = s;
end = e;
}
#Override
public String toString() {
return "[" + start + "," + end + "]";
}
}
public void sortHelper(List<Interval> intervals) {
int len = intervals.size();
// Only this way works
List<Interval> aux = new ArrayList<Interval>(intervals);
// This way NOT work ?
//List<Interval> aux = new ArrayList<Interval>(len);
sort(intervals, aux, 0, len - 1);
}
public void sort(List<Interval> intervals, List<Interval> aux, int lo, int hi) {
if(hi <= lo) {
return;
}
int mid = lo + (hi - lo) / 2;
sort(intervals, aux, lo, mid);
sort(intervals, aux, mid + 1, hi);
mergeHelper(intervals, aux, lo, mid, hi);
}
public void mergeHelper(List<Interval> intervals, List<Interval> aux, int lo, int mid, int hi) {
// Only this way works
for(int i = lo; i <= hi; i++) {
aux.set(i, intervals.get(i));
}
// Combine with List<Interval> aux = new ArrayList<Interval>(len);
// this way NOT work ?
//for(int i = lo; i <= hi; i++) {
// aux.add(intervals.get(i));
//}
int left = lo;
int right = mid + 1;
for(int k = lo; k <= hi; k++) {
if(left > mid) {
intervals.set(k, aux.get(right++));
} else if(right > hi) {
intervals.set(k, aux.get(left++));
} else if(less(aux.get(right), aux.get(left))) {
intervals.set(k, aux.get(right++));
} else {
intervals.set(k, aux.get(left++));
}
}
}
public boolean less(Interval a, Interval b) {
return a.start - b.start < 0;
}
public static void main(String[] args) {
Test m = new Test();
Interval one = m.new Interval(1, 3);
Interval two = m.new Interval(2, 6);
Interval three = m.new Interval(8, 10);
Interval four = m.new Interval(15, 18);
List<Interval> intervals = new ArrayList<Interval>();
// Adding order as [2,6],[1,3],[8,10],[15,18]
intervals.add(two);
intervals.add(one);
intervals.add(three);
intervals.add(four);
m.sortHelper(intervals);
// Expected sort result should be
// [1,3],[2,6],[8,10],[15,18]
for(Interval i : intervals) {
System.out.println(i);
}
}
}
Could anyone help me on understanding why must create auxiliary ArrayList aux with ArrayList<Collection<? extends E> c> and initialize it with aux.set(i, intervals.get(i)); ? Or my understanding goes the wrong way ?
Thanks
Thanks for important hint from #maraca and #Dukeling, after some work, below is my opinion for the issue here:
Q1: Why using List<Interval> aux = new ArrayList<Interval>(len); with aux.add(intervals.get(i)); not work ?
A1: There are two main issues here, I should admit I fail into some habitual thoughts as previous work with array implementation of MergeSort, the impact here are (1) new ArrayList<Interval>(len) equal to new Interval[len] or not ? (2) aux[i] = intervals[i](just for explain, not use = in real code) equal to aux.add(intervals.get(i)) ?
For problem (1), the real case here is using new ArrayList<Interval>(len) will only define the initial limitation of ArrayList size, but no real items used like space holder to fill into the initialized list, e.g If we set len = 4, the initialized list is actually [], not [interval obj, interval obj, interval obj, interval obj]. Assume using new ArrayList<Interval>(len) with aux.set(i, intervals.get(i)), IDE will throw out java.lang.IndexOutOfBoundsException: Index: 0, Size: 0, that's because no real object fill into this list as initialized and set method cannot work on an empty list. The solution for this issue is create len numbers of dummy interval objects to fill in list when initialized, such as
List<Interval> aux = new ArrayList<Interval>((Arrays.asList(new Interval(), new Interval(), new Interval(), new Interval())));, but since we don't care what initialized first on auxiliary list, just used as place holder and in case of the objects are too many, above code can be replaced with List<Interval> aux = new ArrayList<Interval>(intervals); which return to our right solution.
For problem (2), the real case here is using aux.add(intervals.get(i)) not equal to aux[i] = intervals[i], at least we should use set() instead of add(), because add() will continually append on aux, which not MergeSort suppose to, because MergeSort require exactly same size auxiliary collection copy to help sorting. Still use same example to explain what happened when using add()
Note after the 3rd merge, the aux size increase to double size of original list, this will cause indices issue when copy back to original list. But if we are using set() will not cause this trouble.
Q2: If still want to use add() what is the proper way ?
A2: As mentioned by #Dukeling, we need to clear() the aux before final round copy from original list to auxiliary list, e.g like before the 3rd merge happen on above example.
The working code with add() and clear() below:
public void mergeHelper(List<Interval> intervals, List<Interval> aux, int lo, int mid, int hi) {
aux.clear();
for(int i = 0; i < lo; i++) {
aux.add(null);
}
for(int i = lo; i <= hi; i++) {
aux.add(intervals.get(i));
}
int left = lo;
int right = mid + 1;
for(int k = lo; k <= hi; k++) {
if(left > mid) {
intervals.set(k, aux.get(right++));
} else if(right > hi) {
intervals.set(k, aux.get(left++));
} else if(less(aux.get(right), aux.get(left))) {
intervals.set(k, aux.get(right++));
} else {
intervals.set(k, aux.get(left++));
}
}
}

How to sort an array of segments (int left, int right) in a ascending order but if left(i)=left(i+1) sort it acording to right(i) and right(i+1)

I have a class Segment and an array of segments like this:
private static class Segment {
int number, type;
Segment(int number, int type) {
this.number = number;
this.type = type;
}
}
Segment[] points = new Segment[n];
points={(0,-1),(1,0),(5,1),(6,0),(6,-1),(10,1),(11,0)}
The element of the left is a list of points, and the list of the right is the type of point: -1 opens a segment, 1 closes a segment and 0 intersects the segment.
As you can see, this array is already sorted according to number, using this code (its an adapted selectionSort):
maxI finds the index of the biggest "number" element
private static int maxI(Segment[] segments, int size){
int max=0;
for (int i=0; i< size;i++){
if(segments[i].number > segments[max].number ){
max=i;
}
}
return max;
}
// swap method swaps elements of the array between index1 and index2
private static void swap(Segment[] segments, int index1, int index2){
int temp1;
int temp2;
temp1 = segments[index1].number;
temp2 = segments[index1].type;
segments[index1].number=segments[index2].number;
segments[index1].type=segments[index2].type;
segments[index2].number=temp1;
segments[index2].type=temp2;
}
selectSort is the sorting method (since Arrays.sort wont work with "segments")
private static void selectSort(Segment[] segments) {
int MaxPos;
for (int i=segments.length-1;i>0;i--){
MaxPos = maxI(segments, i+1);
swap (segments, MaxPos, i);
}
}
The original input was 2 ranges and 3 intersection points:
Range 1: 0 5
Range 2: 6 10
Intersection points: 1 6 11
So after sorting, the result as above is:
(0,-1),(1,0),(5,1),(6,0),(6,-1),(10,1),(11,0)
I've tried to modify the maxI method, so 6,-1 comes before 6,0 (-1 < 0) using a second if statement:
if (segments[i].number = segments[max].number && segments[i].type > segments[max].type)
But it messes the output.Since the input is random, the code must be prepared to sort testcases where many numbers are equal.
The closest question I've seen with this subject is one made in C++, I'm just learning Java so I'm struggling enough to try to understand C++. I feel the answer is close, but not sure what am I missing.Maybe I'm using the wrong data structure.
After this I just traverse the array, adding the sum of the types, so if a number passes 3 opening of ranges (x,-1), it's -3,in absolute= 3 so it's intersecting 3 ranges, which is the answer I'll need.
Just create a Comparator that compares number then type, then you can use Arrays.sort(). If you have Java 8, you can do it like this:
Arrays.sort(points, Comparator.comparingInt((Segment s) -> s.number).thenComparingInt((Segment s) -> s.type));
If you're using Java 7, you can do this:
Arrays.sort(points, new Comparator<Segment>() {
#Override
public int compare(Segment s1, Segment s2) {
int result = Integer.compare(s1.number, s2.number);
if (result == 0) {
result = Integer.compare(s1.type, s2.type);
}
return result;
}
});
Alternatively, you can have Segment implement the Comparable interface, and Arrays.sort(points) will work out of the box:
private static class Segment implements Comparable<Segment> {
int number, type;
Segment(int number, int type) {
this.number = number;
this.type = type;
}
#Override
public int compareTo(Segment s) {
int result = Integer.compare(this.number, s.number);
if (result == 0) {
result = Integer.compare(this.type, s.type);
}
return result;
}
}

how to understand quick sort algorithm

I am learning quick sort in java, according to the material I have. The best case is the pivot is the median, worst case is when one side of the pivot is always empty.
For the following code, is "indexPartition" the pivot? What kind of parameters do you put in the following function so it will run in the worst situation?
private void quickSortSegment(E[] list, int start, int end)
{
if (end-start>1)
{
int indexPartition = partition(list, start, end);
quickSortSegment(list, start, indexPartition);
quickSortSegment(list, indexPartition+1, end);
}
}
private int partition(E[] list, int start, int end)
{
E temp;
E partitionElement = list[start];
int leftIndex = start;
int rightIndex = end-1;
while (leftIndex<rightIndex)
{
while (list[leftIndex].compareTo(partitionElement) <= 0 && leftIndex<rightIndex)
{
leftIndex++;
}
while (list[rightIndex].compareTo(partitionElement) > 0)
{
rightIndex--;
}
if (leftIndex<rightIndex)
{
temp = list[leftIndex];
list[leftIndex] = list[rightIndex];
list[rightIndex] = temp;
}
}
list[start] = list[rightIndex];
list[rightIndex] = partitionElement;
return rightIndex;
}
No the pivot is partitionElement. It also seems this code always selects the first element in the sequence to be the pivot. The function will run in all cases including the worst cases, but it will perform badly(have square complexity).
Different people propose different solutions for selecting the pivot but I personally like this one: Select 3 random elements from the interval in consideration, compute their average and use it as pivot.
This video along with the wikipedia article should clear things up. Wikipedia is pretty awesome for explaining sorting algorithms. With respect to your question, indexPartition is rightIndex, which is the index of partitionElement, the pivot.
Something wrong with this method partition(), e.g. for {3,1,2} we'll get {1,3,2}:
public class CompareApp {
public static void main(String... args) {
Integer[] arr = {3, 1, 2};
quickSortSegment(arr, 0, 2);
for (Integer i : arr) System.out.println(i);
}
private static <E extends Comparable> void quickSortSegment(E[] list, int start, int end) {
if (end - start > 1) {
int indexPartition = partition(list, start, end);
quickSortSegment(list, start, indexPartition);
quickSortSegment(list, indexPartition + 1, end);
}
}
private static <E extends Comparable> int partition(E[] list, int start, int end) {
E temp;
E partitionElement = list[start];
int leftIndex = start;
int rightIndex = end - 1;
while (leftIndex < rightIndex) {
while (list[leftIndex].compareTo(partitionElement) <= 0 && leftIndex < rightIndex) {
leftIndex++;
}
while (list[rightIndex].compareTo(partitionElement) > 0) {
rightIndex--;
}
if (leftIndex < rightIndex) {
temp = list[leftIndex];
list[leftIndex] = list[rightIndex];
list[rightIndex] = temp;
}
}
list[start] = list[rightIndex];
list[rightIndex] = partitionElement;
return rightIndex;
}}
java system.compare.CompareApp
1
3
2

binary search, count duplicates

I have sorted array
{1,2,3,5,5,5,7,8,8}
I would like to count how many times the number that i am sending is found in the array in longn only.
for example:
public static int count(int[] array,5)
will reply 3
public static int count(int[] array,8)
will reply 2
so my plan is:
1) to do a binary search to find the number
2) binary search the top border index and the bottom border index.
3) print (top index - bottom index) will give me the time of target number in the array.
Is my code is logn ?
Please help! :)
public class binarySearch
{
public static void main(String[]args)
{
System.out.println("d");
int[]data={1,1,2,3,1,1,1};
System.out.println(count(data,1));
}
public static int count(int[] a, int x)
{
int low=0;
int high = a.length-1;
int count=0;
while(low <=high)
{
int mid=((low+high)/2);
if(x>a[mid])
low=mid+1;
if(x<a[mid])
high=mid-1;
if(x==a[mid])
{
int top=findTopIndex(a,x,mid);
int bottom=findBottomIndex(a,x,mid);
return (top-bottom);
}
}
return 111111111;
}
public static int findTopIndex(int[] a, int x, int index)
{
int low=index;
int high = a.length-1;
int mid;
if(x==a[high])
return high;
while(low <= high)
{
mid=((low+high)/2);
if(x<a[mid]&&x==a[mid-1])
return mid-1;
else if(x==a[mid])
low=mid+1;
else if(a[mid]>x && a[mid-1]!=x)
high=mid-1;
}
return 11111111;
}
public static int findBottomIndex(int[] a, int x, int index)
{
int low=0;
int high = index-1;
int mid;
if(x==a[low])
return low-1;
while(low <= high)
{
mid=((low+high)/2);
if(x>a[mid]&&x==a[mid+1])
return mid;
else if(x==a[mid])
high=mid-1;
else if(a[mid]<x && a[mid+1]!=x)
low=mid+1;
}
return 111;
}
}
What you have written is really close to the solution you need. You first do a binary search to find a single instance of the number you are searching for(let's say its found on position index) and then you do two more binary searches -one for the sequence 0, index, and one for index, size to find up to where in both sequences is the number found.
So I suggest you simply pass an index to both findTopIndex and findBottomIndex and make use of it. I can write the whole solution but it will be better for you to come to it on your own.

Finding the largest positive int in an array by recursion

I decided to implement a very simple program recursively, to see how well Java handles recursion*, and came up a bit short. This is what I ended up writing:
public class largestInIntArray {
public static void main(String[] args)
{
// These three lines just set up an array of ints:
int[] ints = new int[100];
java.util.Random r = new java.util.Random();
for(int i = 0; i < 100; i++) ints[i] = r.nextInt();
System.out.print("Normal:"+normal(ints,-1)+" Recursive:"+recursive(ints,-1));
}
private static int normal(int[] input, int largest) {
for(int i : input)
if(i > largest) largest = i;
return largest;
}
private static int recursive(int[] ints, int largest) {
if(ints.length == 1)
return ints[0] > largest ? ints[0] : largest;
int[] newints = new int[ints.length - 1];
System.arraycopy(ints, 1, newints, 0, ints.length - 1);
return recursive(newints, ints[0] > largest ? ints[0] : largest);
}
}
And that works fine, but as it's a bit ugly I wondered if there was a better way. If anyone has any thoughts/alternatives/syntactic sugar to share, that'd be much appreciated!
P.s. If you say "use Lisp" you win nothing (but respect). I want to know if this can be made to look nice in Java.
*and how well I handle recursion
Here's how I might make the recursive method look nicer:
private static int recursive(int[] ints, int largest, int start) {
if (start == ints.length) {
return largest;
}
return recursive(ints, Math.max(ints[start], largest), start + 1);
}
This avoids the expensive array copy, and works for an empty input array. You may implement an additional overloaded method with only two parameters for the same signature as the iterative function:
private static int recursive(int[] ints, int largest) {
return recursive(ints, largest, 0);
}
2 improvements:
no copy of the array (just using the offset)
no need to give the current max
private static int recursive(int[] ints, int offset) {
if (ints.length - 1 == offset) {
return ints[offset];
} else {
return Math.max(ints[offset], recursive(ints, offset + 1));
}
}
Start the recursion with recursive(ints, 0).
You could pass the current index as a parameter rather than copying almost the entire array each time or you could use a divide and conquer approach.
public static int max(int[] numbers) {
int size = numbers.length;
return max(numbers, size-1, numbers[size-1]);
}
public static int max(int[] numbers, int index, int largest) {
largest = Math.max(largest, numbers[index]);
return index > 0 ? max(numbers, index-1, largest) : largest;
}
... to see how well Java handles recursion
The simple answer is that Java doesn't handle recursion well. Specifically, Sun java compilers and Hotspot JVMs do not implement tail call recursion optimization, so recursion intensive algorithms can easily consume a lot of stack space.
However, I have seen articles that say that IBM's JVMs do support this optimization. And I saw an email from some non-Sun guy who said he was adding it as an experimental Hotspot extension as a thesis project.
Here's a slight variation showing how Linked Lists are often a little nicer for recursion, where "nicer" means "less parameters in method signature"
private static int recursive(LinkedList<Integer> list) {
if (list.size() == 1){
return list.removeFirst();
}
return Math.max(list.removeFirst(),recursive(list));
}
Your recursive code uses System.arrayCopy, but your iterative code doesn't do this, so your microbenchmark isn't going to be accurate. As others have mentioned, you can clean up that code by using Math.min and using an array index instead of the queue-like approach you had.
public class Maximum
{
/**
* Just adapted the iterative approach of finding maximum and formed a recursive function
*/
public static int max(int[] arr,int n,int m)
{
if(m < arr[n])
{
m = arr[n];
return max(arr,n - 1,m);
}
return m;
}
public static void main(String[] args)
{
int[] arr = {1,2,3,4,5,10,203,2,244,245,1000,55000,2223};
int max1 = max(arr,arr.length-1,arr[0]);
System.out.println("Max: "+ max1);
}
}
I actually have a pre made class that I setup for finding the largest integer of any set of values. You can put this class into your project and simply use it in any class like so:
System.out.println(figures.getLargest(8,6,12,9,120));
This would return the value "120" and place it in the output. Here is the methods source code if you are interested in using it:
public class figures {
public static int getLargest(int...f) {
int[] score = new int[f.length];
int largest=0;
for(int x=0;x<f.length;x++) {
for(int z=0;z<f.length;z++) {
if(f[x]>=f[z]) {
score[x]++;
}else if(f[x]<f[z]) {
}else {
continue;
}
if(z>=f.length) {
z=0;
break;
}
}
}
for(int fg=0;fg<f.length;fg++) {
if(score[fg]==f.length) {
largest = f[fg];
}
}
return largest;
}
}
The following is a sample code given by my Java instructor, Professor Penn Wu, in one of his lectures. Hope it helps.
import java.util.Random;
public class Recursion
{
static int s = 0;
public static Double max(Double[] d, int n, Double max)
{
if (n==0) { return max;}
else
{
if (d[n] > max)
{
max = d[n];
}
return max(d, n-1, max);
}
}
public static void main(String[] args)
{
Random rn = new Random();
Double[] d = new Double[15];
for (int i=0; i
{
d[i] = rn.nextDouble();
System.out.println(d[i]);
}
System.out.print("\nMax: " + max(d, d.length-1, d[0]));
}
}
Here is my alternative
public class recursion
{
public static int max( int[] n, int index )
{
if(index == n.length-1) // If it's simple, solve it immediately:
return n[index]; // when there's only one number, return it
if(max(n, index+1) > n [index]) // is one number bigger than n?
return max(n, index+1); // return the rest, which contains that bigger number
return n[index]; // if not, return n which must be the biggest number then
}
public static void main(String[] args)
{
int[] n = {100, 3, 5, 1, 2, 10, 2, 15, -1, 20, -1203}; // just some numbers for testing
System.out.println(max(n,0));
}
}

Categories

Resources