I can be considered new in Java . I ' ve been struggling with one question which was given to us by our professor in our school. I am supposed to make a natural merge sort algorithm which has to find two sorted sub-arrays everytime and merge them.(which is a version of bottom up merge sort). But i am stuck here is my code
public class NaturalMergeMine {
private static Comparable[] aux;
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i += 1) {
if (a[i - 1].compareTo(a[i]) < 0) {
return false;
}
}
return true;
}
private static void sort(Comparable[] a, int lo, int hi) {
int i = lo;
int j = 0;
int mid = 0;
int az = 0;
while (true) {
i = 0;
System.out.println("outter");
while (i < a.length) {
System.out.println("inner 1");
if (i == a.length - 1) {
break;
} else if (a[i].compareTo(a[i + 1]) < 0) {
break;
}
i++;
}
j = i + 1;
while (j < a.length) {
System.out.println("inner 2");
if (j == a.length - 1) {
break;
} else if (a[j].compareTo(a[j + 1]) < 0) {
break;
}
j++;
}
mid = lo + (j - lo) / 2;
Merge(a, lo, mid, j);
lo = 0;
if (isSorted(a)) {
break;
}
}
}
public static void Merge(Comparable[] a, int lo, int mid, int hi) {
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
for (int k = lo; k <= hi; k++) {
if (i > mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (aux[i].compareTo(aux[j]) < 0) {
a[k] = aux[j++];
} else {
a[k] = aux[i++];
}
}
}
public static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
public static void main(String[] args) {
Integer[] arr = {6, 4, 5, 7, 8, 3, 2, 1};
sort(arr);
show(arr);
}
}
what happens is that it is not merging correctly and it goes in an infinite loop in the outter loop ,which is because it is not sorted properly. Is there anyone who can advice me a better way or can tell me the mistake i make here. Thanks in advance.
The issue is in the mid calculation, im not quite sure why you do that,but if the mid is less than i in you merge method you wont reach the fault case so you will stuck in discovering it without solving, for each run you need to pick up two arrays to sort, so instead of mid you can insert i to the merge method so you start the merging from the fault in.
public class NaturalMergeMine {
private static Comparable[] aux;
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i += 1) {
if (a[i - 1].compareTo(a[i]) > 0) {//changed operator to greater than
return false;
}
}
return true;
}
private static void sort(Comparable[] a, int lo, int hi) {
int i = lo;
int j = 0;
int mid = 0;
int az = 0;
while (true) {
i = 0;
System.out.println("outter");
while (i < a.length) {
System.out.println("inner 1");
if (i == a.length - 1) {
break;
} else if (a[i].compareTo(a[i + 1]) > 0) {//changed operator to greater than
break;
}
i++;
}
j = i + 1;
while (j < a.length) {
System.out.println("inner 2");
if (j == a.length - 1) {
break;
} else if (a[j].compareTo(a[j + 1]) > 0) {//changed operator to greater than
break;
}
j++;
}
// mid = lo + (j - lo) / 2;
Merge(a, lo, i, j);
lo = 0;
if (isSorted(a)) {
break;
}
}
}
public static void Merge(Comparable[] a, int lo, int mid, int hi) {
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
for (int k = lo; k <= hi; k++) {
if (i > mid) {
a[k] = aux[j++];
} else if (j > hi) {
a[k] = aux[i++];
} else if (aux[i].compareTo(aux[j]) > 0) {//changed the operator to greater than
a[k] = aux[j++];
} else {
a[k] = aux[i++];
}
}
}
public static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
public static void main(String[] args) {
Integer[] arr = {6, 4, 5, 7, 8, 3, 2, 1};
sort(arr);
show(arr);
}
}
Amer Qarabsa's answer fixes the problems with the original code. Below are some alternate examples that are a bit more optimized, scanning the array for pairs of ascending sequences, rather than starting over at the beginning each time. If the array was reversed, then the first pass creates runs of 2, the next pass runs of 4, ... . With the original version, the run size would only increase by one on each loop. The example code below uses open intervals (last index is end of array instead of last element of array).
public class jsortns {
private static Comparable[] aux;
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
int i;
int j;
int k;
while (true) { // merge pass
i = 0;
while(true) { // find, merge pair of runs
j = i; // find left run
while (++j < a.length) {
if (a[j-1].compareTo(a[j]) > 0)
break;
}
if(j == a.length){ // if only one run left
if(i == 0) // if done return
return;
else // else end of merge pass
break;
}
k = j; // find right run
while (++k < a.length) {
if (a[k-1].compareTo(a[k]) > 0){
break;
}
}
Merge(a, i, j, k); // merge runs
i = k;
if(i == a.length) // if end of merge pass, break
break;
}
}
}
// merge left and right runs
// ll = start of left run
// rr = start of right run == end of left run
// ee = end of right run
public static void Merge(Comparable[] a, int ll, int rr, int ee) {
int i = ll;
int j = rr;
int k;
for (k = ll; k < ee; k++)
aux[k] = a[k];
k = ll;
while(true){
// if left element <= right element
if (aux[i].compareTo(aux[j]) <= 0) {
a[k++] = aux[i++]; // copy left element
if(i == rr){ // if end of left run
while(j < ee) // copy rest of right run
a[k++] = aux[j++];
return; // and return
}
} else {
a[k++] = aux[j++]; // copy right element
if(j == ee){ // if end of right run
while(i < rr){ // copy rest of left run
a[k++] = aux[i++];
}
return; // and return
}
}
}
}
No copy back version, instead merges back and forth between two arrays, and only copies at the end if the sorted data ends up in the wrong array.
public class jsortns {
private static Comparable[] b; // temp array
private static Comparable[] o; // original array reference
private static Comparable[] t; // used to swap a, b
public static void sort(Comparable[] a) {
o = a; // save ref to a
b = new Comparable[a.length]; // allocate temp array
int i;
int j;
int k;
while (true) { // merge pass
i = 0;
while(true) { // find, merge pair of runs
j = i; // find left run
while (++j < a.length) {
if (a[j-1].compareTo(a[j]) > 0)
break;
}
if(j == a.length){ // if only one run left
if(i != 0){ // if not done
while(i < j){ // copy run to b
b[i] = a[i];
i++;
}
break; // break to end merge pass
} else { // else sort done
if(a != o){ // if a not original a, copy
for(i = 0; i < a.length; i++)
b[i] = a[i];
}
return;
}
}
k = j; // find right run
while (++k < a.length) {
if (a[k-1].compareTo(a[k]) > 0){
break;
}
}
Merge(a, b, i, j, k); // merge left, right into b
i = k;
if(i == a.length) // break if end pass
break;
}
t = a; // swap a and b (references)
a = b;
b = t;
}
}
// merge left and right runs from a[] to b[]
// ll = start of left run
// rr = start of right run == end of left run
// ee = end of right run
public static void Merge(Comparable[] a, Comparable[] b, int ll, int rr, int ee) {
int i = ll;
int j = rr;
int k = ll;
while(true){
// if left element <= right element
if (a[i].compareTo(a[j]) <= 0) {
b[k++] = a[i++]; // copy left element
if(i == rr){ // if end of left run
while(j < ee) // copy rest of right run
b[k++] = a[j++];
return; // and return
}
} else {
b[k++] = a[j++]; // copy right element
if(j == ee){ // if end of right run
while(i < rr){ // copy rest of left run
b[k++] = a[i++];
}
return; // and return
}
}
}
}
Related
I am working on sorting algorithms and I am trying to improve mergeSort by locating already sorted subArrays.
public static void mergeSort(int[] array)
{
if(array == null)
{
return;
}
if(array.length > 1)
{
int mid = array.length / 2;
// left
int[] left = new int[mid];
for(int i = 0; i < mid; i++)
{
left[i] = array[i];
}
//right
int[] right = new int[array.length - mid];
for(int i = mid; i < array.length; i++)
{
right[i - mid] = array[i];
}
//recursively calls
mergeSort(left);
mergeSort(right);
int i = 0;
int j = 0;
int k = 0;
// left and right merged
while(i < left.length && j < right.length)
{
if(left[i] < right[j])
{
array[k] = left[i];
i++;
}
else
{
array[k] = right[j];
j++;
}
k++;
}
// left overs
while(i < left.length)
{
array[k] = left[i];
i++;
k++;
}
while(j < right.length)
{
array[k] = right[j];
j++;
k++;
}
}
}
What you are looking for is called natural merge sort. Before you start with sorting the dataset you will do one run to identify all the presorted data. The mergesort itself stays the same.
I found some example code for you at: happycoders
package eu.happycoders.sort.method.mergesort;
import eu.happycoders.sort.method.Counters;
import eu.happycoders.sort.method.SortAlgorithm;
/**
* Natural merge sort implementation for performance tests.
*
* #author Sven Woltmann
*/
public class NaturalMergeSort implements SortAlgorithm {
#Override
public void sort(int[] elements) {
int numElements = elements.length;
int[] tmp = new int[numElements];
int[] starts = new int[numElements + 1];
// Step 1: identify runs
int runCount = 0;
starts[0] = 0;
for (int i = 1; i <= numElements; i++) {
if (i == numElements || elements[i] < elements[i - 1]) {
starts[++runCount] = i;
}
}
// Step 2: merge runs, until only 1 run is left
int[] from = elements;
int[] to = tmp;
while (runCount > 1) {
int newRunCount = 0;
// Merge two runs each
for (int i = 0; i < runCount - 1; i += 2) {
merge(from, to, starts[i], starts[i + 1], starts[i + 2]);
starts[newRunCount++] = starts[i];
}
// Odd number of runs? Copy the last one
if (runCount % 2 == 1) {
int lastStart = starts[runCount - 1];
System.arraycopy(from, lastStart, to, lastStart,
numElements - lastStart);
starts[newRunCount++] = lastStart;
}
// Prepare for next round...
starts[newRunCount] = numElements;
runCount = newRunCount;
// Swap "from" and "to" arrays
int[] help = from;
from = to;
to = help;
}
// If final run is not in "elements", copy it there
if (from != elements) {
System.arraycopy(from, 0, elements, 0, numElements);
}
}
private void merge(int[] source, int[] target, int startLeft,
int startRight, int endRight) {
int leftPos = startLeft;
int rightPos = startRight;
int targetPos = startLeft;
// As long as both arrays contain elements...
while (leftPos < startRight && rightPos < endRight) {
// Which one is smaller?
int leftValue = source[leftPos];
int rightValue = source[rightPos];
if (leftValue <= rightValue) {
target[targetPos++] = leftValue;
leftPos++;
} else {
target[targetPos++] = rightValue;
rightPos++;
}
}
// Copy the rest
while (leftPos < startRight) {
target[targetPos++] = source[leftPos++];
}
while (rightPos < endRight) {
target[targetPos++] = source[rightPos++];
}
}
#Override
public void sort(int[] elements, Counters counters) {
// Not implemented
}
}
I have an assignment to draw a graph and analyze the Hoare's partitioning algorithm and compare theoretical and practical worst-case scenario values. It says that I need a count variable to keep track of critical steps (i.e comparison and swapping). I wrote this code for Hoare's partitioning.
public static int partition(Comparable[] a, int left, int right) {
Comparable pivot = a[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true) {
do {
i++;
} while (a[i].compareTo(pivot) < 0);
do {
j--;
} while (a[j].compareTo(pivot) > 0);
if (i < j) {
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
} else {
return j;
}
}
}
Now I am confused about where to add the count variable. I know that count variable will be incremented in if (i < j) line. But do I also need to increment count in both do-while loops to keep track of comparisons?
So, the updated code will be like this?
public static int partition(Comparable[] a, int left, int right) {
int count = 0;
Comparable pivot = a[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true) {
do {
count++;
i++;
} while (a[i].compareTo(pivot) < 0);
do {
count++;
j--;
} while (a[j].compareTo(pivot) > 0);
if (i < j) {
count++;
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
} else {
return j;
}
}
}
Or like this?
public static int partition(Comparable[] a, int left, int right) {
int count = 0;
Comparable pivot = a[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true) {
do {
i++;
} while (a[i].compareTo(pivot) < 0);
do {
j--;
} while (a[j].compareTo(pivot) > 0);
if (i < j) {
count++;
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
} else {
return j;
}
}
}
package practice;
import java.util.Arrays;
public class SmallestPositiveIntegerNotInArray {
public static int solution(int[] a) {
int solutionRet = 1;
Arrays.sort(a);
if (a.length == 0) { //if empty return 1
return solutionRet;
} else if(a.length == 1) { //if 1 element, return 1 or 2
if(solutionRet == a[0]) {
return solutionRet + 1;
} else {
return solutionRet;
}
} else if(a[a.length - 1] < 1) { //if all negatives, return 1
return solutionRet;
} else {
for (int i = 0; i < a.length; i++) {
if (a[i] > solutionRet) {
if (a[i] > solutionRet + 1) {
if (a[i - 1] == solutionRet) {
return solutionRet + 1;
} else {
return solutionRet;
}
}
solutionRet++;
} else if (a[i] == solutionRet) {
if (a[i] + 1 < a[i + 1] && a[i + 1] < a.length) {
return solutionRet + 1;
}
}
}
}
if (solutionRet == a[a.length - 1]) { //if the last num is same as solution, add 1
solutionRet++;
}
return solutionRet;
}
public static void main(String[] args) {
int testArray[] = {};
System.out.println(solution(testArray));
}
}
Any suggestions for improving readability welcome, explanation for suggestion not necessary but appreciated.
If you know/find a more efficient way of doing this I would also appreciate if you told me/guided me in the right direction
class SmallestPositiveIntegerNotInArray {
public int solution(int[] a) {
HashSet<Integer> hashSet = new HashSet<Integer>();
int smallInt = 1;
for (int i = 0 ; i < a.length; i++) {
hashSet.add(a[i]);
}
while (hashSet.contains(smallInt)) {
smallInt++;
}
return smallInt;
}
}
The easiest way is to sort the array, and iterate through, looking for the next element you'd expect if the positive ints are contiguous. The downside is that you have to destroy the ordering of the array (unless you copy the array and sort that).
Arrays.sort(a);
int expectedNext = 1;
for (int i = 0; i < a.length; ++i) {
if (a[i] > 0) {
if (a[i] != expectedNext) return expectedNext;
++expectedNext;
while ((i+1) < a.length && a[i] == a[i+1])) {
++i;
}
}
}
return aa.length + 1;
A non-destructive way is to use a BitSet to record the numbers that are present, and then look for the first non-set bit (not including zero):
BitSet bits = new BitSet(a.length);
for (int aa : a) {
if (aa > 0 && aa < a.length) {
bits.set(aa);
}
}
return bits.nextClearBit(1);
My solution:
public static int solution(int[] A) {
int num = 1;
for (int j = 0; j < A.length; j++) {
for (int i = 0; i < A.length; i++) {
if (A[i] > 0) {
if (A[i] == num) {
num++;
break;
}
}
}
}
return num;
}
I think sorting array first will consuming time, that is not efficient for code performance.
you could try the following simple code:
public static int solution2(int[] A) {
// write your code in Java SE 8
Arrays.sort(A);
int smallest =1 ;
for (int i=0 ; i< A.length; i++) {
System.out.println(A[i]);
if (A[i]==smallest) // to increase smallest
smallest++;
}
return smallest;
}
Here is my code for a mergesort in java:
public class MergeSort {
public static void mergesort(int[] input) {
int inputSize = input.length;
if(inputSize < 2) {
return;
}
int[] left = new int[inputSize/2];
int[] right = new int[inputSize/2];
int count = 0;
for(int i=0; i < inputSize/2; i++) {
left[i] = input[i];
}
for(int i=inputSize/2; i<inputSize; i++) {
right[count] = input[i];
count++;
}
mergesort(left);
mergesort(right);
merge(left, right, input);
}
public static int[] merge(int[] returnArr, int[] left, int[] right) {
int leftSize = left.length;
int rightSize = right.length;
int i = 0;
int j =0;
int k = 0;
int count = 0;
while(i < leftSize && j < rightSize) {
if(left[i] <= right[j]) {
returnArr[k] = left[i];
i++;
}
else {
returnArr[k] = right[j];
j++;
}
k++;
}
while(i<leftSize) {
returnArr[k] = left[i];
i++;
k++;
}
while(j < rightSize) {
returnArr[k] = right[j];
j++;
k++;
}
for(int x=0; x<returnArr.length; x++) {
System.out.print(returnArr[x]);
}
return returnArr;
}
public static void main(String[] args) {
int[] array = {3,4,6,2,7,1,8,6};
mergesort(array);
}
}
My issue is that I'm getting an out of bounds exception.
I'm using the debugger and have found that after mergesort(left) and mergesort(right) have finished recursively running.
The arrays left and right, which go into the merge function, have the values [3] and [4] respectively, which is correct.
But when the debugger jumps into the merge function, left has value [3] and right, for some reason is length 2 and has the value [3,4].
This is the source of my out of bounds exception, though I'm not sure why when the merge function runs for its first time, it changes the value of "right".
One problem that is readily visible is that the you shouldn't make 2 arrays of size inputSize/2. Make two arrays of inputSize/2 and inputsize-inputSize/2. Otherwise the algorithm would fail for odd length array.
Also call the function with proper order of the arguments. merge( input, left, right);
I fixed your code and merged them to 1 method, left.length and right.length are limited by input.length so you only need to loop by input.length:
public static void mergeSort(int[] input)
{
if (input.length < 2)
{
return;
}
int[] left = new int[input.length / 2];
int[] right = new int[input.length - input.length / 2];
for (int i = 0; i < input.length; i++)
{
if (i < input.length / 2)
left[i] = input[i];
else
right[i - input.length / 2] = input[i];
}
mergeSort(left);
mergeSort(right);
for (int i = 0, l = 0, r = 0; i < input.length; i++)
{
if (l >= left.length)
{
input[i] = right[r];
r++;
}
else if (r >= right.length)
{
input[i] = left[l];
l++;
}
else
{
if (left[l] >= right[r])
{
input[i] = right[r];
r++;
}
else
{
input[i] = left[l];
l++;
}
}
}
}
you had two problems with your code:
1- as #coderredoc said: your left and right array sizes are wrong:
exemple: if you had an array of 7 elements, your left and right arrays would have a size of 7/2 = 3 so you would have a total of 6 elements in left and right arrays and not 7.
2- you are calling merge function in the mergeSort function with wrong parameters order:
it should be returnArr, left, right and not left,right, returnArr.
Explanation:
if you pass the left array as the first parameter, it would merge the right and the returnArr in the left array. But your left array has a size of 3 and the sum of the sizes of the others is 7 + 3 = 10 that's why you got an OutOfBoundsException.
you need to call merge(input,left,right);
here is the final version:
public class MergeSort {
public static void mergesort(int[] input) {
int inputSize = input.length;
if(inputSize < 2) {
return;
}
int[] left = new int[inputSize/2];
int[] right = new int[inputSize-inputSize/2];
int count = 0;
for(int i=0; i < inputSize/2; i++) {
left[i] = input[i];
}
for(int i=inputSize/2; i<inputSize; i++) {
right[count] = input[i];
count++;
}
mergesort(left);
mergesort(right);
merge(input,left, right);
}
public static int[] merge(int[] returnArr, int[] left, int[] right) {
int leftSize = left.length;
int rightSize = right.length;
int i = 0;
int j =0;
int k = 0;
int count = 0;
while(i < leftSize && j < rightSize) {
if(left[i] <= right[j]) {
returnArr[k] = left[i];
i++;
}
else {
returnArr[k] = right[j];
j++;
}
k++;
}
while(i<leftSize) {
returnArr[k] = left[i];
i++;
k++;
}
while(j < rightSize) {
returnArr[k] = right[j];
j++;
k++;
}
for(int x=0; x<returnArr.length; x++) {
System.out.print(returnArr[x]);
}
return returnArr;
}
public static void main(String[] args) {
int[] array = {3,4,6,2,7,1,8,6};
mergesort(array);
}
}
Can someone show me how to handle duplicates in QuickSort with my function here?
private static int[] quickSort(int[] input, int left, int right) {
int mid = (left + right) / 2;
int i = left;
int j = right;
if((right - left) < 1) {
return new int[]{};
}
while(i <= j) {
while((input[i] < input[mid])) {
i++;
}
while((input[j] > input[mid])) {
j--;
}
if(i <= j) {
int temp = input[i];
input[i] = input[j];
input[j] = temp;
i++;
j--;
}
}
if(j > left) {
input = quickSort(input, left, j);
}
if(i < right) {
input = quickSort(input, i, right);
}
return input;
}
The code has several problems.
"return new int[]{};" This will generate new arrays and consumes memory unnecessarily. You may return "input".
For the first inner loops, you may want to have "input[i] <= input[mid] && i < mid". This will handle duplicates.
There are some well-tested code here: Quick Sort