I've been trying to make a merge sort and I got the merging part down, it's just the recursive splitting that I'm having a little trouble with. The left and right lists are getting merged and sorted individually and not carrying over between each recursive pass. I'm not sure what I'm doing wrong with the recursion or how to fix it without scrapping the entire division method.
public static int[] mergeSort(int[] x)
{
divide(x);
return sorted;
}
public static void divide(int[] x)
{
int midP;
if((x.length/2f) == 1.5f) //the left side of the list will always be larger
midP = 2;
else
midP = x.length/2;
if(midP == 0) //if the list contains one number end
return;
System.out.println("mid: " + midP);
int[] left = new int[midP];
int[] right = new int[x.length - midP];
for(int i = 0; i < midP; i++) //fills the left list
left[i] = x[i];
for(int i = midP; i < x.length; i++) //fills the right list
right[i-midP] = x[i];
divide(left);
divide(right);
sorted = merge(left, right);
}
public static int[] merge(int[] x, int[] y)
{
int[] mergedList = new int[x.length + y.length];
int counter = 0, xCounter = 0, yCounter = 0, high = 0;
while(xCounter < x.length && yCounter < y.length)
{
printArray(x);
printArray(y);
System.out.println("checking: " + x[xCounter] + " " + y[yCounter]);
if(x[xCounter] < y[yCounter])
{
mergedList[counter] = x[xCounter];
high = y[yCounter];
if(xCounter != x.length)
xCounter++;
}
else
{
mergedList[counter] = y[yCounter];
high = x[xCounter];
if(yCounter != y.length)
yCounter++;
}
counter++;
}
mergedList[counter] = high;
return mergedList;
}
public static void printArray(int[] x)
{
System.out.print("list: ");
for(int i = 0; i < x.length; i++)
System.out.print(x[i] + " ");
System.out.println();
}
When using recursive methods, it's tricky to use static or instance variables like sorted in this case. What's happening is that sorted gets set and reset over the recursive calls, and it can be difficult to predict what its value will be at any given time. Recursive functions are easier to understand if you only use local variables. So change your divide function so that it returns the sorted array, and use the return value from the recursive calls:
public static int[] divide(int[] x) {
... your existing divide logic ...
int[] leftSorted = divide(left);
int[] rightSorted = divide(right);
return merge(leftSorted, rightSorted);
}
Don't forget to also change the main entry point:
public static int[] mergeSort(int[] x) {
return divide(x);
}
You seem to still have a bug in the merge method:
int[] x = {5, 4, 1, 2, 3};
int[] sorted = mergeSort(x);
results in 1 2 3 4 0
Related
I was learning to merge sort an integer array, when I noticed that while copying the sorted array elements to the original array, we need two separate loop variables to run simultaneously, while the values at those indices are copied to the original array. Here is the code for reference:
class MergeSort {
public static void sort(int arr[], int si, int ei, int mid) {
int merged[] = new int[ei - si + 1];
int index1 = si; // tracks the first array
int index2 = mid + 1; // tracks the second array
int i = 0;
while (index1 <= mid && index2 <= ei) {
if (arr[index1] <= arr[index2]) {
merged[i++] = arr[index1++];
} else {
merged[i++] = arr[index2++];
}
} // end of while
while (index1 <= mid) {
merged[i++] = arr[index1++];
}
while (index2 <= ei) {
merged[i++] = arr[index2++];
}
// to copy merged[] to arr[]
int j = si;
for (i = 0; i < merged.length; i++, j++) {
arr[j] = merged[i];
}
} // end sort()
public static void divide(int arr[], int si, int ei) {
// base case
if (si >= ei) {
return;
} // end of base case
int mid = si + (ei - si) / 2; // same as (ei-si)/2 but with less space complexity
divide(arr, si, mid);
divide(arr, mid + 1, ei);
sort(arr, si, ei, mid);
} // end of divide
public static void main(String args[]) {
int arr[] = { 1, 8, 0, 7, -4 };
int n = arr.length;
divide(arr, 0, n - 1);
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
} // end of for
} // end of main
} // end of class
Notice that while copying the values of the array merged[] to the array arr[], we are using two separate variables i and j. I did try using only one loop variable, which went like:
for (int i = 0; i < arr.length; i++) {
arr[i] = merged[i];
}
but received an incorrect output. If anyone knows why we need two separate variables for the operation, please let me know. Thank you :)
You could use a single variable in this final loop, but you must add the offset of the start of the slice in the destination array:
for (int i = 0; i < arr.length; i++) {
arr[si + i] = merged[i];
}
I have a programming assignment where I have to make a method that takes in an array that is split up into subarrays, and merge them. My method works fine but the Integer array that is passed in 'a' is only changed from inside the method, and remains unchanged from the outside.
After looking around on google, I have concluded that it is impossible to change an array parameter, but then I thought about Arrays.sort which does end up changing the array parameter from outside of the method.
How do I change the array that is passed in as a parameter so that it stays changed outside of the method?
/**
* Merge three sorted arrays with these ranges [lo..mid1], [mid1+1..mid2], [mid2+1..hi] into one sorted array.
* Array a has the original input and final sorted input.
* Array aux is the auxiliary array.
*/
public static void Merge(Integer[] a, int lo, int mid1, int mid2, int hi, Integer[] aux) {
int[] arr1 = copyArray(a, lo, mid1);
int[] arr2 = copyArray(a, mid1+1, mid2);
int[] arr3 = copyArray(a, mid2+1, hi);
int i = 0, j = 0, k = 0, idx = 0;
while(i < arr1.length-1 || j < arr2.length-1 || k < arr3.length-1) {
// arr1[i] is the smallest
if(arr1[i] <= arr2[j] && arr1[i] <= arr3[k]) {
System.out.println("i is bigger at " + arr1[i]);
aux[idx] = arr1[i];
i++;
}
// arr2[j] is the smallest
else if(arr2[j] <= arr1[i] && arr2[j] <= arr3[k]) {
System.out.println("j is bigger at " + arr2[j]);
aux[idx] = arr2[j];
j++;
}
// arr3[k] is the smallest
else if(arr3[k] <= arr2[j] && arr3[k] <= arr1[i]) {
System.out.println("k is bigger at " + arr3[k]);
aux[idx] = arr3[k];
k++;
}
idx++;
}
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
System.out.println(Arrays.toString(arr3));
System.out.println(Arrays.toString(aux));
a = aux;
a[0] = 900;
System.out.println("a from Merge method: " + Arrays.toString(a));
}
You have to iterate over your aux array and assign the value to the 'a' array:
a[i] = aux[i]
Or return your merged array as result.
public static int[] Merge(int[] a, int mid1, ...){
// do something
return a;
}
You can change void to int[][] and then return the arrays
public static int[][] Merge(arguments) {
//Do math
int[][] result = new int[][] {arr1, arr2, arr3};
return result;
}
Then, outside of the method you call It like below:
int[][] integerArray = Merge(arguments);
arr1 = integerArray[0];
arr2 = integerArray[1];
arr3 = integerArray[2];
I'm having trouble combining these two algorithms together. I've been asked to modify Binary Search to return the index that an element should be inserted into an array. I've been then asked to implement a Binary Insertion Sort that uses my Binary Search to sort an array of randomly generated ints.
My Binary Search works the way it's supposed to, returning the correct index whenever I test it alone. I wrote out Binary Insertion Sort to get a feel for how it works, and got that to work as well. As soon as I combine the two together, it breaks. I know I'm implementing them incorrectly together, but I'm not sure where my problem lays.
Here's what I've got:
public class Assignment3
{
public static void main(String[] args)
{
int[] binary = { 1, 7, 4, 9, 10, 2, 6, 12, 3, 8, 5 };
ModifiedBinaryInsertionSort(binary);
}
static int ModifiedBinarySearch(int[] theArray, int theElement)
{
int leftIndex = 0;
int rightIndex = theArray.length - 1;
int middleIndex = 0;
while(leftIndex <= rightIndex)
{
middleIndex = (leftIndex + rightIndex) / 2;
if (theElement == theArray[middleIndex])
return middleIndex;
else if (theElement < theArray[middleIndex])
rightIndex = middleIndex - 1;
else
leftIndex = middleIndex + 1;
}
return middleIndex - 1;
}
static void ModifiedBinaryInsertionSort(int[] theArray)
{
int i = 0;
int[] returnArray = new int[theArray.length + 1];
for(i = 0; i < theArray.length; i++)
{
returnArray[ModifiedBinarySearch(theArray, theArray[i])] = theArray[i];
}
for(i = 0; i < theArray.length; i++)
{
System.out.print(returnArray[i] + " ");
}
}
}
The return value I get for this when I run it is 1 0 0 0 0 2 0 0 3 5 12. Any suggestions?
UPDATE: updated ModifiedBinaryInsertionSort
static void ModifiedBinaryInsertionSort(int[] theArray)
{
int index = 0;
int element = 0;
int[] returnArray = new int[theArray.length];
for (int i = 1; i < theArray.lenght - 1; i++)
{
element = theArray[i];
index = ModifiedBinarySearch(theArray, 0, i, element);
returnArray[i] = element;
while (index >= 0 && theArray[index] > element)
{
theArray[index + 1] = theArray[index];
index = index - 1;
}
returnArray[index + 1] = element;
}
}
Here is my method to sort an array of integers using binary search.
It modifies the array that is passed as argument.
public static void binaryInsertionSort(int[] a) {
if (a.length < 2)
return;
for (int i = 1; i < a.length; i++) {
int lowIndex = 0;
int highIndex = i;
int b = a[i];
//while loop for binary search
while(lowIndex < highIndex) {
int middle = lowIndex + (highIndex - lowIndex)/2; //avoid int overflow
if (b >= a[middle]) {
lowIndex = middle+1;
}
else {
highIndex = middle;
}
}
//replace elements of array
System.arraycopy(a, lowIndex, a, lowIndex+1, i-lowIndex);
a[lowIndex] = b;
}
}
How an insertion sort works is, it creates a new empty array B and, for each element in the unsorted array A, it binary searches into the section of B that has been built so far (From left to right), shifts all elements to the right of the location in B it choose one right and inserts the element in. So you are building up an at-all-times sorted array in B until it is the full size of B and contains everything in A.
Two things:
One, the binary search should be able to take an int startOfArray and an int endOfArray, and it will only binary search between those two points. This allows you to make it consider only the part of array B that is actually the sorted array.
Two, before inserting, you must move all elements one to the right before inserting into the gap you've made.
I realize this is old, but the answer to the question is that, perhaps a little unintuitively, "Middleindex - 1" will not be your insertion index in all cases.
If you run through a few cases on paper the problem should become apparent.
I have an extension method that solves this problem. To apply it to your situation, you would iterate through the existing list, inserting into an empty starting list.
public static void BinaryInsert<TItem, TKey>(this IList<TItem> list, TItem item, Func<TItem, TKey> sortfFunc)
where TKey : IComparable
{
if (list == null)
throw new ArgumentNullException("list");
int min = 0;
int max = list.Count - 1;
int index = 0;
TKey insertKey = sortfFunc(item);
while (min <= max)
{
index = (max + min) >> 1;
TItem value = list[index];
TKey compKey = sortfFunc(value);
int result = compKey.CompareTo(insertKey);
if (result == 0)
break;
if (result > 0)
max = index - 1;
else
min = index + 1;
}
if (index <= 0)
index = 0;
else if (index >= list.Count)
index = list.Count;
else
if (sortfFunc(list[index]).CompareTo(insertKey) < 0)
++index;
list.Insert(index, item);
}
Dude, I think you have some serious problem with your code. Unfortunately, you are missing the fruit (logic) of this algorithm. Your divine goal here is to get the index first, insertion is a cake walk, but index needs some sweat. Please don't see this algorithm unless you gave your best and desperate for it. Never give up, you already know the logic, your goal is to find it in you. Please let me know for any mistakes, discrepancies etc. Happy coding!!
public class Insertion {
private int[] a;
int n;
int c;
public Insertion()
{
a = new int[10];
n=0;
}
int find(int key)
{
int lowerbound = 0;
int upperbound = n-1;
while(true)
{
c = (lowerbound + upperbound)/2;
if(n==0)
return 0;
if(lowerbound>=upperbound)
{
if(a[c]<key)
return c++;
else
return c;
}
if(a[c]>key && a[c-1]<key)
return c;
else if (a[c]<key && a[c+1]>key)
return c++;
else
{
if(a[c]>key)
upperbound = c-1;
else
lowerbound = c+1;
}
}
}
void insert(int key)
{
find(key);
for(int k=n;k>c;k--)
{
a[k]=a[k-1];
}
a[c]=key;
n++;
}
void display()
{
for(int i=0;i<10;i++)
{
System.out.println(a[i]);
}
}
public static void main(String[] args)
{
Insertion i=new Insertion();
i.insert(56);
i.insert(1);
i.insert(78);
i.insert(3);
i.insert(4);
i.insert(200);
i.insert(6);
i.insert(7);
i.insert(1000);
i.insert(9);
i.display();
}
}
So given an array for example [3, 5, 7, 2] I want to use recursion to give me all the possible combinations of sums, for example: 3, 5, 7, 2, 8(3+5),10(3+7),5(3+5)... 15(3+5+7) etc. I'm not exactly sure how to go about this using java.
You have two choice with each number in the array.
Use the number
Don't use the number
void foo(int[] array, int start, int sum) {
if(array.length == start) return;
int val = sum + array[start];
//print val;
foo(array, start + 1, val); //use the number
foo(array, start + 1, sum); //don't use the number
}
the initial call is foo(a, 0, 0)
An recursive algorithm for this could work as follows:
All the sums for a list equals the union of:
The first number plus the sums of the sublist without the first number
The sums of the sublist without the first number
Eventually your recursive call will hit the stopping condition of an empty list, which has only one sum(zero)
Here's one way of doing it in pseudo code:
getAllPossibleSums(list)
if(list.length == 1)
return list[0];
otherSums = getAllPossibleSums(list[1:end])
return union(
otherSums, list[0] + otherSums);
public static void main(String[] args) {
findAllSums(new int[] {3, 5, 7, 2}, 0, 0);
}
static void findAllSums(int[] arrayOfNumbers, int index, int sum) {
if (index == arrayOfNumbers.length) {
System.out.println(sum);
return;
}
findAllSums(arrayOfNumbers, index + 1, sum + arrayOfNumbers[index]);
findAllSums(arrayOfNumbers, index + 1, sum);
}
You have two branches, one in which you add the current number and another in which you don't.
public static void main(String[] args) {
int [] A = {3, 5, 7, 2};
int summation = recursiveSum(A, 0, A.length-1);
System.out.println(summation);
}
static int recursiveSum(int[] Array, int p, int q) {
int mid = (p+q)/2; //Base case
if (p>q) return 0; //Base case
else if (p==q) return Array[p];
**else
return recursiveSum(Array, p, mid) + recursiveSum(Array, mid + 1, q);**
}
Simplest way ever:
private int noOfSet;
Add below method:
private void checkSum() {
List<Integer> input = new ArrayList<>();
input.add(9);
input.add(8);
input.add(10);
input.add(4);
input.add(5);
input.add(7);
input.add(3);
int targetSum = 15;
checkSumRecursive(input, targetSum, new ArrayList<Integer>());
}
private void checkSumRecursive(List<Integer> remaining, int targetSum, List<Integer> listToSum) {
// Sum up partial
int sum = 0;
for (int x : listToSum) {
sum += x;
}
//Check if sum matched with potential
if (sum == targetSum) {
noOfSet++;
Log.i("Set Count", noOfSet + "");
for (int value : listToSum) {
Log.i("Value", value + "");
}
}
//Check sum passed
if (sum >= targetSum)
return;
//Iterate each input character
for (int i = 0; i < remaining.size(); i++) {
// Build list of remaining items to iterate
List<Integer> newRemaining = new ArrayList<>();
for (int j = i + 1; j < remaining.size(); j++)
newRemaining.add(remaining.get(j));
// Update partial list
List<Integer> newListToSum = new ArrayList<>(listToSum);
int currentItem = remaining.get(i);
newListToSum.add(currentItem);
checkSumRecursive(newRemaining, targetSum, newListToSum);
}
}
Hope this will help you.
I'm having trouble printing out a MergeSort. I need help printing out each step by step process as it's sorting an ArrayList.
The following example is from a InsertionSort, as I had it print at every single time it swapped two elements in the ArrayList:
11 79 60 45 START
11 79 60 45
11 60 79 45
11 45 60 79 FINISH
Is there anyway to do this for MergeSort while showing the entire array from start to finish (like above?)
Code:
import java.util.ArrayList;
public class Merge
{
public static void main (String [] args)
{
Merge run = new Merge();
run.test();
}
public void test ( )
{
ArrayList<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i < 16; i++)
{
numbers.add(new Integer(1 + (int)(Math.random() * 100)));
}
printArray(numbers);
mergeSort(numbers);
printArray(numbers);
}
public void printArray (ArrayList<Integer> array)
{
System.out.println("\n\n");
for (int i = 0; i < array.size(); i++)
{
System.out.printf("%-5d",array.get(i).intValue());
}
System.out.println("\n\n");
}
public void mergeSort (ArrayList<Integer> array)
{
int length = array.size();
if (length < 2)
{
return; // the array is already sorted in this case
}
// divide
ArrayList<Integer> array1 = new ArrayList<Integer>();
ArrayList<Integer> array2 = new ArrayList<Integer>();
int i = 0;
while (i < length/2)
{
array1.add(array.remove(0)); // move the first n/2 elements to array1
i++;
}
while (!array.isEmpty())
{
array2.add(array.remove(0)); // move the rest to array2
}
mergeSort(array1);
mergeSort(array2);
merge(array1,array2,array);
}
public void merge (ArrayList<Integer> array1, ArrayList<Integer> array2, ArrayList<Integer> array)
{
while (!array1.isEmpty() && !array2.isEmpty())
{
if ((array1.get(0).compareTo(array2.get(0)) <= 0))
{
array.add(array1.remove(0));
}
else
{
array.add(array2.remove(0));
}
}
while(!array1.isEmpty()) // move the remaining elements of array1
{
array.add(array1.remove(0));
}
while(!array2.isEmpty()) // move the remaining elements of array2
{
array.add(array2.remove(0));
}
}
}
If you passed some offset to mergeSort, you may be able to print the sub-array indented over to where it would be in the full array as you make swaps, but since you are only passing portions of the array down, you won't be able to show the full array in this manner. However, there is a faster way that would allow you to.
Instead of making new arrays and passing them down, pass the array and 2 indices, the begin and end point. So you say mergeSort(array, 0, n) for the first, then mergeSort(array, 0, n/2) and mergeSort(array, n/2, n) for the recursive calls. You do your splitting and merging only within those bounds. Then as you merge, you can print out the whole merged array. This would show the step at each merge. At the bottom level, it would show the 1-1 swap (if it occurs). That's the only "step-by-step" you could see in a merge sort.
With out seeing your code it's hard to know exactly but I grabbed a Mergesort Implementation from here: http://www.vogella.de/articles/JavaAlgorithmsMergesort/article.html.
I've updated it to print out like you want.
public class Mergesort
{
private int[] numbers;
private int[] helper;
private int number;
public void sort(int[] values)
{
this.numbers = values;
number = values.length;
this.helper = new int[number];
System.out.println("START");
mergesort(0, number - 1);
System.out.println("END");
}
private void mergesort(int low, int high)
{
// Check if low is smaller then high, if not then the array is sorted
if (low < high)
{
// Get the index of the element which is in the middle
int middle = (low + high) / 2;
// Sort the left side of the array
mergesort(low, middle);
// Sort the right side of the array
mergesort(middle + 1, high);
// Combine them both
merge(low, middle, high);
}
}
private void merge(int low, int middle, int high)
{
// Copy both parts into the helper array
for (int i = low; i <= high; i++)
{
helper[i] = numbers[i];
}
int i = low;
int j = middle + 1;
int k = low;
// Copy the smallest values from either the left or the right side back
// to the original array
while (i <= middle && j <= high)
{
if (helper[i] <= helper[j])
{
numbers[k] = helper[i];
i++;
}
else
{
numbers[k] = helper[j];
j++;
}
k++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle)
{
numbers[k] = helper[i];
k++;
i++;
}
}
private void printArray()
{
for(int x : numbers)
System.out.print(x + " ");
System.out.println(" ");
}
}
If you don't want to print to the console you can build the output to a String of the output and return it when you're all done.
Here is a little Merge sort algorithm program. I copied the algorithm from
http://en.wikipedia.org/wiki/Merge_sort. You can just run it as a JUnittest or run the main method.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import junit.framework.TestCase;
/**
* Simple MergeSortTest
*/
public class MergeSortTest extends TestCase {
private static int FIRST_ENTRY = 0;
public static void main(String[] args) {
MergeSortTest mergesorttest = new MergeSortTest();
Integer [] unsortedInt = {1,38, 27, 110, 9, 82, 10, 100, 299, 13};
List<Integer> unsorted = Arrays.asList(unsortedInt);
List<Integer> sorted = mergesorttest.mergeSort(unsorted);
System.out.println(sorted.toString());
}
public void testMergeSort() {
Integer [] unsortedInt = {1,38, 27, 110, 9, 82, 10, 100, 299, 13};
List<Integer> unsorted = Arrays.asList(unsortedInt);
List<Integer> sorted = mergeSort(unsorted);
assertEquals("[1, 9, 10, 13, 27, 38, 82, 100, 110, 299]", sorted.toString());
}
private List<Integer> mergeSort(List<Integer> list) {
List<Integer> result;
List<Integer> left = new ArrayList<Integer>();
List<Integer> right = new ArrayList<Integer>();;
int middle;
int counter;
if (list.size() <= 1) {
return list;
}
middle = list.size() / 2;
for (counter = 0; counter < middle; counter++) {
left.add(list.get(counter));
}
for (counter = middle; counter < list.size(); counter++) {
right.add(list.get(counter));
}
left = mergeSort(left);
right = mergeSort(right);
result = merge(left, right);
System.out.println(result);
return result;
}
private List<Integer> merge(List<Integer> left, List<Integer> right) {
List<Integer> result = new ArrayList<Integer>();
while (!left.isEmpty() || !right.isEmpty()) {
if (!left.isEmpty() && !right.isEmpty()) {
if (left.get(FIRST_ENTRY) <= right.get(FIRST_ENTRY)) {
handle(left, result);
} else {
handle(right, result);
}
} else if (!left.isEmpty()) {
handle(left, result);
} else if (!right.isEmpty()) {
handle(right, result);
}
}
return result;
}
private void handle(List<Integer> list, List<Integer> result) {
if (!list.isEmpty()) {
result.add(list.get(FIRST_ENTRY));
list.remove(FIRST_ENTRY);
}
}
}