Need help streamlining my Merge Sort implementation [closed] - java

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I am building a class of sortable ArrayLists which extends ArrayList. The goal is to be able to call a sort method on a SortDoubleArray, and have that array be sorted via the method described. I got Quicksort, Insertion Sort, Bubble Sort, and Selection Sort all working as I want. I am having some difficulty with Merge Sort, however.
The sort works, but due to the way the recursion involved is working, I am forced reset the contents of the list to be the method applied to itself.
First, here is the tester class. It shows how the other sorts are being implemented. If I did a poor job explaining my issue, hopefully you will see the difference in how the mergeSort() method must be used.
public class SortTester
{
/**
* #param args
*/
public static void main(String[] args)
{
SortDoubleArray list = new SortDoubleArray();
// Code to fill an array with random values.
//list.quickSort();
//list.insertionSort();
//list.selectionSort();
//list.bubbleSort();
list = list.mergeSort();
// Code to print the sorted array.
}
}
Next, here is the SortDoubleArray class. All of the other sorts but insertionSort (to serve as an example of one working the way I want) have been removed for brevity.
public class SortDoubleArray extends ArrayList<Double>
{ // Start of class.
private static final long serialVersionUID = 1271821028912510404L;
/**
* Progresses through the elements one at a time inserting them in their proper place
* via swaps.
*/
public void insertionSort()
{ // Start of insertionSort.
int current = 1;
while (current < size())
{
int i = current;
boolean placeFound = false;
while(i > 0 && !placeFound)
{
if (get(i) < get(i - 1))
{
double temp = get(i);
set(i, get(i - 1));
set(i - 1, temp);
i -= 1;
}
else
{
placeFound = true;
}
}
current += 1;
}
} // End of insertionSort.
/**
* Triggers the recursive mSort method.
* #return
*/
public SortDoubleArray mergeSort()
{ // start of mergeSort.
return mSort(this);
} // End of mergeSort.
/**
* Separates the values each into their own array.
*/
private SortDoubleArray mSort(SortDoubleArray list)
{ // Start of mSort.
if (list.size() <= 1)
{
return list;
}
SortDoubleArray left = new SortDoubleArray();
SortDoubleArray right = new SortDoubleArray();
int middle = list.size() / 2;
for (int i = 0; i < middle; i += 1)
{
left.add(list.get(i));
}
for (int j = middle; j < list.size(); j += 1)
{
right.add(list.get(j));
}
left = mSort(left);
right = mSort(right);
return merge(left, right);
} // End of mSort.
/**
* Merges the separated values back together in order.
*/
private SortDoubleArray merge(SortDoubleArray left, SortDoubleArray right)
{ // Start of merge.
SortDoubleArray result = new SortDoubleArray();
while (left.size() > 0 || right.size() > 0)
{
if (left.size() > 0 && right.size() > 0)
{
if (left.get(0) <= right.get(0))
{
result.add(left.get(0));
left.remove(0);
}
else
{
result.add(right.get(0));
right.remove(0);
}
}
else if (left.size() > 0)
{
result.add(left.get(0));
left.remove(0);
}
else if (right.size() > 0)
{
result.add(right.get(0));
right.remove(0);
}
}
return result;
} // End of merge.
} // End of class.
Please give me some ideas on how I can alter the mergeSort() / mSort() functions within the SortDoubleArray class to have the same implementation as the rest of the sorts.
Thank you!

Given that mSort and merge methods are correct, how about this ?
public void mergeSort()
{ // start of mergeSort.
SortDoubleArray result = mSort(this);
clear();
addAll(result);
} // End of mergeSort.
The relevant line in your test would then be:
list.mergeSort();
Good luck!

Currently your mergeSort() and merge() functions are each creating new SortedDoubleArray objects. Ideally you would do everything in-place without creating new arrays, the amount your creating and copying will create quite a performance hit for your algorithm.
So your methods would have prototypes something like this:
private SortDoubleArray mSort(SortDoubleArray list, int startIndex, int length)
private SortDoubleArray merge(SortDoubleArray list,
int leftIndex, int leftlength,
int rightIndex, int rightlength)
Then use ArrayList.set and .get with a temporary variable to do the swapping in-place. This will mean you're only working on a single array and not creating any new unnecessary ones.
Does this help? Let me know if I understood the issue or you need more explanation.
Note that int endIndex can also work instead of int length

