2D Peak Algorithm fails to find the peak - algorithm

I just started an MIT course on algorithm, and we were taught the 2D Peak Finding algo. I tried dry running and implementing it yet the algo seems to be failing for this input.
{5, 0, 3, 2}
{1, 1, 2, 4}
{1, 2, 4, 4}
This is the Algorithm:
• Pick middle column j = m/2
• Find global maximum on column j at (i,j)
• Compare(i,j−1),(i,j),(i,j+1)
• Pick left columns of(i,j−1)>(i,j)
• Similarly for right
• (i,j) is a 2D-peak if neither condition holds ← WHY?
• Solve the new problem with half the number of columns.
• When you have a single column, find global maximum and you‘re done.
Update, Here is the code which I tried and doesn't seem to be working:
#include <bits/stdc++.h>
using namespace std;
const int MAX = 100;
int findMax(int arr[][MAX], int rows, int mid, int& max)
{
int max_index = 0;
for (int i = 0; i < rows; i++) {
if (max < arr[i][mid]) {
max = arr[i][mid];
max_index = i;
}
}
return max_index;
}
int findPeakRec(int arr[][MAX], int rows, int columns, int mid)
{
int max = 0;
int max_index = findMax(arr, rows, mid, max);
if (mid == 0 || mid == columns - 1)
return max;
if (max >= arr[max_index][mid - 1] && max >= arr[max_index][mid + 1])
return max;
if (max < arr[max_index][mid - 1])
return findPeakRec(arr, rows, columns, mid - ceil((double)mid / 2));
return findPeakRec(arr, rows, columns, mid + ceil((double)mid / 2));
}
int findPeak(int arr[][MAX], int rows, int columns)
{
return findPeakRec(arr, rows, columns, columns / 2);
}
int main()
{
int arr[][MAX] = { { 5, 0, 3, 2 },
{ 1, 1, 2, 4 },
{ 1, 2, 4, 4 },
{ 3, 2, 0, 1 } };
int rows = 4, columns = 4;
cout << findPeak(arr, rows, columns);
return 0;
}
this is how I implemented the algorithm.

The algorithm is correct (Just a spelling mistake in the fourth bullet point: "of" should read "if").
You missed a correct definition of "peak". The algorithm for peak finding intends to find a local maximum, not necessarily the global maximum. For a global maximum the algorithm is trivial, you just look for the maximum value with a row by row scan.
But peak finding can be more efficient as not all values need to be inspected.

Related

How to get the number of the Subarray with at least K different numbers?

