How to write recursive left-shift array algorithm more efficiently - java

I was completing some practice problems today on HackerRank, and on a problem asking me to write an algorithm that left-shifts all elements in an array n-times (i.e. rotLeft({1, 2, 3, 4, 5}, 1) returns {2, 3, 4, 5, 1}), I ran into the classic timeout error due to my algorithm being inefficient. This isn't the first time I have been dinged for inefficient algo writing by an online coding system. Really I have two questions: 1) How can I specifically rewrite my left-shift algorithm to be more time-efficient? 2) In general, how do I improve performance-wise upon an algorithm which runs inefficiently?
public static final int NULL = -2147483648;
static int[] rotLeft(int[] a, int d) {
return rotLeftRec(a, d, 0);
}
static int[] rotLeftRec(int[] a, int d, int numRot) {
if (numRot >= d) {
return a;
} else {
int first = a[0];
int temp1 = NULL;
int temp2 = NULL;
for (int i = a.length - 1; i >= 0; i--) {
if (i == a.length - 1) {
temp1 = a[i];
} else {
if (temp1 == NULL) {
temp1 = a[i];
a[i] = temp2;
temp2 = NULL;
} else {
temp2 = a[i];
a[i] = temp1;
temp1 = NULL;
}
}
}
a[a.length - 1] = first;
}
return rotLeftRec(a, d, numRot + 1);
}

I was curious about the arraycopy approach, looks like this. There's no need to call the method proposed here m times where you shift the array by n each time. You can call it with the product m*n and you're there as well (I think). Would be curious if that one passes.
Btw. ArrayList uses arrayCopy, I got it from there.
static int[] rotLeft(int[] a, int d) {
if (d < 0 || a == null || a.length == 0) {
throw new IllegalArgumentException();
}
int shift = d % a.length;
if (shift == 0) {
return a;
}
int[] result = new int[a.length];
System.arraycopy(a, shift, result, 0, a.length - shift);
System.arraycopy(a, 0, result, a.length - shift, shift);
return result;
}

You recurse for d times shifting once. That is inefficient as you can immediately find the correct value at index i by i+d % a.length.
The point is left-rotating by d would seem to need multiple variables. For this you should use recursion.
static void rotLeft(int[] a, int d) {
rotLeftRec(a, d, 0);
}
static void rotLeftRec(int[] a, int d, int i) {
if (i >= a.length) {
return;
}
int j = (i + d) % a.length;
int aj = a[j];
rotLeftRec(a, d, i + 1);
a[i] = aj;
}
The trick is, that after coming back from the recursion you fill in the data a[i] with the value of a[j] you remembered before the recursive call.
You see, that the recursion enables you to hold a stack of i and aj.
Implementing such recursion is doing the simple step and looking at the time line of variables, what should be remembered beforehand, what future partial result is needed (some return value of a recursive call).
Transformation to iterative version (still further optimizable)
static void rotLeft(int[] a, int d) {
int[] stack = new int[a.length];
for (int i = 0; i < a.length; ++i) {
int j = (i + d) % a.length;
int aj = a[j];
stack[i] = aj;
}
System.arraycopy(stack, 0, a, 0, a.length);
}

Related

Merge Sort Java Implementation so merge works within one array with second splitted array reversed

Challenge: within a lecture on data structures and algorithms I encountered a version of merge sort which uses the merge routine in a way that the second half is being reversed from the splitting index and from there compares the first and the last element. I tried to implement in java and it always failed somehow.
Problem: The array is being sorted so that the output is [1, 2, 4, 8, 6] so the 6 is not sorted. It seems as if the recursive call is not looking at the element 6 in the last merge call.
What I tried: Shifting different indices and adding different print statements for checking.
I tried to make j = r before the last for loop within merge which lead to stack overflow every time. I tried to change the way how the size of the array is being calculated, since I was not sure if the pseudo code excepts the array to start from 1 or 0. I tried to shift if(p < r-1) to if(p <= r-1) but get a stack overflow.
I looked at different implementations of java merge routine and every I found so far seems to work with two arrays. Is there a serious reason why the approach above is not working correctly or any idea how to fix this issue?
Given the following pseudo code:
void merge_sort(array<T>& A, int p, int r) {
if (p < r - 1) {
int q = Floor((p + r) / 2);
merge_sort(A, p, q);
merge_sort(A, q + 1, r);
merge(A, p, q, r);
}
}
void merge(array<T>& A, int p, int q, int r) {
array<T> B(p, r - 1);
int i, j;
for (i = p; i < q; i++)
B[i] = A[i];
// Now i=q
for (j = r; i < r; i++)
B[--j] = A[i];
i = p;
j = r - 1;
for (int k = p; k < r; k++)
A[k] = (B[i] < B[j]) ? B[i++] : B[j--];
}
I tried to implement in java like so:
import java.util.Arrays;
public class Mergesort {
private static int[] A = new int[]{ 4, 2, 1, 8, 6 };
public static void main(String[] args) {
merge_sort(0, A.length - 1);
System.out.println(Arrays.toString(A));
}
public static void merge_sort(int p, int r) {
if (p < r - 1) {
int q = Math.floor((p + r) / 2);
merge_sort(p, q);
merge_sort(q + 1, r);
merge(p, q, r);
}
}
public static void merge(int p, int q, int r) {
int[] B = new int[r - p];
int i, j;
for (i = p; i < q; i++)
B[i] = A[i]
for (j = r; i < r; i++)
B[--j] = A[i];
i = p;
j = r - 1;
for (int k = p; k < r; k++)
A[k] = (B[i] < B[j])? B[i++] : B[j--];
}
}
There are multiple problems in your code:
the temporary array is too short: since r is the index of the last element, the size should be r - p + 1. It is much simpler to pass r as the index one past the last element of the slice to sort.
the first for loop is incorrect: you should use a different index into B and A.
the second for loop copies to B[r - 1] downwards, but it should use B[r - p] instead.
the merging loop is incorrect: you should test if i and j are still within the boundaries of the respective halves before accessing B[i] and/or B[j].
[minor] there is no need for int q = Math.floor((p + r) / 2); in java as p and r are have type int, so the division will use integer arithmetics.
Here is a modified version:
public class Mergesort {
private static int[] A = new int[]{ 4, 2, 1, 8, 6 };
public static void main(String[] args) {
merge_sort(0, A.length);
System.out.println(Arrays.toString(A));
}
public static void merge_sort(int p, int r) {
if (r - p >= 2) {
int q = p + (r - p) / 2;
merge_sort(p, q);
merge_sort(q, r);
merge(p, q, r);
}
}
public static void merge(int p, int q, int r) {
int m = q - p; // zero based index of the right half
int n = r - p; // length of the merged slice
int[] B = new int[n];
int i, j, k;
for (i = p, j = 0; j < m; j++)
B[j] = A[i++];
for (i = r, j = m; j < n; j++)
B[j] = A[--i];
for (i = 0, j = n, k = p; k < r; k++) {
// for stable sorting, i and j must be tested against their boundary
// A[k] = (i < m && (j <= m || B[i] <= B[j - 1])) ? B[i++] : B[--j];
// stability is not an issue for an array of int
A[k] = (B[i] <= B[j - 1]) ? B[i++] : B[--j];
}
}
}
Reversing the second half allows for a simpler merge loop without boundary tests. Note however that there is a simpler approach that uses less memory and might be more efficient:
public static void merge(int p, int q, int r) {
int m = q - p; // length of the left half
int[] B = new int[m];
int i, j, k;
// only save the left half
for (i = p, j = 0; j < m; j++)
B[j] = A[i++];
for (i = 0, j = q, k = p; i < m; k++) {
A[k] = (j >= r || B[i] <= A[j]) ? B[i++] : A[j++];
}
}