Related

sorting lists in ascending order, with a System out of bound error

My code sorts lists in ascending order when the length of a list is 3, the code works perfectly, however, when the length of a list increases and the number of inserted variables increase, the output is either System out of bound or it doesn't sort it correctly.
public class Lists {
int [] lists;
int itemcount;
public Lists(int l) {
lists =new int[l];
itemcount=0;
}
public void insert(int x) {
if(itemcount==0) {
lists[itemcount]=x;
itemcount++;
}
else {
for(int i=itemcount-1;i>=0;i--) {
if(lists[i]>x) {
lists[i+1]=lists[i];
lists[i]=x;
itemcount++;
}
else {
lists[i+1]=x;
itemcount++;
}
}
}
}
public static void main(String[]args) {
Lists s=new Lists(5);
s.insert(9);
s.insert(4);
s.insert(6);
s.insert(5);
s.insert(8);
for (int i=0;i<s.lists.length;i++) {
System.out.print(s.lists[i]);
}
}
}
This is insertion sort, each time we insert an item to the already sorted list. All the elements greater than the item is moved ahead. So we need to stop at the right position so it require additional condition in the iteration loop.
see how it works , https://en.wikipedia.org/wiki/File:Insertion-sort-example-300px.gif
public void insert(int x) {
int i;/*Insertion position */
for(i=itemcount-1;i>=0 && lists[i]>x;i--) {
lists[i+1]=lists[i];
}
lists[i+1]=x;
itemcount++;
}
The "sorting" part does not seem to be correct, itemcount is incremented in loop several times instead of 1 when only 1 element is inserted.
Thus, the index should be detected where the new item has to be inserted, then the elements shoulds be shifted to the end of lists array.
Also, a case needs to be addressed when there is an attempt to insert more elements than lists.length: print a message and ignore the attempt, throw an exception, or increase the size of lists (e.g. using Arrays.copyOf)
Linear search of the id is as follows as the size of lists is relatively small (if the lists size is large enough, a binary search should be used)
public void insert(int x) {
if (itemcount == lists.length) {
lists = Arrays.copyOf(lists, lists.length * 2);
}
int id = 0;
for (; id < itemcount; id++) {
if (x <= lists[id]) {
break; // insert in id
}
}
for (int i = itemcount; i > id; i--) {
lists[i] = lists[i - 1];
}
lists[id] = x;
itemcount++;
}
Also, when printing the contents of lists it may better to use itemcount as a limit, unless it's needed to show 0's as empty elements.
So, the output for the following test setup:
Lists s = new Lists(5);
s.insert(9); s.insert(4); s.insert(6);
s.insert(5); s.insert(8); s.insert(5);
s.insert(2);
for (int i=0;i<s.itemcount;i++) {
System.out.print(s.lists[i] + " ");
}
Output:
2 4 5 5 6 8 9

Problem with Implementing Merge Sort using an ArrayList