The question is:
"Given an array A only contains integers Return the number of subarrays that contain at least k different numbers. Subarrays cannot be duplicated."
Example:
input array = {1, 2, 3, 4, 2} k = 3
output: 4
Explanation:
the number of the Subarray with at least K different numbers should be 4,
which are [1, 2, 3] [2, 3, 4] [3, 4, 2] [1, 2, 3, 4]
Right now what I can do is just find about the number of the subarray with exactly K different numbers:
class Solution {
public int subarraysWithKDistinct(int[] A, int K) {
return atMostK(A, K) - atMostK(A, K - 1);
}
private int atMostK(int[] A, int K) {
int i = 0, res = 0;
Map<Integer, Integer> count = new HashMap<>();
for (int j = 0; j < A.length; ++j) {
if (count.getOrDefault(A[j], 0) == 0) K--;
count.put(A[j], count.getOrDefault(A[j], 0) + 1);
while (K < 0) {
count.put(A[i], count.get(A[i]) - 1);
if (count.get(A[i]) == 0) K++;
i++;
}
res += j - i + 1;
}
return res;
}
}
But when the input be:
array = {1, 2, 3, 4, 2} k = 2
my code will not work correctly, but I don't know where to change. Any thoughts? Thanks!
Update: thanks to #MBo and others' answers, I used 2 pointers to fix this problem, but still cannot get the right answer with:
array = {1, 2, 3, 4, 2} k = 3 -> output: 6 (should be 4)
It looks like there are some duplicated substrings be counted, but I don't know how to fix it.
class Solution {
public static void main(String[] args) {
int[] A = {1, 2, 3, 4, 2};
int k = 3;
int res = helper(A, k);
System.out.println(res);
// output is 6, but should be 4
}
private static int helper(int[] A, int k) {
if (A == null || A.length == 0) return 0;
int n = A.length;
int res = 0;
int differentNumbers = 0;
Map<Integer, Integer> counter = new HashMap<>();
int j = 0; // j - 1 is the right point
for (int i = 0; i < n; i ++) {
while (j < n && differentNumbers < k) {
int numOfThisNumber = counter.getOrDefault(A[j], 0);
counter.put(A[j], numOfThisNumber + 1);
if (counter.get(A[j]) == 1) {
differentNumbers ++;
}
j ++;
}
if (differentNumbers == k) {
res += n - j + 1;
}
counter.put(A[i], counter.get(A[i]) - 1);
if (counter.get(A[i]) == 0) {
differentNumbers --;
}
}
return res;
}
}
You can combine your hashmap approach with method of two pointers (indices).
Set both indices into 0 and move right one, updating hashmap counts for values at the right end of interval until hashmap size reaches K. Fix right index.
Now move left index, decreasing counts corresponding to the values at left end. Before every step (including left=0) add size-right to result, because all subarrays starting from left and ending after right, do contain needed number of elements.
When some count becomes 0, remove value from hashmap, and fix left index.
Repeat with right index and so on.

Partition a set into K subsets with equal sum

I'm going through an exercise to partition a set into K subsets with equal sum.
Let's say
Input : arr = [2, 1, 4, 5, 6], K = 3
Output : Yes
we can divide above array into 3 parts with equal
sum as [[2, 4], [1, 5], [6]]
I found a solution here,
http://www.geeksforgeeks.org/partition-set-k-subsets-equal-sum/
// C++ program to check whether an array can be
// partitioned into K subsets of equal sum
#include <bits/stdc++.h>
using namespace std;
// Recursive Utility method to check K equal sum
// subsetition of array
/**
array - given input array
subsetSum array - sum to store each subset of the array
taken - boolean array to check whether element
is taken into sum partition or not
K - number of partitions needed
N - total number of element in array
curIdx - current subsetSum index
limitIdx - lastIdx from where array element should
be taken */
bool isKPartitionPossibleRec(int arr[], int subsetSum[], bool taken[],
int subset, int K, int N, int curIdx, int limitIdx)
{
if (subsetSum[curIdx] == subset)
{
/* current index (K - 2) represents (K - 1) subsets of equal
sum last partition will already remain with sum 'subset'*/
if (curIdx == K - 2)
return true;
// recursive call for next subsetition
return isKPartitionPossibleRec(arr, subsetSum, taken, subset,
K, N, curIdx + 1, N - 1);
}
// start from limitIdx and include elements into current partition
for (int i = limitIdx; i >= 0; i--)
{
// if already taken, continue
if (taken[i])
continue;
int tmp = subsetSum[curIdx] + arr[i];
// if temp is less than subset then only include the element
// and call recursively
if (tmp <= subset)
{
// mark the element and include into current partition sum
taken[i] = true;
subsetSum[curIdx] += arr[i];
bool nxt = isKPartitionPossibleRec(arr, subsetSum, taken,
subset, K, N, curIdx, i - 1);
// after recursive call unmark the element and remove from
// subsetition sum
taken[i] = false;
subsetSum[curIdx] -= arr[i];
if (nxt)
return true;
}
}
return false;
}
// Method returns true if arr can be partitioned into K subsets
// with equal sum
bool isKPartitionPossible(int arr[], int N, int K)
{
// If K is 1, then complete array will be our answer
if (K == 1)
return true;
// If total number of partitions are more than N, then
// division is not possible
if (N < K)
return false;
// if array sum is not divisible by K then we can't divide
// array into K partitions
int sum = 0;
for (int i = 0; i < N; i++)
sum += arr[i];
if (sum % K != 0)
return false;
// the sum of each subset should be subset (= sum / K)
int subset = sum / K;
int subsetSum[K];
bool taken[N];
// Initialize sum of each subset from 0
for (int i = 0; i < K; i++)
subsetSum[i] = 0;
// mark all elements as not taken
for (int i = 0; i < N; i++)
taken[i] = false;
// initialize first subsubset sum as last element of
// array and mark that as taken
subsetSum[0] = arr[N - 1];
taken[N - 1] = true;
// call recursive method to check K-substitution condition
return isKPartitionPossibleRec(arr, subsetSum, taken,
subset, K, N, 0, N - 1);
}
// Driver code to test above methods
int main()
{
int arr[] = {2, 1, 4, 5, 3, 3};
int N = sizeof(arr) / sizeof(arr[0]);
int K = 3;
if (isKPartitionPossible(arr, N, K))
cout << "Partitions into equal sum is possible.\n";
else
cout << "Partitions into equal sum is not possible.\n";
}
This works in all scenarios.
Question
Let's say if I pass arr = [4, 4, 1, 3, 2, 3, 2, 1] and k = 4,the algorithm tried to solve it by adding 1+2+2 and then, 3+3 or 3+1 and so on. It doesn't gets the partition and finally solves it to [[4,1], [4,1], [3,2], [3,2]]. I am not sure how does this algorithm finds the alternative? I'm not able to follow up with the recursion.
What are the ways to solve it? Is the backtracking the only way?
Thanks!

