Error in java code for merge sort - java

Merge Sort is not a new algorithm and there are solutions available to it. I was trying to write my own code but it has some logical errors.
Any insights about what i am doing wrong?
public class MergeSort {
public static void main(String[] args) {
int[] array = {5,4,8,3,7,10};
mergeSort(array);
System.out.println("Sorted array: \n");
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
public static void mergeSort(int[] array) {
if (array.length > 1) {
int[] firstHalf = new int[array.length / 2];
System.arraycopy(array, 0, firstHalf, 0, array.length / 2);
mergeSort(firstHalf);
int[] secondHalf = new int[array.length - array.length/2];
System.arraycopy(array, array.length / 2, secondHalf, 0, array.length - array.length/2);
mergeSort(secondHalf);
merge(firstHalf, secondHalf, array);
}
}
public static void merge(int[] firstHalf, int[] secondHalf, int[] array) {
int i = 0;
int j = 0;
int k = 0;
for (k = 0; k < array.length; k++) {
for (i = 0, j = 0; i < firstHalf.length && j < secondHalf.length; i++, j++) {
if (firstHalf[i] < secondHalf[j]) {
array[k] = firstHalf[i];
i++;
} else if (secondHalf[j] < firstHalf[i]) {
array[k] = secondHalf[j];
j++;
}
}
}
}
}

There are multiple issues with your merge() function:
The most obvious one, merge() does not need a nested loop. Note
that you repeatidly overriding values of array[k] with new
values.
Another issue is at each iteration of the inner loop you increase one
of i or j twice.
In addition, there are some elements that you never touch, the biggest elements in one of the arrays (consider if you have an already sorted array, so you have left=[1,2,3] right=[4,5,6]. After you figure out you need to increase only one iterator, and not both, you will still have an issue that you never touch the elements 2,3 from left).
My tip: Try to follow a step by step pseudo code when implementing the algorithm for the first time. If you feel you got it, erase it and try writing it again, without the reference.

Related

Getting StackOverFlow error while trying to code Merge Sort in Java

I am new to coding and I was trying to create merge sort algorithm in java. I am getting too many errors and I am not able to figure out the exact mistake
in the code.
I feel my logic is correct but don't know which step(s) is causing the error. Could someone help me rectify the mistakes in the following code.
Thank you
package com.company;
public class MergeSort_Array {
//Method created to print Input Array
public static void printInputArray(int inputArray[]) {
for (int i:inputArray) { //for-each loop
System.out.print(i + " ");
}
System.out.println();
}
//Function created to sort and merge Input Array:
public static void SortArray(int[] A) {
int midpoint = A.length / 2;
int[] left = new int[midpoint];
int[] right;
if (A.length % 2 == 0) {
right = new int[midpoint];
} else {
right = new int[midpoint + 1];
}
//Copying values from super Array to left Array:
for (int i = 0; i < midpoint; i++) {
left[i] = A[i];
}
//Copying elements from super Array to right Array:
for (int j = 0; j < right.length; j++) {
right[j] = A[midpoint + j];
}
//using Recursion
SortArray(left);
SortArray(right);
MergeArray(A, left, right);
}
// Creating a Function to merge left and right arrays.
public static void MergeArray(int[] result, int[] L, int[] R) {
//result array length = length of left array+ right array length
result = new int[L.length + R.length];
int i = 0, j = 0, k = 0;
while (k < result.length) {
if (L[i] < R[j]) {
result[k] = L[i];
i++;
} else
if (R[j] < L[i]) {
result[k] = R[j];
j++;
} else
if (i > L.length) {
while (j <= R.length) {
result[k] = R[j];
j++;
}
} else
if (j > R.length && i <= L.length) {
while (i <= L.length) {
result[k] = L[i];
i++;
}
}
k++;
}
}
public static void main(String[] args) {
int[] inputArray = { 2, 5, 4, 1, 7, 9, 6 };
MergeSort_Array ms = new MergeSort_Array();
ms.printInputArray(inputArray);
SortArray(inputArray);
for (int i: inputArray) {
System.out.println(i + " ");
}
}
}
Every time you call SortArray it will itself call SortArray twice. There is no end condition: every invocation will try to call SortArray twice.
This means that no call of SortArray can ever complete, since you infinitely recurse in each of them.
There must be some base case where it no longer calls itself. For mergesort one usually switches to some other algorithm once the array is small enough, but for simplicities sake you can even fall back to the most trivial base case for sorting: any array shorter than 2 elements is always sorted and needs nothing else to be done to be sorted.
There are multiple problems in your code:
[Major] SortArray() always attempts to split the array and sort both halves. You should not do this if the array length is less than 2, otherwise you get an infinite recursion causing a stack overflow exception.
[Hint] right can be initialized unconditionally as int[] right = new int[A.length - midpoint];
[Major] MergeArray should not reallocate the destination array. The merge must be performed in place into result so the caller's object is updated.
[Major] in the merge loop, you must test the index values before attempting to read L[i] or R[j], otherwise you might get an out of bounds exception.
Here is a modified version:
package com.company;
public class MergeSort_Array {
// Method created to print Input Array
public static void printInputArray(int inputArray[]) {
for (int i : inputArray) { //for-each loop
System.out.print(i + " ");
}
System.out.println();
}
//Function created to sort and merge Input Array:
public static void SortArray(int[] A) {
if (A.length >= 2) {
int midpoint = A.length / 2;
int[] left = new int[midpoint];
int[] right = new int[A.length - midpoint];
//Copying values from super Array to left Array:
for (int i = 0; i < midpoint; i++) {
left[i] = A[i];
}
//Copying elements from super Array to right Array:
for (int j = 0; j < right.length; j++) {
right[j] = A[midpoint + j];
}
//using Recursion
SortArray(left);
SortArray(right);
MergeArray(A, left, right);
}
}
// Creating a Function to merge left and right arrays.
public static void MergeArray(int[] result, int[] L, int[] R) {
for (int i = 0, j = 0, k = 0; k < result.length; k++) {
if (j >= R.length || (i < L.length && L[i] < R[j])) {
result[k] = L[i++];
} else {
result[k] = R[j++];
}
}
}
public static void main(String[] args) {
int[] inputArray = { 2, 5, 4, 1, 7, 9, 6 };
printInputArray(inputArray);
SortArray(inputArray);
printInputArray(inputArray);
}
}

Changing sorting algorithm java

starting Java coder here. I was wondering how to change my code so it would sort array by always swapping the biggest value to first. Sample output should be:
[3, 1, 2, 0] [3, 2, 1, 0].
public class Sorting {
static void biggest(int[] arr, int start, int end) {
for (start = 0; start < arr.length; start++) {
for (end = start + 1; end < arr.length; end++) {
if (arr[start] < arr[end]) {
int temp = arr[end];
arr[end] = arr[start];
arr[start] = temp;
System.out.println(Arrays.toString(arr));
}
}
}
}
public static void main(String[] args) {
int[] arr = {0, 1, 2, 3};
int temp = 0;
for (int i = 0; i < 4; ++i) {
biggest(arr, temp, 4 - 1);
for (int j = 0; j < 4; ++j) {
}
++temp;
}
}
Thanks in advance,
- Em
If you just want the sort to be successful, I suggest taking advantage of Java's built in sort method then reversing the list as suggested here:
Arrays.sort(arr);
ArrayUtils.reverse(arr);
But it sounds like the spirit of your question is to modify your code for this purpose. Here's the solution I came up with:
import java.util.Arrays;
public class Sorting {
static void biggest(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(Arrays.toString(arr));
int max, maxAt = i;
for (int j = i; j < arr.length; j++) {
maxAt = arr[j] > arr[maxAt] ? j : maxAt;
}
max = arr[maxAt];
if (arr[i] < max) {
arr[maxAt] = arr[i];
arr[i] = max;
}
}
}
public static void main(String[] args) {
int[] arr = {0, 1, 2, 3};
biggest(arr);
System.out.println(Arrays.toString(arr));
}
}
First off, you had a lot of extra code that you didn't need. It's a bad idea to have a loop in your main. That should be handled by the helper function. You also had a lot of redundant declarations (like start and end). You were on the right track with your helper function, but because of your main loop your time complexity was 0(n²). Eliminating that allows mine to be O(logn). Complexity aside, the key difference in terms of logic is here in your internal loop:
for (end = start + 1; end < arr.length; end++) {
if (arr[start] < arr[end]) {
int temp = arr[end];
arr[end] = arr[start];
arr[start] = temp;
In this loop you're switching the array entry with the first one you find that's bigger. This will result in unwanted early switches (like 1 & 2). Here is my solution:
for (int j = i; j < arr.length; j++) {
maxAt = arr[j] > arr[maxAt] ? j : maxAt;
}
max = arr[maxAt];
if (arr[i] < max) {
arr[maxAt] = arr[i];
arr[i] = max;
}
The key difference is that I search for the maximum value entry following the one we're swapping. That way as we proceed through the array we will always bring forward the next biggest.
Best of luck learning Java I hope this helps!

Remove duplicates in array, zero padding at end

I have a task, to remove duplicates in array, what by "remove" means to shift elements down by 1, and making the last element equal to 0,
so if I have int[] array = {1, 1, 2, 2, 3, 2}; output should be like:
1, 2, 3, 0, 0, 0
I tried this logic:
public class ArrayDuplicates {
public static void main(String[] args) {
int[] array = {1, 1, 2, 2, 3, 2};
System.out.println(Arrays.toString(deleteArrayDuplicates(array)));
}
public static int[] deleteArrayDuplicates(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] == array[j]) { //this is for comparing elements
for (; i > 0; i--) {
array[j + 1] = array[j]; //this is for shifting
}
array[array.length - 1] = 0; //making last element equal to "0"
}
}
}
return array;
}
}
But it doesn't work.. Is anyone familiar with a right solution?
I appreciate your assistance and attention very much.
Your Code:
In short, the approach you have chosen calls for a third loop variable, k, to represent the index that is currently being shifted left by 1 position.
i - the current unique item's position
j - the current position being tested for equality with unique item at i
k - the current position being shifted left due to erasure at j
Suggestion:
A more efficient approach would be to eliminate the repetitive left shifting which occurs each time a duplicate is found and instead keep track of an offset based on the number of duplicates found:
private static int[] deleteArrayDuplicates(int[] array) {
int dupes = 0; // total duplicates
// i - the current unique item's position
for (int i = 0; i < array.length - 1 - dupes; i++) {
int idupes = 0; // duplicates for current value of i
// j - the current position being tested for equality with unique item at i
for (int j = i + 1; j < array.length - dupes; j++) {
if (array[i] == array[j]) {
idupes++;
dupes++;
} else if(idupes > 0){
array[j-idupes] = array[j];
}
}
}
if(dupes > 0) {
Arrays.fill(array, array.length-dupes, array.length, 0);
}
return array;
}
This has similar complexity to the answer posted by dbl, although it should be slightly faster due to eliminating some extra loops at the end. Another advantage is that this code doesn't rely on any assumptions that the input should not contain zeroes, unlike that answer.
#artshakhov:
Here is my approach, which is pretty much close enough to what you've found but using a bit fewer operations...
private static int[] deleteArrayDuplicates(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
if (array[i] == NEUTRAL) continue; //if zero is a valid input value then don't waste time with it
int idx = i + 1; //no need for third cycle, just use memorization for current shifting index.
for (int j = i + 1; j < array.length; j++) {
if (array[i] == array[j]) {
array[j] = NEUTRAL;
} else {
array[idx++] = array[j];
}
}
}
return array;
}
I just wrote the following code to answer your question. I tested it and I am getting the output you expected. If there are any special cases I may have missed, I apologize but it seemed to work for a variety of inputs including yours.
The idea behind is that we will be using a hash map to keep track if we have already seen a particular element in our array as we are looping through the array. If the map already contains that element- meaning we have already seen that element in our array- we just keep looping. However, if it is our first time seeing that element, we will update the element at the index where j is pointing to the element at the index where i is pointing to and then increment j.
So basically through the j pointer, we are able to move all the distinct elements to the front of the array while also making sure it is in the same order as it is in our input array.
Now after the first loop, our j pointer points to the first repeating element in our array. We can just set i to j and loop through the rest of the array, making them zero.
The time complexity for this algorithm is O(N). The space complexity is O(N) because of the hash table. There is probably a way to do this in O(N) time, O(1) space.
public static int[] deleteArrayDuplicates(int[] array) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int j = 0;
for (int i = 0; i < array.length; i++) {
if (map.containsKey(array[i])) {
continue;
}
else {
map.put(array[i],1);
array[j] = array[i];
j++;
}
}
for (int i = j; i < array.length; i++) {
array[i] = 0;
}
return array;
}
Let me know if you have additional questions.
Spent a couple of hours trying to find a solution for my own, and created something like this:
public static int[] deleteArrayDuplicates(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[j] == array[i]) { //this is for comparing elements
int tempIndex = j;
while (tempIndex + 1 < array.length) {
array[tempIndex] = array[tempIndex + 1]; //this is for shifting elements down/left by "1"
array[array.length - 1] = 0; //making last element equal to "0"
tempIndex++;
}
}
}
}
return array;
}
Code is without any API-helpers, but seems like is working now.
Try this:
public static void main(String[] args)
{
int a[]={1,1,1,2,3,4,5};
int b[]=new int[a.length];
int top=0;
for( int i : a )
{
int count=0;
for(int j=0;j<top;j++)
{
if(i == b[j])
count+=1;
}
if(count==0)
{
b[top]=i;
top+=1;
}
}
for(int i=0 ; i < b.length ; i++ )
System.out.println( b[i] );
}
Explanation:
Create an another array ( b ) of same size of the given array.Now just include only the unique elements in the array b. Add the elements of array a to array b only if that element is not present in b.
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class StackOverFlow {
public static void main(String[] args) {
int[] array = {1, 1, 2, 2, 3, 2};
Set<Integer> set=new HashSet<>();
for (int anArray : array) {
set.add(anArray);
}
int[] a=new int[array.length];
int i=0;
for (Integer s:set) {
a[i]=s;
i++;
}
System.out.println(Arrays.toString(a));
}
}
Hope this simple one may help you.
Make use of Set which doesn't allow duplicates.
We can use ARRAYLIST and Java-8 Streams features to get the output.
public static int[] deleteArrayDuplicates(int[] array) {
List<Integer> list = new ArrayList(Arrays.stream(array).boxed().distinct().collect(Collectors.toList()));
for (int i = 0; i < array.length; i++) {
if (i < list.size()) {
array[i] = list.get(i);
} else {
array[i] = 0;
}
}
return array;
}
OUTPUT
[1, 2, 3, 0, 0, 0]