When I execute the following code, for some reason I get
java.util.ConcurrentModificationException
I have tried researching this exception and I believe it happens because the list is being continually edited while I am trying to access it yet again.
This is really frustrating because when I instead of using ArrayLists, used regular arrays, everything seemed to work fine, so I'm not exactly sure how I can go around using the same procedure just with array lists and get it to work.
Here's the code:
public static void mergeSort(List<Integer> indexList, int listLen) {
if (listLen < 2) {
// calls merge method when 1 term is in either left or right arrays
return;
}
int middlepoint = listLen / 2;
List<Integer> leftArr = indexList.subList(0, middlepoint);
List<Integer> rightArr = indexList.subList(middlepoint, listLen);
// passing the numList to the merge (once all numbers are in groups of 1)
merge(indexList, leftArr, rightArr, middlepoint, listLen - middlepoint);
}
public static void merge(
List<Integer> numList, List<Integer> leftArr, List<Integer> rightArr, int left, int right) {
// while there are terms in both lists
int i = 0, j = 0, k = 0;
// while numbers in both lists
while (i < left && j < right) {
int leftVal = leftArr.get(i);
int rightVal = rightArr.get(j);
// if the term in the right array is bigger/equal (filling the final list smallest to greatest)
if (leftVal <= rightVal) {
numList.add(k++, leftVal);
i++;
}
else {
numList.add(k++, rightVal);
j++;
}
while (i < left) {
numList.add(k++, leftVal);
i++;
}
while (j < right) {
numList.add(k++, rightVal);
j++;
}
}
}
You have used subList to divide and traverse the List. Arraylist doesn't allow you to modify the values when you are in middle of traversal and throws an Concurrent Modification Exception.
One way of solving this issue is to remove the dependency on subList method and update your recursive method to take List, startIndex and endIndex.
The other way to work around is to use a Thread safe implementation of List. You can take this route if you can change your list data structure.
Hope this helps.

How to use selection sort with comparator in java?

help here will be greatly appreciated. I'm pretty stuck. What I have to do is I have to use the selection sort algorithm to sort an arraylist, but there are multiple indexes in each object in the arraylist. This has to be done in java.
For example:
public class a {
private int number;
private String letter;
public a(int n, String l)
{
number = n;
letter = l;
}
}
public class SortingArrays {
private ArrayList<a> myarray;
private Comparator<a> sortByNumber;
private Comparator<a> sortByLetter;
public FootballPlayerData() {
myarray = new ArrayList<a>();
getmyarray().add(new a(2, "A"));
getmyarray().add(new a(7, "J"));
//COMPARATORs//
sortByNumber = new Comparator<a>() {
#Override
public int compare(a o1, a o2)
{
if (o1.number < (o2.number)) {
return -1;
}
if (o1.number == (o2.number)) {
return 0;
}
return 1;
}
};
sortByLetter = new Comparator<a>() {
#Override
public int compare(a o1, a o2)
{
return o1.letter.compareTo(o2.letter);
}
};
public void selectionSortbyNumber
{
???
}
public void selectionSortbyLetter
{
???
}
}
So how do I create a selection sort in java (has to be selection sort) that sorts the arraylist by different elements within the objects? I already have the comparator part down, but I don't know how to incorporate that with selection sort.
A Comparator implementation is usually used to compare two elements with one another, returning -1 (or any negative number) if the first element is less than the second, 0 if they are equal, and 1 (or any positive number) if the first element is greater than the second. This can be used to compare two elements to see if one is greater, less than, or equal to the other.
In the context of selection sort, you can use a supplied comparator to determine which value in the unsorted portion of the list is the minimum. The general algorithm for selection sort is as follows:
for i from 0 to array.length:
current_minimum_index = i
for j from i + 1 to array.length:
if array at j is less than array at current_minimum_index:
current_minimum_index = j
swap array at current_minimum_index with array at i
The if array at j is less than array at current_minimum_index can be implemented using Comparator. For example, given a supplied ArrayList called array, the call to the Comparator object named comparator would be:
if (comparator.compare(array.get(j), array.get(current_minimum_index))) < 0)
I do not want to provide you with the complete answer, as that would not help you learn selection sorting, but the method signature for your sorting would resemble the following:
public <T> void selectionSort(ArrayList<T> array, Comparator<T> comparator) {
// for i from 0 to array.size():
// currentMinIndex = i
// for j from i + 1 to array.size():
if (comparator.compare(array.get(j), array.get(currentMinIndex))) < 0) {
// currentMinIndex = j
}
// swap array at currentMinIndex with array at i
}
Your call to this method would then look like one of the following:
selectionSort(myarray, sortByNumber);
selectionSort(myarray, sortByLetter);
Source from https://en.wikipedia.org/wiki/Selection_sort:
/* a[0] to a[n-1] is the array to sort */
int i,j;
int n;
/* advance the position through the entire array */
/* (could do j < n-1 because single element is also min element) */
int iMin;
for (j = 0; j < n-1; j++) {
/* find the min element in the unsorted a[j .. n-1] */
/* assume the min is the first element */
iMin = j;
/* test against elements after j to find the smallest */
for (i = j+1; i < n; i++) {
/* if this element is less, then it is the new minimum */
if (a[i] < a[iMin]) {
/* found new minimum; remember its index */
iMin = i;
}
}
}
if (iMin != j) {
swap(a[j], a[iMin]);
}
The method swap() just switch the values in the array.
Your job will be to swap the array with list. :P But that's not so hard because you can access the list value by index get(int index) method.