Shell Sort and Confusion with gaps

I've recently seen and studied the shell-sort algorithm and the seen the example provided here. In the example they consider a 'inc' or 'gap'.
I made a c implementation of the algorithm, and taking the gap value as a parameter in the sort.
I am observing the fact that for 10 unsorted values which are :
5, 1, 3, 2, 4, 8, 6, 9, 7, 10
using any gap, I am getting the following output :
1 2 3 4 5 6 8 7 9 10
I've built a recursive version of the code, which is here :
void shellSort(int *arr, int size, int gap){
int i, tmp;
if (gap == 0) return;
for (i = 0; i < size / gap; i+=gap){
if (i < size - 1){//Valid Index
if (arr[i] > arr[i + gap]){
tmp = arr[i];
arr[i] = arr[i + gap];
arr[i + gap] = tmp;
}
}
}
printf("Interation : \n");
for (i = 0; i < 10; i++){
printf("%d\t", arr[i]);
}
printf("\n\n");
shellSort(arr, size, gap - 1);
}
Example :
int main()
{
int arr[] = { 5, 1, 3, 2, 4, 8, 6, 9, 7, 10 }, i;
shellSort(&arr[0], 10, 3);
getch();
return 0;
}
After reading through the material in the internet, I am completely confused about selecting this gap value, in some places like wikipedia, they are using a gap sequence. Some assistance would be appreciated.
After Correction,
for (i = 0; i < size - gap; i++)
Output:
1 3 2 4 5 6 8 7 9 10
This code is not shell sort, it is a kind of comb sort
In this line
for (i = 0; i < size / gap; i+=gap)
index i doesn't walk through the whole array. Possible correction:
for (i = 0; i < size - gap; i++)
For full sorting, you have to repeat sort with gap = 1 while swapping occurs. Examples
Gaps are described in every manual about shellsort or combsort.
In short - two indexes, separated by gap, walk through array, so unordered elements are moving closer to their places in sorted array. After every run gap decreases (in shellsort denominator is 2-3, in combsort - 1.3). Decrementing gap by 1 (as your code shows) makes the code very slow.
In the Shell-Sort Algorithm, you need to understand the concept of gap-sorting an array (or h-sorting).
Assume that you have a gap 'h'. You will begin with the first element tab[0] and sort all the elements spaced by h in your array: tab[0], tab[h], tab[2h] ... then you will pick the secend element as a begining and try to sort the spaced elements. Each time the starting element is incremented. To sort the h-spaced elements, you use the insertion sort algorithm.
After that choose a sequence for your h-insertion sort code starting from the biggest gap.
This is my implementation in java:
// Using Pratt sequence
public static void hSort(int tab[]) {
int N = tab.length;
int k = 0;
int sequence = 1;
// Getting the final term of the pratt sequence
while(((int)(Math.pow(3, k) - 1) / 2) < N/3) {
sequence = (int)(Math.pow(3, k) - 1) / 2;
k++;
}
k--;
while(sequence > 0) {
hInsertionSort(tab, sequence);
k--;
sequence = (int)(Math.pow(3, k) - 1) / 2;
}
}
public static void hInsertionSort(int[] tab, int h) {
int N = tab.length;
int k = 0;
while (k < h) {
for (int i = k; i < N; i+=h) {
for (int j = i; j > k+h-1; j-=h) {
if (less(tab[j], tab[j-h]) == -1) exch(tab, j, j-h);
else break;
}
}
k++;
}
}
private static inttab[1] (it means sort tab[1], tab[h+1], ...). This is h-sorting your array. less(int value1, int value2) {
if (value1 == value2) return 0;
if (value1 < value2) return -1;
else return 1;
}
tab[1] (it means sort tab[1], tab[h+1], ...). This is h-sorting your array.
private static void exch(int[] tab, int key1, int key2) {
int inter;
inter = tab[key1];
tab[key1] = tab[key2];
tab[key2] = inter;
}