Solving Project Euler 15 efficiency issue [duplicate]

I have the following programm calculating the binomial coefficient of two integers. But I want to change the programm, that it calculates and saves only the necessary coefficients for the solution.
The problem is that I have really no idea how to it, right now.
The Code
public static long binomialIteration(int n, int k)
{
if(k<0 || n<k)
{
return 0;
}
long[][] h= new long[n+1][n+1];
for(int i=0; i<=n; i++)
{
h[i][0]=h[i][i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=0; j<=i; j++)
{
h[i][j] = (j==0 ? 0: h[i-1][j-1]) + (i == j ? 0 : h[i-1][j]);
}
}
return h[n][k];
}
Do you want to keep your code afterall?
Because you can also compute the binominal coefficient recursively, which would reduce your function to these 4 lines:
static long binomi(int n, int k) {
if ((n == k) || (k == 0))
return 1;
else
return binomi(n - 1, k) + binomi(n - 1, k - 1);
}
What about this Code from this site
private static long binomial(int n, int k)
{
if (k>n-k)
k=n-k;
long b=1;
for (int i=1, m=n; i<=k; i++, m--)
b=b*m/i;
return b;
}
You don't say which coefficients youi need. If you need C(N,n) for some fixed N, you could translate the C code below, which uses a one dimensional array.
After the call, C[n] will hold the binomial coefficient C(N,n) for 0<=m<=N, as long as N is at most 66 -- if you need bigger N you will need to use an integral type with more bits.
static int64_t* pascals_triangle( int N)
{
int n,k;
int64_t* C = calloc( N+1, sizeof *C);
for( n=0; n<=N; ++n)
{ C[n] = 1;
k = n;
while( --k>0)
{ C[k] += C[k-1];
}
}
return C;
}

How can improve this algorithm to optimize the running time (find points in segments)