Compressing a list of intervals

I need to compress a list of intervals into a smaller list. Let me explain:
For example I have a list containing intervals [1,4],[2,5],[5,7],[10,11],[13,20],[19,21] and i want to join the intersecting intervals and return a list [1,7],[10,11],[13,21] that transforming intersecting intervals into a single longer interval.
For this I wrote this method:
public List compress(List<Interval> intervals) {
for (int j = 0; j < intervals.size(); j++) {
Interval a = intervals.get(j);
int aIndex = j;
for (int i = 1 + aIndex; i < intervals.size(); i++) {
Interval b = intervals.get(i);
if (a.intersects(b)) {
//the union method return a union of two intervals. For example returns [1,7] for [1,4] and [2,5]
intervals.add(j, a.union(b));
intervals.remove(j+1);
intervals.remove(i);
}
}
}
return intervals;
}
This seems to work fine for the first pair of intervals that are checked but it stops there. That is the final output is a list containing [1, 5],[5, 7],[10, 11],[13, 20],[19, 21].
I have found that this may be a problem with illegal removing of elements from a list? https://codereview.stackexchange.com/questions/64011/removing-elements-on-a-list-while-iterating-through-it?newreg=cc3f30e670e24cc2b05cd1fa2492906f
But I have no idea how to get around this.
Please can anyone give me a hint.
Notice: Sorry if I did anything wrong as this is my first post to stackoverflow. And thanks to anyone that will try to help.
UPDATE:
Here is the solution I found after Maraboc proposed to create a copy of the list and manipulate that one.
That seems to work.
public List compress(List<Interval> intervals) {
List<Interval> man = intervals;
for (int j = 0; j < intervals.size(); j++) {
Interval a = intervals.get(j);
int aIndex = j;
for (int i = 1 + aIndex; i < intervals.size(); i++) {
Interval b = intervals.get(i);
if (a.intersects(b)) {
a = a.union(b);
man.add(j,a);
man.remove(j+1);
man.remove(i);
i--;
}
}
}
return intervals;
}
Thank you everyone.
You are actually NOT using iterator, you are using for-cycles and select elements from list based on their position, therefore you do not have to be afraid of "I am not able to remove while iterating" issue.
I posted this question first to stackexchange by mistake. They redirected me to this place and the question was put on hold. But before that happened Maraboc[a link](https://codereview.stackexchange.com/users/87685/maraboc
)
Helped with an idea. He told me to create a new list and modify that one. I did that and it seems to work. The updated solution will be in the updated question.
Just for the fun of it I took an existing Interval Tree and added a minimise method that seems to work nicely.
/**
* Title: IntervlTree
*
* Description: Implements a static Interval Tree. i.e. adding and removal are not possible.
*
* This implementation uses longs to bound the intervals but could just as easily use doubles or any other linear value.
*
* #author OldCurmudgeon
* #version 1.0
* #param <T> - The Intervals to work with.
*/
public class IntervalTree<T extends IntervalTree.Interval> {
// My intervals.
private final List<T> intervals;
// My center value. All my intervals contain this center.
private final long center;
// My interval range.
private final long lBound;
private final long uBound;
// My left tree. All intervals that end below my center.
private final IntervalTree<T> left;
// My right tree. All intervals that start above my center.
private final IntervalTree<T> right;
public IntervalTree(List<T> intervals) {
if (intervals == null) {
throw new NullPointerException();
}
// Initially, my root contains all intervals.
this.intervals = intervals;
// Find my center.
center = findCenter();
/*
* Builds lefts out of all intervals that end below my center.
* Builds rights out of all intervals that start above my center.
* What remains contains all the intervals that contain my center.
*/
// Lefts contains all intervals that end below my center point.
final List<T> lefts = new ArrayList<>();
// Rights contains all intervals that start above my center point.
final List<T> rights = new ArrayList<>();
long uB = Long.MIN_VALUE;
long lB = Long.MAX_VALUE;
for (T i : intervals) {
long start = i.getStart();
long end = i.getEnd();
if (end < center) {
lefts.add(i);
} else if (start > center) {
rights.add(i);
} else {
// One of mine.
lB = Math.min(lB, start);
uB = Math.max(uB, end);
}
}
// Remove all those not mine.
intervals.removeAll(lefts);
intervals.removeAll(rights);
uBound = uB;
lBound = lB;
// Build the subtrees.
left = lefts.size() > 0 ? new IntervalTree<>(lefts) : null;
right = rights.size() > 0 ? new IntervalTree<>(rights) : null;
// Build my ascending and descending arrays.
/**
* #todo Build my ascending and descending arrays.
*/
}
/*
* Returns a list of all intervals containing the point.
*/
List<T> query(long point) {
// Check my range.
if (point >= lBound) {
if (point <= uBound) {
// Gather all intersecting ones.
List<T> found = intervals
.stream()
.filter((i) -> (i.getStart() <= point && point <= i.getEnd()))
.collect(Collectors.toList());
// Gather others.
if (point < center && left != null) {
found.addAll(left.query(point));
}
if (point > center && right != null) {
found.addAll(right.query(point));
}
return found;
} else {
// To right.
return right != null ? right.query(point) : Collections.<T>emptyList();
}
} else {
// To left.
return left != null ? left.query(point) : Collections.<T>emptyList();
}
}
/**
* Blends the two lists together.
*
* If the ends touch then make them one.
*
* #param a
* #param b
* #return
*/
static List<Interval> blend(List<Interval> a, List<Interval> b) {
// Either empty - lreturn the other.
if (a.isEmpty()) {
return b;
}
if (b.isEmpty()) {
return a;
}
Interval aEnd = a.get(a.size() - 1);
Interval bStart = b.get(0);
ArrayList<Interval> blended = new ArrayList<>();
// Do they meet?
if (aEnd.getEnd() >= bStart.getStart() - 1) {
// Yes! merge them.
// Remove the last.
blended.addAll(a.subList(0, a.size() - 1));
// Add a combined one.
blended.add(new SimpleInterval(aEnd.getStart(), bStart.getEnd()));
// Add all but the first.
blended.addAll(b.subList(1, b.size()));
} else {
// Just join them.
blended.addAll(a);
blended.addAll(b);
}
return blended;
}
static List<Interval> blend(List<Interval> a, List<Interval> b, List<Interval>... more) {
List<Interval> blended = blend(a, b);
for (List<Interval> l : more) {
blended = blend(blended, l);
}
return blended;
}
List<Interval> minimise() {
// Calculate min of left and right.
List<Interval> minLeft = left != null ? left.minimise() : Collections.EMPTY_LIST;
List<Interval> minRight = right != null ? right.minimise() : Collections.EMPTY_LIST;
// My contribution.
long meLeft = minLeft.isEmpty() ? lBound : Math.max(lBound, minLeft.get(minLeft.size() - 1).getEnd());
long meRight = minRight.isEmpty() ? uBound : Math.min(uBound, minRight.get(0).getEnd());
return blend(minLeft,
Collections.singletonList(new SimpleInterval(meLeft, meRight)),
minRight);
}
private long findCenter() {
//return average();
return median();
}
protected long median() {
if (intervals.isEmpty()) {
return 0;
}
// Choose the median of all centers. Could choose just ends etc or anything.
long[] points = new long[intervals.size()];
int x = 0;
for (T i : intervals) {
// Take the mid point.
points[x++] = (i.getStart() + i.getEnd()) / 2;
}
Arrays.sort(points);
return points[points.length / 2];
}
/*
* What an interval looks like.
*/
public interface Interval {
public long getStart();
public long getEnd();
}
/*
* A simple implemementation of an interval.
*/
public static class SimpleInterval implements Interval {
private final long start;
private final long end;
public SimpleInterval(long start, long end) {
this.start = start;
this.end = end;
}
#Override
public long getStart() {
return start;
}
#Override
public long getEnd() {
return end;
}
#Override
public String toString() {
return "{" + start + "," + end + "}";
}
}
/**
* Test code.
*
* #param args
*/
public static void main(String[] args) {
/**
* #todo Needs MUCH more rigorous testing.
*/
// Test data.
long[][] data = {
{1, 4}, {2, 5}, {5, 7}, {10, 11}, {13, 20}, {19, 21},};
List<Interval> intervals = new ArrayList<>();
for (long[] pair : data) {
intervals.add(new SimpleInterval(pair[0], pair[1]));
}
// Build it.
IntervalTree<Interval> test = new IntervalTree<>(intervals);
// Check minimise.
List<Interval> min = test.minimise();
System.out.println("Minimise test: ---");
System.out.println(min);
}
}
For your algorithm to work, the intervals must be sorted, say by start.
Then the for-i loop can make a the longest possible interval.
if (a.intersects(b)) {
a = a.union(b);
intervals.remove(i);
--i; // So we remain at old i value.
}
} // for i
intervals.set(j, a);
The reason for these requirements is that intervals A, B, C might form one long interval ABC, whereas C. B, A might.
Indeed the problem is that when you remove an element from the list, then all subsequent elements will be shifted. At around j I'm guessing it doesn't change because you insert then remove an item at the same location. But the removal at position i will shift all elements in the list.
What you could be doing, instead of removing the elements, is to put a null value at that position, so that the indices remain the same. You will then have to perform a final pass to remove null elements from the array (and check for nulls before comparing).
You could also run your inner loop backwards (from max i down to j) so that any element that gets shifted after i has already been processed.