Find the first "missing" number in a sorted list

Let's say I have the continuous range of integers [0, 1, 2, 4, 6], in which the 3 is the first "missing" number. I need an algorithm to find this first "hole". Since the range is very large (containing perhaps 2^32 entries), efficiency is important. The range of numbers is stored on disk; space efficiency is also a main concern.
What's the best time and space efficient algorithm?
Use binary search. If a range of numbers has no hole, then the difference between the end and start of the range will also be the number of entries in the range.
You can therefore begin with the entire list of numbers, and chop off either the first or second half based on whether the first half has a gap. Eventually you will come to a range with two entries with a hole in the middle.
The time complexity of this is O(log N). Contrast to a linear scan, whose worst case is O(N).
Based on the approach suggested by #phs above, here is the C code to do that:
#include <stdio.h>
int find_missing_number(int arr[], int len) {
int first, middle, last;
first = 0;
last = len - 1;
middle = (first + last)/2;
while (first < last) {
if ((arr[middle] - arr[first]) != (middle - first)) {
/* there is a hole in the first half */
if ((middle - first) == 1 && (arr[middle] - arr[first] > 1)) {
return (arr[middle] - 1);
}
last = middle;
} else if ((arr[last] - arr[middle]) != (last - middle)) {
/* there is a hole in the second half */
if ((last - middle) == 1 && (arr[last] - arr[middle] > 1)) {
return (arr[middle] + 1);
}
first = middle;
} else {
/* there is no hole */
return -1;
}
middle = (first + last)/2;
}
/* there is no hole */
return -1;
}
int main() {
int arr[] = {3, 5, 1};
printf("%d", find_missing_number(arr, sizeof arr/(sizeof arr[0]))); /* prints 4 */
return 0;
}
Since numbers from 0 to n - 1 are sorted in an array, the first numbers should be same as their indexes. That's to say, the number 0 is located at the cell with index 0, the number 1 is located at the cell with index 1, and so on. If the missing number is denoted as m. Numbers less then m are located at cells with indexes same as values.
The number m + 1 is located at a cell with index m, The number m + 2 is located at a cell with index m + 1, and so on. We can see that, the missing number m is the first cell whose value is not identical to its value.
Therefore, it is required to search in an array to find the first cell whose value is not identical to its value. Since the array is sorted, we could find it in O(lg n) time based on the binary search algorithm as implemented below:
int getOnceNumber_sorted(int[] numbers)
{
int length = numbers.length
int left = 0;
int right = length - 1;
while(left <= right)
{
int middle = (right + left) >> 1;
if(numbers[middle] != middle)
{
if(middle == 0 || numbers[middle - 1] == middle - 1)
return middle;
right = middle - 1;
}
else
left = middle + 1;
}
return -1;
}
This solution is borrowed from my blog: http://codercareer.blogspot.com/2013/02/no-37-missing-number-in-array.html.
Based on algorithm provided by #phs
int findFirstMissing(int array[], int start , int end){
if(end<=start+1){
return start+1;
}
else{
int mid = start + (end-start)/2;
if((array[mid] - array[start]) != (mid-start))
return findFirstMissing(array, start, mid);
else
return findFirstMissing(array, mid+1, end);
}
}
Below is my solution, which I believe is simple and avoids an excess number of confusing if-statements. It also works when you don't start at 0 or have negative numbers involved! The complexity is O(lg(n)) time with O(1) space, assuming the client owns the array of numbers (otherwise it's O(n)).
The Algorithm in C Code
int missingNumber(int a[], int size) {
int lo = 0;
int hi = size - 1;
// TODO: Use this if we need to ensure we start at 0!
//if(a[0] != 0) { return 0; }
// All elements present? If so, return next largest number.
if((hi-lo) == (a[hi]-a[lo])) { return a[hi]+1; }
// While 2 or more elements to left to consider...
while((hi-lo) >= 2) {
int mid = (lo + hi) / 2;
if((mid-lo) != (a[mid]-a[lo])) { // Explore left-hand side
hi = mid;
} else { // Explore right hand side
lo = mid + 1;
}
}
// Return missing value from the two candidates remaining...
return (lo == (a[lo]-a[0])) ? hi + a[0] : lo + a[0];
}
Test Outputs
int a[] = {0}; // Returns: 1
int a[] = {1}; // Returns: 2
int a[] = {0, 1}; // Returns: 2
int a[] = {1, 2}; // Returns: 3
int a[] = {0, 2}; // Returns: 1
int a[] = {0, 2, 3, 4}; // Returns: 1
int a[] = {0, 1, 2, 4}; // Returns: 3
int a[] = {0, 1, 2, 4, 5, 6, 7, 8, 9}; // Returns: 3
int a[] = {2, 3, 5, 6, 7, 8, 9}; // Returns: 4
int a[] = {2, 3, 4, 5, 6, 8, 9}; // Returns: 7
int a[] = {-3, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // Returns: -1
int a[] = {-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // Returns: 10
The general procedure is:
(Optional) Check if the array starts at 0. If it doesn't, return 0 as missing.
Check if the array of integers is complete with no missing integer. If it is not missing an integer, return the next largest integer.
In a binary search fashion, check for a mismatch between the difference in the indices and array values. A mismatch tells us which half a missing element is in. If there is a mismatch in the first half, move left, otherwise move right. Do this until you have two candidate elements left to consider.
Return the number that is missing based on incorrect candidate.
Note, the algorithm's assumptions are:
First and last elements are considered to never be missing. These elements establish a range.
Only one integer is ever missing in the array. This will not find more than one missing integer!
Integer in the array are expected to increase in steps of 1, not at any other rate.
Have you considered a run-length encoding? That is, you encode the first number as well as the count of numbers that follow it consecutively. Not only can you represent the numbers used very efficiently this way, the first hole will be at the end of the first run-length encoded segment.
To illustrate with your example:
[0, 1, 2, 4, 6]
Would be encoded as:
[0:3, 4:1, 6:1]
Where x:y means there is a set of numbers consecutively starting at x for y numbers in a row. This tells us immediately that the first gap is at location 3. Note, however, that this will be much more efficient when the assigned addresses are clustered together, not randomly dispersed throughout the range.
if the list is sorted, I'd iterate over the list and do something like this Python code:
missing = []
check = 0
for n in numbers:
if n > check:
# all the numbers in [check, n) were not present
missing += range(check, n)
check = n + 1
# now we account for any missing numbers after the last element of numbers
if check < MAX:
missing += range(check, MAX + 1)
if lots of numbers are missing, you might want to use #Nathan's run-length encoding suggestion for the missing list.
Missing
Number=(1/2)(n)(n+1)-(Sum of all elements in the array)
Here n is the size of array+1.
Array: [1,2,3,4,5,6,8,9]
Index: [0,1,2,3,4,5,6,7]
int findMissingEmementIndex(int a[], int start, int end)
{
int mid = (start + end)/2;
if( Math.abs(a[mid] - a[start]) != Math.abs(mid - start) ){
if( Math.abs(mid - start) == 1 && Math.abs(a[mid] - a[start])!=1 ){
return start +1;
}
else{
return findMissingElmementIndex(a,start,mid);
}
}
else if( a[mid] - a[end] != end - start){
if( Math.abs(end - mid) ==1 && Math.abs(a[end] - a[mid])!=1 ){
return mid +1;
}
else{
return findMissingElmementIndex(a,mid,end);
}
}
else{
return No_Problem;
}
}
This is an interview Question. We will have an array of more than one missing numbers and we will put all those missing numbers in an ArrayList.
public class Test4 {
public static void main(String[] args) {
int[] a = { 1, 3, 5, 7, 10 };
List<Integer> list = new ArrayList<>();
int start = 0;
for (int i = 0; i < a.length; i++) {
int ch = a[i];
if (start == ch) {
start++;
} else {
list.add(start);
start++;
i--; // a must do.
} // else
} // for
System.out.println(list);
}
}
Functional Programming solution (Scala)
Nice and elegant
Lazy evaluation
def gapFinder(sortedList: List[Int], start: Int = 0): Int = {
def withGuards: Stream[Int] =
(start - 1) +: sortedList.toStream :+ (sortedList.last + 2)
if (sortedList.isEmpty) start
else withGuards.sliding(2)
.dropWhile { p => p.head + 1 >= p.last }.next()
.headOption.getOrElse(start) + 1
} // 8-line solution
// Tests
assert(gapFinder(List()) == 0)
assert(gapFinder(List[Int](0)) == 1)
assert(gapFinder(List[Int](1)) == 0)
assert(gapFinder(List[Int](2)) == 0)
assert(gapFinder(List[Int](0, 1, 2)) == 3)
assert(gapFinder(List[Int](0, 2, 4)) == 1)
assert(gapFinder(List[Int](0, 1, 2, 4)) == 3)
assert(gapFinder(List[Int](0, 1, 2, 4, 5)) == 3)
import java.util.Scanner;
class MissingNumber {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] arr =new int[n];
for (int i=0;i<n;i++){
arr[i]=scan.nextInt();
}
for (int i=0;i<n;i++){
if(arr[i+1]==arr[i]+1){
}
else{
System.out.println(arr[i]+1);
break;
}
}
}
}
I was looking for a super simple way to find the first missing number in a sorted array with a max potential value in javascript and didn't have to worry about efficiency too much as I didn't plan on using a list longer 10-20 items at the most. This is the recursive function I came up with:
function findFirstMissingNumber(sortedList, index, x, maxAllowedValue){
if(sortedList[index] == x && x < maxAllowedValue){
return findFirstMissingNumber(sortedList, (index+1), (x+1), maxAllowedValue);
}else{ return x; }
}
findFirstMissingNumber([3, 4, 5, 7, 8, 9], 0, 3, 10);
//expected output: 6
Give it your array, the index you wish to start at, the value you expect it to be and the maximum value you'd like to check up to.
i got one algorithm for finding the missing number in the sorted list. its complexity is logN.
public int execute2(int[] array) {
int diff = Math.min(array[1]-array[0], array[2]-array[1]);
int min = 0, max = arr.length-1;
boolean missingNum = true;
while(min<max) {
int mid = (min + max) >>> 1;
int leftDiff = array[mid] - array[min];
if(leftDiff > diff * (mid - min)) {
if(mid-min == 1)
return (array[mid] + array[min])/2;
max = mid;
missingNum = false;
continue;
}
int rightDiff = array[max] - array[mid];
if(rightDiff > diff * (max - mid)) {
if(max-mid == 1)
return (array[max] + array[mid])/2;
min = mid;
missingNum = false;
continue;
}
if(missingNum)
break;
}
return -1;
}
Based on algorithm provided by #phs
public class Solution {
public int missing(int[] array) {
// write your solution here
if(array == null){
return -1;
}
if (array.length == 0) {
return 1;
}
int left = 0;
int right = array.length -1;
while (left < right - 1) {
int mid = left + (right - left) / 2;
if (array[mid] - array[left] != mid - left) { //there is gap in [left, mid]
right = mid;
}else if (array[right] - array[mid] != right - mid) { //there is gap in [mid, right]
left = mid;
}else{ //there is no gapin [left, right], which means the missing num is the at 0 and N
return array[0] == 1 ? array.length + 1 : 1 ;
}
}
if (array[right] - array[left] == 2){ //missing number is between array[left] and array[right]
return left + 2;
}else{
return array[0] == 1 ? -1 : 1; //when ther is only one element in array
}
}
}
public static int findFirst(int[] arr) {
int l = -1;
int r = arr.length;
while (r - l > 1) {
int middle = (r + l) / 2;
if (arr[middle] > middle) {
r = middle;
}
l = middle;
}
return r;
}