I'm given 2 integrals, the first is the number of segments (Xi,Xj) and the second is the number of points that can or cant be inside those segments.
As an example, the input could be:
2 3
0 5
8 10
1 6 11
Where, in first line, 2 means "2 segments" and 3 means "3 points".
The 2 segments are "0 to 5" and "8 to 10", and the points to look for are 1, 6, 11.
The output is
1 0 0
Where point 1 is in segment "0 to 5", and point 6 and 11 are not in any segment. If a point appears in more than one segment, like a 3, the output would be 2.
The original code, was just a double loop to search the points between segments. I used the Java Arrays quicksort (modified so when it sorts endpoints of segments, sorts also startpoints so start[i] and end[i] belong to the same segment i) to improve the speed of the double loop but it isnt enought.
The next code works fine but when there's too many segments it gets very slow:
public class PointsAndSegments {
private static int[] fastCountSegments(int[] starts, int[] ends, int[] points) {
sort(starts, ends);
int[] cnt2 = CountSegments(starts,ends,points);
return cnt2;
}
private static void dualPivotQuicksort(int[] a, int[] b, int left,int right, int div) {
int len = right - left;
if (len < 27) { // insertion sort for tiny array
for (int i = left + 1; i <= right; i++) {
for (int j = i; j > left && b[j] < b[j - 1]; j--) {
swap(a, b, j, j - 1);
}
}
return;
}
int third = len / div;
// "medians"
int m1 = left + third;
int m2 = right - third;
if (m1 <= left) {
m1 = left + 1;
}
if (m2 >= right) {
m2 = right - 1;
}
if (a[m1] < a[m2]) {
swap(a, b, m1, left);
swap(a, b, m2, right);
}
else {
swap(a, b, m1, right);
swap(a, b, m2, left);
}
// pivots
int pivot1 = b[left];
int pivot2 = b[right];
// pointers
int less = left + 1;
int great = right - 1;
// sorting
for (int k = less; k <= great; k++) {
if (b[k] < pivot1) {
swap(a, b, k, less++);
}
else if (b[k] > pivot2) {
while (k < great && b[great] > pivot2) {
great--;
}
swap(a, b, k, great--);
if (b[k] < pivot1) {
swap(a, b, k, less++);
}
}
}
// swaps
int dist = great - less;
if (dist < 13) {
div++;
}
swap(a, b, less - 1, left);
swap(a, b, great + 1, right);
// subarrays
dualPivotQuicksort(a, b, left, less - 2, div);
dualPivotQuicksort(a, b, great + 2, right, div);
// equal elements
if (dist > len - 13 && pivot1 != pivot2) {
for (int k = less; k <= great; k++) {
if (b[k] == pivot1) {
swap(a, b, k, less++);
}
else if (b[k] == pivot2) {
swap(a, b, k, great--);
if (b[k] == pivot1) {
swap(a, b, k, less++);
}
}
}
}
// subarray
if (pivot1 < pivot2) {
dualPivotQuicksort(a, b, less, great, div);
}
}
public static void sort(int[] a, int[] b) {
sort(a, b, 0, b.length);
}
public static void sort(int[] a, int[] b, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
dualPivotQuicksort(a, b, fromIndex, toIndex - 1, 3);
}
private static void rangeCheck(int length, int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IllegalArgumentException("fromIndex > toIndex");
}
if (fromIndex < 0) {
throw new ArrayIndexOutOfBoundsException(fromIndex);
}
if (toIndex > length) {
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
private static void swap(int[] a, int[] b, int i, int j) {
int swap1 = a[i];
int swap2 = b[i];
a[i] = a[j];
b[i] = b[j];
a[j] = swap1;
b[j] = swap2;
}
private static int[] naiveCountSegments(int[] starts, int[] ends, int[] points) {
int[] cnt = new int[points.length];
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < starts.length; j++) {
if (starts[j] <= points[i] && points[i] <= ends[j]) {
cnt[i]++;
}
}
}
return cnt;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n, m;
n = scanner.nextInt();
m = scanner.nextInt();
int[] starts = new int[n];
int[] ends = new int[n];
int[] points = new int[m];
for (int i = 0; i < n; i++) {
starts[i] = scanner.nextInt();
ends[i] = scanner.nextInt();
}
for (int i = 0; i < m; i++) {
points[i] = scanner.nextInt();
}
//use fastCountSegments
int[] cnt = fastCountSegments(starts, ends, points);
for (int x : cnt) {
System.out.print(x + " ");
}
}
I believe the problem is in the CountSegments() method but I'm not sure of another way to solve it. Supposedly, I should use a divide and conquer algorithm, but after 4 days, I'm up to any solution.
I found a similar problem in CodeForces but the output is different and most solutions are in C++. Since I have just 3 months that I started to learn java, I think I have reached my knowledge limit.
Given the constrains by OP, let n be the # of segments, m be the number of points to be query, where n,m <= 5*10^4, I can come up with a O(nlg(n) + mlg(n)) solution (which should be enough to pass most online judge)
As each query is a verifying problem: Can the point be covered by some intervals, yes or no, we do not need to find which / how many intervals the point has been covered.
Outline of the algorithm:
Sort all intervals first by starting point, if tie then by length (rightmost ending point)
Try to merge the intervals to get some disjoint overlapping intervals. For e.g. (0,5), (2,9), (3,7), (3,5), (12,15) , you will get (0,9), (12,15). As the intervals are sorted, this can be done greedily in O(n)
Above are the precomputation, now for each point, we query using the disjoint intervals. Simply binary search if any interval contains such point, each query is O(lg(n)) and we got m points, so total O(m lg(n))
Combine whole algorithm, we will get an O(nlg(n) + mlg(n)) algorithm
This is an implementation similar to #Shole's idea:
public class SegmentsAlgorithm {
private PriorityQueue<int[]> remainSegments = new PriorityQueue<>((o0, o1) -> Integer.compare(o0[0], o1[0]));
private SegmentWeight[] arraySegments;
public void addSegment(int begin, int end) {
remainSegments.add(new int[]{begin, end});
}
public void prepareArrayCache() {
List<SegmentWeight> preCalculate = new ArrayList<>();
PriorityQueue<int[]> currentSegmentsByEnds = new PriorityQueue<>((o0, o1) -> Integer.compare(o0[1], o1[1]));
int begin = remainSegments.peek()[0];
while (!remainSegments.isEmpty() && remainSegments.peek()[0] == begin) {
currentSegmentsByEnds.add(remainSegments.poll());
}
preCalculate.add(new SegmentWeight(begin, currentSegmentsByEnds.size()));
int next;
while (!remainSegments.isEmpty()) {
if (currentSegmentsByEnds.isEmpty()) {
next = remainSegments.peek()[0];
} else {
next = Math.min(currentSegmentsByEnds.peek()[1], remainSegments.peek()[0]);
}
while (!currentSegmentsByEnds.isEmpty() && currentSegmentsByEnds.peek()[1] == next) {
currentSegmentsByEnds.poll();
}
while (!remainSegments.isEmpty() && remainSegments.peek()[0] == next) {
currentSegmentsByEnds.add(remainSegments.poll());
}
preCalculate.add(new SegmentWeight(next, currentSegmentsByEnds.size()));
}
while (!currentSegmentsByEnds.isEmpty()) {
next = currentSegmentsByEnds.peek()[1];
while (!currentSegmentsByEnds.isEmpty() && currentSegmentsByEnds.peek()[1] == next) {
currentSegmentsByEnds.poll();
}
preCalculate.add(new SegmentWeight(next, currentSegmentsByEnds.size()));
}
SegmentWeight[] arraySearch = new SegmentWeight[preCalculate.size()];
int i = 0;
for (SegmentWeight l : preCalculate) {
arraySearch[i++] = l;
}
this.arraySegments = arraySearch;
}
public int searchPoint(int p) {
int result = 0;
if (arraySegments != null && arraySegments.length > 0 && arraySegments[0].begin <= p) {
int index = Arrays.binarySearch(arraySegments, new SegmentWeight(p, 0), (o0, o1) -> Integer.compare(o0.begin, o1.begin));
if (index < 0){ // Bug fixed
index = - 2 - index;
}
if (index >= 0 && index < arraySegments.length) { // Protection added
result = arraySegments[index].weight;
}
}
return result;
}
public static void main(String[] args) {
SegmentsAlgorithm algorithm = new SegmentsAlgorithm();
int[][] segments = {{0, 5},{3, 10},{8, 9},{14, 20},{12, 28}};
for (int[] segment : segments) {
algorithm.addSegment(segment[0], segment[1]);
}
algorithm.prepareArrayCache();
int[] points = {-1, 2, 4, 6, 11, 28};
for (int point: points) {
System.out.println(point + ": " + algorithm.searchPoint(point));
}
}
public static class SegmentWeight {
int begin;
int weight;
public SegmentWeight(int begin, int weight) {
this.begin = begin;
this.weight = weight;
}
}
}
It prints:
-1: 0
2: 1
4: 2
6: 1
11: 2
28: 0
EDITED:
public static void main(String[] args) {
SegmentsAlgorithm algorithm = new SegmentsAlgorithm();
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
for (int i = 0; i < n; i++) {
algorithm.addSegment(scanner.nextInt(), scanner.nextInt());
}
algorithm.prepareArrayCache();
for (int i = 0; i < m; i++) {
System.out.print(algorithm.searchPoint(scanner.nextInt())+ " ");
}
System.out.println();
}