Recursion - Combination with in array with no repetition in Java

So I know how to get the size of a combination - factorial of the size of the array (in my case) over the size of the subset of that array wanted. The issue I'm having is getting the combinations. I've read through most of the questions so far here on stackoverflow and have come up with nothing. I think the issue I'm finding is that I want to add together the elements in the combitorial subsets created. All together this should be done recursively
So to clarify:
int[] array = {1,2,3,4,5};
the subset would be the size of say 2 and combinations would be
{1,2},{1,3},{1,4},{1,5},{2,3},{2,4},{2,5},{3,4},{3,5},{4,5}
from this data I want to see if the subset say... equals 6, then the answers would be:
{1,5} and {2,4} leaving me with an array of {1,5,2,4}
so far I have this:
public static int[] subset(int[] array, int n, int sum){
// n = size of subsets
// sum = what the sum of the ints in the subsets should be
int count = 0; // used to count values in array later
int[] temp = new temp[array.length]; // will be array returned
if(array.length < n){
return false;
}
for (int i = 1; i < array.length; i++) {
for (int j = 0; j < n; j++) {
int[] subset = new int[n];
System.arraycopy(array, 1, temp, 0, array.length - 1); // should be array moved forward to get new combinations
**// unable to figure how how to compute subsets of the size using recursion so far have something along these lines**
subset[i] = array[i];
subset[i+1] = array[i+1];
for (int k = 0; k < n; k++ ) {
count += subset[k];
}
**end of what I had **
if (j == n && count == sum) {
temp[i] = array[i];
temp[i+1] = array[i+1];
}
}
} subset(temp, n, goal);
return temp;
}
How should I go about computing the possible combinations of subsets available?
I hope you will love me. Only thing you have to do is to merge results in one array, but it checks all possibilities (try to run the program and look at output) :) :
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
int n = 2;
subset(array, n, 6, 0, new int[n], 0);
}
public static int[] subset(int[] array, int n, int sum, int count, int[] subarray, int pos) {
subarray[count] = array[pos];
count++;
//If I have enough numbers in my subarray, I can check, if it is equal to my sum
if (count == n) {
//If it is equal, I found subarray I was looking for
if (addArrayInt(subarray) == sum) {
return subarray;
} else {
return null;
}
}
for (int i = pos + 1; i < array.length; i++) {
int[] res = subset(array, n, sum, count, subarray.clone(), i);
if (res != null) {
//Good result returned, so I print it, here you should merge it
System.out.println(Arrays.toString(res));
}
}
if ((count == 1) && (pos < array.length - 1)) {
subset(array, n, sum, 0, new int[n], pos + 1);
}
//Here you should return your merged result, if you find any or null, if you do not
return null;
}
public static int addArrayInt(int[] array) {
int res = 0;
for (int i = 0; i < array.length; i++) {
res += array[i];
}
return res;
}
You should think about how this problem would be done with loops.
for (int i = 0; i < array.length - 1; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] + array[j] == sum) {
//Add the values to the array
}
}
}
Simply convert this to a recursive code.
The best way I can think to do this would be to have each recursive call run on a subset of the original array. Note that you don't need to create a new array to do this as you are doing in your code example. Just have a reference in each call to the new index in the array. So your constructor might look like this:
public static int[] subset(int[] array, int ind, int sum)
where array is the array, ind is the new starting index and sum is the sum you are trying to find