Insertion sort sorting an ArrayList problems

now I have been at this for a while and there is an error I am having. Now the program I am making is an address book, and I am using an insertion sort to sort an arraylist of objects which I call books(address entries). Now I soon discovered that my sorter is not sorting properly so I made a simple program to test the sorter and again it does not work. I was wondering if you guys could have a look at it and help me out.
Here is my Sorter:
import java.util.ArrayList;
public class Sorts {
/**
* Sorts and array of integer from low to high
* pre: none
* post: Integers has been sorted from low to high
*/
public static void insertionSort(ArrayList<String> test) {
Comparable temp;
int previousIndex;
ArrayList<String> objectSort = test;
for (int i = 1; i < objectSort.size(); i++) {
temp = objectSort.get(i);
previousIndex = i - 1;
while ((objectSort.get(previousIndex).compareTo((String) temp)) == 1 && (previousIndex > 0)) {
objectSort.set(previousIndex + 1, objectSort.get(previousIndex));
previousIndex -= 1; //decrease index to compare current item with next previous item
}
if (objectSort.get(previousIndex).compareTo((String) temp) == 1) {
/* shift item in first element up into next element */
objectSort.set(previousIndex + 1, objectSort.get(previousIndex));
/* place current item at index 0 (first element */
objectSort.set(previousIndex, (String) temp);
} else {
/* place current item at index ahead of previous item */
objectSort.set(previousIndex + 1, (String) temp);
}
}
}
}
My simple program to test it is:
import java.util.ArrayList;
public class Main {
public static void main(String[] args){
ArrayList<String> test = new ArrayList<String>();
test.add("Roxy");
test.add("Proxy");
test.add("Moxy");
test.add("Samuel Adams");
Sorts.insertionSort(test);
System.out.println(test);
}
}
To sum it up, I am having troubles with my ArrayList sorter. The problem is it wont sort correctly and I do not know why. Thank you so much in advance. If you have any questions feel free to ask. :)
First problem: you're expecting compareTo to always return 1 for "greater than". It just returns a value greater than 0 which may be a different positive integer. So both your == 1 comparisons should be > 0.
There may be other problems, but that's the first one I'd look at.

Categories

Resources