Is this a proper implementation of mergesort?

I'm worried that the creation of 3 arrays for every recursion step might take up too much space, but I really couldn't figure out another way of doing it. Please tell me whatever is wrong with it.
public static int[] split(int [] vector){
if(vector.length <= 1 || vector == null)
return vector;
int len = vector.length;
int[] list1 = new int[len / 2];
// If the number of elements is odd the second list will be bigger
int[] list2 = new int[len / 2 + (len % 2)];
// Here we assign the elements to 2 separate lists
for(int x = 0; x < len / 2; x++)
list1[x] = vector[x];
for(int j = 0, i = len / 2; j < list2.length; i++, j++)
list2[j]=vector[i];
// Apply the recursion, this will eventually order the lists
list1 = split(list1);
list2 = split(list2);
// Here we take the 2 ordered lists and merge them into 1
int i = 0, a = 0, b = 0;
int[] listfinal = new int[len];
while(i < len){
if(a >= list1.length){
listfinal[i] = list2[b];
b++;
} else if(b >= list2.length){
listfinal[i] = list1[a];
a++;
} else if(list1[a] <= list2[b]){
listfinal[i] = list1[a];
a++;
} else if(list1[a] > list2[b]){
listfinal[i] = list2[b];
b++;
}
i++;
}
return listfinal; // Return the merged and ordered list
}
You shouldn't need to create more than one temporary array to do mergesort. What you're doing wrong is copying the arrays to pass to the recursive invocation; you should instead pass the original array.
It may be informative to look at the implementation of mergesort in the JDK - look on line 1146 of Arrays.java.
Here is code that allocates a single array equal to the input size at the top level and re-uses it for all the recursion. On a million integers, this takes about 300 ms on my machine and the Java library sort takes 230 ms. Okay for no tuning effort, I guess...
// Sort the elements of a between lo and hi inclusive.
private static void sortImpl(int [] a, int lo, int hi, int [] tmp) {
if (hi <= lo) return;
// Recur on sublists.
int mid = (hi + lo) / 2;
sortImpl(a, lo, mid, tmp);
sortImpl(a, mid + 1, hi, tmp);
// Move past items already in the right place.
int t1 = lo;
while (a[t1] < a[mid + 1]) t1++;
// Merge sublists into result.
int p1 = t1;
int p2 = mid + 1;
int i = t1;
System.arraycopy(a, t1, tmp, t1, mid - t1 + 1);
while (p1 <= mid)
a[i++] = (p2 > hi || tmp[p1] < a[p2]) ? tmp[p1++] : a[p2++];
}
public static void sort(int [] a) {
sortImpl(a, 0, a.length - 1, new int[a.length]);
}