how to merge two sorted integer array in place using O(n) time and O(1) space cost

For example, given an integer array and its two consecutive sequence 's beginning position which are 'b1' and 'b2', furthermore provided with the position 'last' which indicates the second sequence's ending position. From array[b1] to array [b2-1] and from array [b2] to array[last] are both in order separately, how to merge them in place using O(n) time and O(1) space cost?
Kronrod's merge was the first published algorithm to do that. It goes roughly like this:
Split both parts of the array into blocks of size k=sqrt(n). Sort the blocks using their first elements as the basis for comparison. This can be done in sqrt(n)^2=O(n) by selection sort. The key property of selection sort here is that it has constant moves per block, so only #comparisons is square.
After this phase, for each element A[i] in the array there are at most k-1 elements "wrongly sorted" below it, that is elements at positions j<i such that A[j]>A[i]. These are (possibly) in the closest block below it that comes from the other merged part. Note that the first element of the block (and all other blocks below it) are already properly sorted relative to A[i] because of the blocks being sorted on their first elements. This is why the second phase works, i.e. achieves the fully sorted array:
Now merge the first block with the second, then second with the third, etc., using the last 2 blocks as temporary space for the output of the merge. This will scramble the contents of the last two blocks but in the last phase they (together with the preceding block) can be sorted by selection sort in sqrt(n)^2=O(n) time.
This is by no means a simple problem It is possible, but rarely done in practice because it's so much more complicated than a standard merge using N-scratch space. Huang and Langston's paper has been around since the late 80's, though practical implementations didn't really surface until later. Earlier, L. Trabb-Prado's paper in 1977 predates Huang and Langston significantly, but I'm challanged to find the exact text that paper; only references abound.
An excellent later publication, Asymptotically efficient in-place merging (1995) by Geert, Katajainenb, and Pasanen is a good coverage of multiple algorithms, and references Trabb-Prado's contributions to the subject.
There are such things as true in-place merges, but they are not straightforward enough that anybody is going to independently reinvent them in the middle of an interview - there have been papers describing a succession of pretty complex algorithms for this for years. One is Practical In-Place Merging, by Huang and Langston, CACM March 1988. The starting idea for this is to divide the data of length n into blocks of size sqrt(n), and use one block, filled with the largest elements of the data, to provide buffer space used in merging the others. The introduction to that paper says
"Given two sorted lists whose lengths sum to n, the obvious methods for merging in O(n) steps require a linear amount of extra memory as well. On the other hand, it is easy to merge in place using only a constant amount of additional space by heap-sorting, but at a cost of O(n log n) time"
Hence I claim that true merging in place can be done but is non-obvious.
Though it is not possible entirely in O(n) time, I have a proposition to do it faster than O(n^2). I use only O(1) space which is temp in my code. I am sure it should run better than O(n^2).
private static int[] mergeSortedArrays(int[] a1, int[] a2) {
int i = 0, j = 0;
while (a1[i] != Integer.MIN_VALUE) {
if (a1[i] > a2[j]) {
int temp = a1[i];
a1[i] = a2[j];
a2[j] = temp;
for (int k = 1; k < a2.length; k++) {
if (a2[k - 1] > a2[k]) {
temp = a2[k - 1];
a2[k - 1] = a2[k];
a2[k] = temp;
}
}
}
i++;
}
while(j < a2.length){
a1[i++] = a2[j++];
}
return a1;
}
Here is O(n-1) Memory (n+1)
/**
* Created by deian on 2016-12-22.
* We just need track the two smallest numbers
*/
public class Merge {
public static void swap(int[] a, int i1, int i2) {
int t = a[i1];
a[i1] = a[i2];
a[i2] = t;
}
public static void merge(int[] a) {
// i1 and i2 - always point to the smallest known numbers
// it would works as well with two m and n sized arrays
int i1 = 0;
int i2 = a.length / 2;
System.out.printf(" %s, i(%d,%d) \n", Arrays.toString(a), i1, i2);
for (int di = 0; di < a.length - 1; di++) {
int ni;
int oi1 = i1; int oi2 = i2;
if (a[i1] > a[i2]) {
ni = i2; i2++;
if (i2 >= a.length) { i2--; }
} else {
ni = i1; i1++;
if (i1 >= i2) { i1 = di; }
}
if (di == i1) { i1 = ni; }
swap(a, di, ni);
System.out.printf("#%d: %s, i(%d,%d)s(%d>%d)i(%d,%d) \n", di + 1, Arrays.toString(a), oi1, oi2, ni, di, i1, i2);
}
System.out.printf(" %s\n", Arrays.toString(a));
}
public static void main(String[] args) {
// int[] a = new int[]{1, 3, 6, 8, -5, -2, 3, 8};
// int[] a = new int[]{1, 3, 6, 8, -5, 2, 3, 8};
// int[] a = new int[]{1, 5, 6, 8, -5, 2, 3, 4};
// int[] a = new int[]{1, 5, 6, 8, -5, -2, -1, 4};
// int[] a = new int[]{ 1, 2, 3, 4, 5, 6, 7, 8};
// int[] a = new int[]{5, 6, 7, 8, 1, 2, 3, 4};
int[] a = new int[]{1, 3, 5, 7, 2, 4, 6, 8};
merge(a);
}
}
I had an interview (with a very important company) a couple of hours ago and I was asked that.
There is the answer in Java
public static void main(String[] args) {
int A[] = { 1, 3, 5, 6, 9 };
int B[] = new int[12];
B[0] = 3;
B[1] = 6;
B[2] = 8;
B[3] = 10;
B[4] = 11;
B[5] = 13;
B[6] = 15;
mergeInB(A, B, 7);
for (int n : B)
System.out.print(n + " ");
}
/**
* #param a
* #param b - it will be modified
* #param j = length of b
*/
public static void mergeInB(int[] a, int[] b, int j) {
int i = a.length - 1, k;
j --;
for (k = b.length-1; k >= 0; k--) {
if (i >= 0 && j >= 0) {
if (a[i] > b[j]) {
b[k] = a[i];
i --;
}
else
{
b[k] = b[j];
j --;
}
}
else break;
}
while(i>=0 && k >=0) {
b[k] = a[i];
k --;
i --;
}
while(j>= 0 && k >=0) {
b[k] = b[j];
j--;
k--;
}
}

Resources