Creating a New Reverse Java Array

CodingBat > Java > Array-1 > reverse3:
Given an array of ints length 3, return a new array with the elements in reverse order, so {1, 2, 3} becomes {3, 2, 1}.
public int[] reverse3(int[] nums) {
int[] values = new int[3];
for (int i = 0; i <= nums.length - 1; i++) {
for (int j = nums.length-1; j >= 0; j--) {
values[i] = nums[j];
}
}
return values;
}
I can't get this to work properly, usually the last int in the array, becomes every single int in the new array
You don't want a two-level loop. Just have one loop:
for(int i = 0, j = nums.length - 1; i < nums.length; i++,j--) {
values[i] = nums[j];
}
or, alternately, just don't track j sepearately, and do this:
for(int i = 0; i < nums.length; i++) {
values[i] = nums[nums.length - 1 - i];
}
Unless this is a homework, why not just use Apache ArrayUtils'
ArrayUtils.reverse(nums)
Never re-invent the wheel :)
The length of the input int[] is fixed at 3? Then it doesn't get any simpler than this.
public int[] reverse3(int[] nums) {
return new int[] { nums[2], nums[1], nums[0] };
}
See also:
CodingBat/Java/Array-1/reverse3
Note that Array-1 is "Basic array problems -- no loops." You can use a loop if you want, but it's designed NOT to be solved using loops.
Firstly, while creating new array give it size of old array.
Next, when you're reversing an array, you don't need two loops, just one:
int length = oldArray.length
for(int i = 0; i < length; i++)
{
newArray[length-i-1] = oldArray[i]
}
You only want a single loop, but with indices going both ways:
public int[] reverse(int[] nums) {
int[] back = new int[nums.length];
int i,j;
for (i=0,j=nums.length-1 ; i<nums.length ; i++,j--)
back[i] = nums[j];
return back;
}
public int[] reverse3(int[] nums) {
int rotate[]={nums[2],nums[1],nums[0]};
return rotate;
}
This code gives correct output:-
public int[] reverse3(int[] nums) {
int[] myArray=new int[3];
myArray[0]=nums[2];
myArray[1]=nums[1];
myArray[2]=nums[0];
return myArray;
}
I got this with Python.. So you can get the idea behind it
def reverse3(nums):
num = []
for i in range(len(nums)):
num.append(nums[len(nums) - i - 1])
return num
In your code, for each value of i, you are setting target array elements at i to value in "nums" at j. That is how you end up with same value for all the elements at the end of all iterations.
First of all, very bad logic to have two loops for a simple swap algorithm such as :
public static void reverse(int[] b) {
int left = 0; // index of leftmost element
int right = b.length-1; // index of rightmost element
while (left < right) {
// exchange the left and right elements
int temp = b[left];
b[left] = b[right];
b[right] = temp;
// move the bounds toward the center
left++;
right--;
}
}//endmethod reverse
or simplified:
for (int left=0, int right=b.length-1; left<right; left++, right--) {
// exchange the first and last
int temp = b[left]; b[left] = b[right]; b[right] = temp;
}
Why go through all this pain, why dont you trust Java's built-in APIs and do something like
public static Object[] reverse(Object[] array)
{
List<Object> list = Arrays.asList(array);
Collections.reverse(list);
return list.toArray();
}
public int[] reverse3(int[] nums) {
int[] values = new int[nums.length];
for(int i=0; i<nums.length; i++) {
values[nums.length - (i + 1)]=nums[i];
}
return values;
}

Categories

Resources