How to merge two sorted arrays into a sorted array? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
This was asked of me in an interview and this is the solution I provided:
public static int[] merge(int[] a, int[] b) {
int[] answer = new int[a.length + b.length];
int i = 0, j = 0, k = 0;
while (i < a.length && j < b.length)
{
if (a[i] < b[j])
{
answer[k] = a[i];
i++;
}
else
{
answer[k] = b[j];
j++;
}
k++;
}
while (i < a.length)
{
answer[k] = a[i];
i++;
k++;
}
while (j < b.length)
{
answer[k] = b[j];
j++;
k++;
}
return answer;
}
Is there a more efficient way to do this?
Edit: Corrected length methods.
public static int[] merge(int[] a, int[] b) {
int[] answer = new int[a.length + b.length];
int i = 0, j = 0, k = 0;
while (i < a.length && j < b.length)
answer[k++] = a[i] < b[j] ? a[i++] : b[j++];
while (i < a.length)
answer[k++] = a[i++];
while (j < b.length)
answer[k++] = b[j++];
return answer;
}
Is a little bit more compact but exactly the same!
I'm surprised no one has mentioned this much more cool, efficient and compact implementation:
public static int[] merge(int[] a, int[] b) {
int[] answer = new int[a.length + b.length];
int i = a.length - 1, j = b.length - 1, k = answer.length;
while (k > 0)
answer[--k] =
(j < 0 || (i >= 0 && a[i] >= b[j])) ? a[i--] : b[j--];
return answer;
}
Points of Interests
Notice that it does same or less number of operations as any other O(n) algorithm but in literally single statement in a single while loop!
If two arrays are of approximately same size then constant for O(n) is same. However if arrays are really imbalanced then versions with System.arraycopy would win because internally it can do this with single x86 assembly instruction.
Notice a[i] >= b[j] instead of a[i] > b[j]. This guarantees "stability" that is defined as when elements of a and b are equal, we want elements from a before b.
A minor improvement, but after the main loop, you could use System.arraycopy to copy the tail of either input array when you get to the end of the other. That won't change the O(n) performance characteristics of your solution, though.
Any improvements that could be made would be micro-optimizations, the overall algorithm is correct.
This solution also very similar to other posts except that it uses System.arrayCopy to copy the remaining array elements.
private static int[] sortedArrayMerge(int a[], int b[]) {
int result[] = new int[a.length +b.length];
int i =0; int j = 0;int k = 0;
while(i<a.length && j <b.length) {
if(a[i]<b[j]) {
result[k++] = a[i];
i++;
} else {
result[k++] = b[j];
j++;
}
}
System.arraycopy(a, i, result, k, (a.length -i));
System.arraycopy(b, j, result, k, (b.length -j));
return result;
}
Here is updated function. It removes duplicates, hopefully someone will find this usable:
public static long[] merge2SortedAndRemoveDublicates(long[] a, long[] b) {
long[] answer = new long[a.length + b.length];
int i = 0, j = 0, k = 0;
long tmp;
while (i < a.length && j < b.length) {
tmp = a[i] < b[j] ? a[i++] : b[j++];
for ( ; i < a.length && a[i] == tmp; i++);
for ( ; j < b.length && b[j] == tmp; j++);
answer[k++] = tmp;
}
while (i < a.length) {
tmp = a[i++];
for ( ; i < a.length && a[i] == tmp; i++);
answer[k++] = tmp;
}
while (j < b.length) {
tmp = b[j++];
for ( ; j < b.length && b[j] == tmp; j++);
answer[k++] = tmp;
}
return Arrays.copyOf(answer, k);
}
It can be done in 4 statements as below
int a[] = {10, 20, 30};
int b[]= {9, 14, 11};
int res[]=new int[a.legth+b.length];
System.arraycopy(a,0, res, 0, a.length);
System.arraycopy(b,0,res,a.length, b.length);
Array.sort(res)
GallopSearch Merge: O(log(n)*log(i)) rather than O(n)
I went ahead and implemented greybeard suggestion in the comments. Mostly because I needed a highly efficient mission critical version of this code.
The code uses a gallopSearch which is O(log(i)) where i is the
distance from the current index the relevant index exists.
The code uses a binarySearch for after the gallop search has
identified the proper,range. Since gallop limited this to a smaller
range the resulting binarySearch is also O(log(i))
The gallop and merge are performed backwards. This doesn't seem
mission critical but it allows in place merging of arrays. If one of
your arrays has enough room to store the results values, you can
simply use it as the merging array and the results array. You must specify the valid range within the array in such a case.
It does not require memory allocation in that case (big savings in critical operations). It simply makes sure it doesn't and cannot overwrite any unprocessed values (which can only be done backwards). In fact, you use the same array for both of the inputs and the results. It will suffer no ill effects.
I consistently used Integer.compare() so this could be switched out for other purposes.
There's some chance I might have goofed a little and not utilized information I have previously proven. Such as binary searching into a range of two values, for which one value was already checked. There might also be a better way to state the main loop, the flipping c value wouldn't be needed if they were combined into two operations in sequence. Since you know you will do one then the other everytime. There's room for for some polish.
This should be the most efficient way to do this, with time complexity of O(log(n)*log(i)) rather than O(n). And worst case time complexity of O(n). If your arrays are clumpy and have long strings of values together, this will dwarf any other way to do it, otherwise it'll just be better than them.
It has two read values at the ends of the merging array and the write value within the results array. After finding out which is end value is less, it does a gallop search into that array. 1, 2, 4, 8, 16, 32, etc. When it finds the range where the the other array's read value is bigger. It binary searches into that range (cuts the range in half, search the correct half, repeat until single value). Then it array copies those values into the write position. Keeping in mind that the copy is, by necessity, moved such that it cannot overwrite the same values from the either reading array (which means the write array and read array can be the same). It then performs the same operation for the other array which is now known to be less than the new read value of the other array.
static public int gallopSearch(int current, int[] array, int v) {
int d = 1;
int seek = current - d;
int prevIteration = seek;
while (seek > 0) {
if (Integer.compare(array[seek], v) <= 0) {
break;
}
prevIteration = seek;
d <<= 1;
seek = current - d;
if (seek < 0) {
seek = 0;
}
}
if (prevIteration != seek) {
seek = binarySearch(array, seek, prevIteration, v);
seek = seek >= 0 ? seek : ~seek;
}
return seek;
}
static public int binarySearch(int[] list, int fromIndex, int toIndex, int v) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = list[mid];
int cmp = Integer.compare(midVal, v);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return mid;// key found
}
}
return -(low + 1);// key not found.
}
static public int[] sortedArrayMerge(int[] a, int[] b) {
return sortedArrayMerge(null, a, a.length, b, b.length);
}
static public int[] sortedArrayMerge(int[] results, int[] a, int aRead, int b[], int bRead) {
int write = aRead + bRead, length, gallopPos;
if ((results == null) || (results.length < write)) {
results = new int[write];
}
if (aRead > 0 && bRead > 0) {
int c = Integer.compare(a[aRead - 1], b[bRead - 1]);
while (aRead > 0 && bRead > 0) {
switch (c) {
default:
gallopPos = gallopSearch(aRead, a, b[bRead-1]);
length = (aRead - gallopPos);
write -= length;
aRead = gallopPos;
System.arraycopy(a, gallopPos--, results, write, length);
c = -1;
break;
case -1:
gallopPos = gallopSearch(bRead, b, a[aRead-1]);
length = (bRead - gallopPos);
write -= length;
bRead = gallopPos;
System.arraycopy(b, gallopPos--, results, write, length);
c = 1;
break;
}
}
}
if (bRead > 0) {
if (b != results) {
System.arraycopy(b, 0, results, 0, bRead);
}
} else if (aRead > 0) {
if (a != results) {
System.arraycopy(a, 0, results, 0, aRead);
}
}
return results;
}
This should be the most efficient way to do it.
Some answers had a duplicate remove ability. That'll require an O(n) algorithm because you must actually compare each item. So here's a stand-alone for that, to be applied after the fact. You can't gallop through multiple entries all the way through if you need to look at all of them, though you could gallop through the duplicates, if you had a lot of them.
static public int removeDuplicates(int[] list, int size) {
int write = 1;
for (int read = 1; read < size; read++) {
if (list[read] == list[read - 1]) {
continue;
}
list[write++] = list[read];
}
return write;
}
Update: Previous answer, not horrible code but clearly inferior to the above.
Another needless hyper-optimization. It not only invokes arraycopy for the end bits, but also for the beginning. Processing any introductory non-overlap in O(log(n)) by a binarySearch into the data. O(log(n) + n) is O(n) and in some cases the effect will be pretty pronounced especially things like where there is no overlap between the merging arrays at all.
private static int binarySearch(int[] array, int low, int high, int v) {
high = high - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = array[mid];
if (midVal > v)
low = mid + 1;
else if (midVal < v)
high = mid - 1;
else
return mid; // key found
}
return low;//traditionally, -(low + 1); // key not found.
}
private static int[] sortedArrayMerge(int a[], int b[]) {
int result[] = new int[a.length + b.length];
int k, i = 0, j = 0;
if (a[0] > b[0]) {
k = i = binarySearch(b, 0, b.length, a[0]);
System.arraycopy(b, 0, result, 0, i);
} else {
k = j = binarySearch(a, 0, a.length, b[0]);
System.arraycopy(a, 0, result, 0, j);
}
while (i < a.length && j < b.length) {
result[k++] = (a[i] < b[j]) ? a[i++] : b[j++];
}
if (j < b.length) {
System.arraycopy(b, j, result, k, (b.length - j));
} else {
System.arraycopy(a, i, result, k, (a.length - i));
}
return result;
}
I had to write it in javascript, here it is:
function merge(a, b) {
var result = [];
var ai = 0;
var bi = 0;
while (true) {
if ( ai < a.length && bi < b.length) {
if (a[ai] < b[bi]) {
result.push(a[ai]);
ai++;
} else if (a[ai] > b[bi]) {
result.push(b[bi]);
bi++;
} else {
result.push(a[ai]);
result.push(b[bi]);
ai++;
bi++;
}
} else if (ai < a.length) {
result.push.apply(result, a.slice(ai, a.length));
break;
} else if (bi < b.length) {
result.push.apply(result, b.slice(bi, b.length));
break;
} else {
break;
}
}
return result;
}
Apache collections supports collate method since version 4; you can do this using the collate method in:
org.apache.commons.collections4.CollectionUtils
Here quote from javadoc:
collate(Iterable<? extends O> a, Iterable<? extends O> b, Comparator<? super O> c)
Merges two sorted Collections, a and b, into a single,
sorted List such that the ordering of the elements according to
Comparator c is retained.
Do not re-invent the wheel! Document reference:
http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html
Here's a shortened form written in javascript:
function sort( a1, a2 ) {
var i = 0
, j = 0
, l1 = a1.length
, l2 = a2.length
, a = [];
while( i < l1 && j < l2 ) {
a1[i] < a2[j] ? (a.push(a1[i]), i++) : (a.push( a2[j]), j++);
}
i < l1 && ( a = a.concat( a1.splice(i) ));
j < l2 && ( a = a.concat( a2.splice(j) ));
return a;
}
public class Merge {
// stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
public static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
// precondition: a[lo .. mid] and a[mid+1 .. hi] are sorted subarrays
assert isSorted(a, lo, mid);
assert isSorted(a, mid+1, hi);
// copy to aux[]
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
// merge back to a[]
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++) {
if (i > mid) a[k] = aux[j++];
else if (j > hi) a[k] = aux[i++];
else if (less(aux[j], aux[i])) a[k] = aux[j++];
else a[k] = aux[i++];
}
// postcondition: a[lo .. hi] is sorted
assert isSorted(a, lo, hi);
}
// mergesort a[lo..hi] using auxiliary array aux[lo..hi]
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
public static void sort(Comparable[] a) {
Comparable[] aux = new Comparable[a.length];
sort(a, aux, 0, a.length-1);
assert isSorted(a);
}
/***********************************************************************
* Helper sorting functions
***********************************************************************/
// is v < w ?
private static boolean less(Comparable v, Comparable w) {
return (v.compareTo(w) < 0);
}
// exchange a[i] and a[j]
private static void exch(Object[] a, int i, int j) {
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}
/***********************************************************************
* Check if array is sorted - useful for debugging
***********************************************************************/
private static boolean isSorted(Comparable[] a) {
return isSorted(a, 0, a.length - 1);
}
private static boolean isSorted(Comparable[] a, int lo, int hi) {
for (int i = lo + 1; i <= hi; i++)
if (less(a[i], a[i-1])) return false;
return true;
}
/***********************************************************************
* Index mergesort
***********************************************************************/
// stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
private static void merge(Comparable[] a, int[] index, int[] aux, int lo, int mid, int hi) {
// copy to aux[]
for (int k = lo; k <= hi; k++) {
aux[k] = index[k];
}
// merge back to a[]
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++) {
if (i > mid) index[k] = aux[j++];
else if (j > hi) index[k] = aux[i++];
else if (less(a[aux[j]], a[aux[i]])) index[k] = aux[j++];
else index[k] = aux[i++];
}
}
// return a permutation that gives the elements in a[] in ascending order
// do not change the original array a[]
public static int[] indexSort(Comparable[] a) {
int N = a.length;
int[] index = new int[N];
for (int i = 0; i < N; i++)
index[i] = i;
int[] aux = new int[N];
sort(a, index, aux, 0, N-1);
return index;
}
// mergesort a[lo..hi] using auxiliary array aux[lo..hi]
private static void sort(Comparable[] a, int[] index, int[] aux, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, index, aux, lo, mid);
sort(a, index, aux, mid + 1, hi);
merge(a, index, aux, lo, mid, hi);
}
// print array to standard output
private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
StdOut.println(a[i]);
}
}
// Read strings from standard input, sort them, and print.
public static void main(String[] args) {
String[] a = StdIn.readStrings();
Merge.sort(a);
show(a);
}
}
I think introducing the skip list for the larger sorted array can reduce the number of comparisons and can speed up the process of copying into the third array. This can be good if the array is too huge.
public int[] merge(int[] a, int[] b) {
int[] result = new int[a.length + b.length];
int aIndex, bIndex = 0;
for (int i = 0; i < result.length; i++) {
if (aIndex < a.length && bIndex < b.length) {
if (a[aIndex] < b[bIndex]) {
result[i] = a[aIndex];
aIndex++;
} else {
result[i] = b[bIndex];
bIndex++;
}
} else if (aIndex < a.length) {
result[i] = a[aIndex];
aIndex++;
} else {
result[i] = b[bIndex];
bIndex++;
}
}
return result;
}
public static int[] merge(int[] a, int[] b) {
int[] mergedArray = new int[(a.length + b.length)];
int i = 0, j = 0;
int mergedArrayIndex = 0;
for (; i < a.length || j < b.length;) {
if (i < a.length && j < b.length) {
if (a[i] < b[j]) {
mergedArray[mergedArrayIndex] = a[i];
i++;
} else {
mergedArray[mergedArrayIndex] = b[j];
j++;
}
} else if (i < a.length) {
mergedArray[mergedArrayIndex] = a[i];
i++;
} else if (j < b.length) {
mergedArray[mergedArrayIndex] = b[j];
j++;
}
mergedArrayIndex++;
}
return mergedArray;
}
Algorithm could be enhanced in many ways. For instance, it is reasonable to check, if a[m-1]<b[0] or b[n-1]<a[0].
In any of those cases, there is no need to do more comparisons.
Algorithm could just copy source arrays in the resulting one in the right order.
More complicated enhancements may include searching for interleaving parts and run merge algorithm for them only.
It could save up much time, when sizes of merged arrays differ in scores of times.
This problem is related to the mergesort algorithm, in which two sorted sub-arrays are combined into a single sorted sub-array. The CLRS book gives an example of the algorithm and cleans up the need for checking if the end has been reached by adding a sentinel value (something that compares and "greater than any other value") to the end of each array.
I wrote this in Python, but it should translate nicely to Java too:
def func(a, b):
class sentinel(object):
def __lt__(*_):
return False
ax, bx, c = a[:] + [sentinel()], b[:] + [sentinel()], []
i, j = 0, 0
for k in range(len(a) + len(b)):
if ax[i] < bx[j]:
c.append(ax[i])
i += 1
else:
c.append(bx[j])
j += 1
return c
You could use 2 threads to fill the resulting array, one from front, one from back.
This can work without any synchronization in the case of numbers, e.g. if each thread inserts half of the values.
//How to merge two sorted arrays into a sorted array without duplicates?
//simple C Coding
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
int InputArray1[] ={1,4,5,7,8,9,12,13,14,17,40};
int InputArray2[] ={4,5,11,14,15,17,18,19,112,122,122,122,122};
int n=10;
int OutputArray[30];
int i=0,j=0,k=0;
//k=OutputArray
while(i<11 && j<13)
{
if(InputArray1[i]<InputArray2[j])
{
if (k == 0 || InputArray1[i]!= OutputArray[k-1])
{
OutputArray[k++] = InputArray1[i];
}
i=i+1;
}
else if(InputArray1[i]>InputArray2[j])
{
if (k == 0 || InputArray2[j]!= OutputArray[k-1])
{
OutputArray[k++] = InputArray2[j];
}
j=j+1;
}
else
{
if (k == 0 || InputArray1[i]!= OutputArray[k-1])
{
OutputArray[k++] = InputArray1[i];
}
i=i+1;
j=j+1;
}
};
while(i<11)
{
if(InputArray1[i]!= OutputArray[k-1])
OutputArray[k++] = InputArray1[i++];
else
i++;
}
while(j<13)
{
if(InputArray2[j]!= OutputArray[k-1])
OutputArray[k++] = InputArray2[j++];
else
j++;
}
for(i=0; i<k; i++)
{
printf("sorted data:%d\n",OutputArray[i]);
};
}
var arrCombo = function(arr1, arr2){
return arr1.concat(arr2).sort(function(x, y) {
return x - y;
});
};
My favorite programming language is JavaScript
function mergeSortedArrays(a, b){
var result = [];
var sI = 0;
var lI = 0;
var smallArr;
var largeArr;
var temp;
if(typeof b[0] === 'undefined' || a[0]<b[0]){
smallArr = a;
largeArr = b;
} else{
smallArr = b;
largeArr = a;
}
while(typeof smallArr[sI] !== 'undefined'){
result.push(smallArr[sI]);
sI++;
if(smallArr[sI]>largeArr[lI] || typeof smallArr[sI] === 'undefined'){
temp = smallArr;
smallArr = largeArr;
largeArr = temp;
temp = sI;
sI = lI;
lI = temp;
}
}
return result;
}
Maybe use System.arraycopy
public static byte[] merge(byte[] first, byte[] second){
int len = first.length + second.length;
byte[] full = new byte[len];
System.arraycopy(first, 0, full, 0, first.length);
System.arraycopy(second, 0, full, first.length, second.length);
return full;
}
public static void main(String[] args) {
int[] arr1 = {2,4,6,8,10,999};
int[] arr2 = {1,3,5,9,100,1001};
int[] arr3 = new int[arr1.length + arr2.length];
int temp = 0;
for (int i = 0; i < (arr3.length); i++) {
if(temp == arr2.length){
arr3[i] = arr1[i-temp];
}
else if (((i-temp)<(arr1.length)) && (arr1[i-temp] < arr2[temp])){
arr3[i] = arr1[i-temp];
}
else{
arr3[i] = arr2[temp];
temp++;
}
}
for (int i : arr3) {
System.out.print(i + ", ");
}
}
Output is :
1, 2, 3, 4, 5, 6, 8, 9, 10, 100, 999, 1001,
You can use ternary operators for making the code a bit more compact
public static int[] mergeArrays(int[] a1, int[] a2) {
int[] res = new int[a1.length + a2.length];
int i = 0, j = 0;
while (i < a1.length && j < a2.length) {
res[i + j] = a1[i] < a2[j] ? a1[i++] : a2[j++];
}
while (i < a1.length) {
res[i + j] = a1[i++];
}
while (j < a2.length) {
res[i + j] = a2[j++];
}
return res;
}
public static int[] mergeSorted(int[] left, int[] right) {
System.out.println("merging " + Arrays.toString(left) + " and " + Arrays.toString(right));
int[] merged = new int[left.length + right.length];
int nextIndexLeft = 0;
int nextIndexRight = 0;
for (int i = 0; i < merged.length; i++) {
if (nextIndexLeft >= left.length) {
System.arraycopy(right, nextIndexRight, merged, i, right.length - nextIndexRight);
break;
}
if (nextIndexRight >= right.length) {
System.arraycopy(left, nextIndexLeft, merged, i, left.length - nextIndexLeft);
break;
}
if (left[nextIndexLeft] <= right[nextIndexRight]) {
merged[i] = left[nextIndexLeft];
nextIndexLeft++;
continue;
}
if (left[nextIndexLeft] > right[nextIndexRight]) {
merged[i] = right[nextIndexRight];
nextIndexRight++;
continue;
}
}
System.out.println("merged : " + Arrays.toString(merged));
return merged;
}
Just a small different from the original solution
To marge two sorted array in O(m+n) time complexity use below approach with one loop only.
m and n is length of first array and second array.
public class MargeSortedArray {
public static void main(String[] args) {
int[] array = new int[]{1,3,4,7};
int[] array2 = new int[]{2,5,6,8,12,45};
int[] newarry = margeToSortedArray(array, array2);
//newarray is marged array
}
// marge two sorted array with o(a+n) time complexity
public static int[] margeToSortedArray(int[] array, int[] array2) {
int newarrlen = array.length+array2.length;
int[] newarr = new int[newarrlen];
int pos1=0,pos2=0;
int len1=array.length, len2=array2.length;
for(int i =0;i<newarrlen;i++) {
if(pos1>=len1) {
newarr[i]=array2[pos2];
pos2++;
continue;
}
if(pos2>=len2) {
newarr[i]=array[pos1];
pos1++;
continue;
}
if(array[pos1]>array2[pos2]) {
newarr[i]=array2[pos2];
pos2++;
} else {
newarr[i]=array[pos1];
pos1++;
}
}
return newarr;
}
}
var arr1 = [2,10,20,30,100];
var arr2 = [2,4,5,6,7,8,9];
var j = 0;
var i =0;
var newArray = [];
for(var x=0;x< (arr1.length + arr2.length);x++){
if(arr1[i] >= arr2[j]){ //check if element arr2 is equal and less than arr1 element
newArray.push(arr2[j]);
j++;
}else if(arr1[i] < arr2[j]){ //check if element arr1 index value is less than arr2 element
newArray.push(arr1[i]);
i++;
}
else if(i == arr1.length || j < arr2.length){ // add remaining arr2 element
newArray.push(arr2[j]);
j++
}else{ // add remaining arr1 element
newArray.push(arr1[i]);
i++
}
}
console.log(newArray);
Since the question doesn't assume any specific language. Here is the solution in Python.
Assuming the arrays are already sorted.
Approach 1 - using numpy arrays:
import numpy
arr1 = numpy.asarray([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 15, 55])
arr2 = numpy.asarray([11, 32, 43, 45, 66, 76, 88])
array = numpy.concatenate((arr1,arr2), axis=0)
array.sort()
Approach 2 - Using list, assuming lists are sorted.
list_new = list1.extend(list2)
list_new.sort()
Here is my java implementation that remove duplicate.
public static int[] mergesort(int[] a, int[] b) {
int[] c = new int[a.length + b.length];
int i = 0, j = 0, k = 0, duplicateCount = 0;
while (i < a.length || j < b.length) {
if (i < a.length && j < b.length) {
if (a[i] == b[j]) {
c[k] = a[i];
i++;j++;duplicateCount++;
} else {
c[k] = a[i] < b[j] ? a[i++] : b[j++];
}
} else if (i < a.length) {
c[k] = a[i++];
} else if (j < a.length) {
c[k] = b[j++];
}
k++;
}
return Arrays.copyOf(c, c.length - duplicateCount);
}
import java.util.Arrays;
public class MergeTwoArrays {
static int[] arr1=new int[]{1,3,4,5,7,7,9,11,13,15,17,19};
static int[] arr2=new int[]{2,4,6,8,10,12,14,14,16,18,20,22};
public static void main(String[] args){
int FirstArrayLocation =0 ;
int SecondArrayLocation=0;
int[] mergeArr=new int[arr1.length + arr2.length];
for ( int i=0; i<= arr1.length + arr2.length; i++){
if (( FirstArrayLocation < arr1.length ) && (SecondArrayLocation < arr2.length)){
if ( arr1[FirstArrayLocation] <= arr2[SecondArrayLocation]){
mergeArr[i]=arr1[FirstArrayLocation];
FirstArrayLocation++;
}else{
mergeArr[i]=arr2[SecondArrayLocation];
SecondArrayLocation++;
}
}
else if(SecondArrayLocation < arr2.length){
mergeArr[i]=arr2[SecondArrayLocation];
SecondArrayLocation++;
}else if ( FirstArrayLocation < arr1.length ){
mergeArr[i]=arr1[FirstArrayLocation];
FirstArrayLocation++;
}
}
}
}

Categories

Resources