如何使用合并排序算法进行就地排序?

时间:2010-04-03 11:04:17

标签: arrays algorithm sorting mergesort in-place

我知道问题不是太具体。

我想要的是告诉我如何将普通合并排序转换为就地合并排序(或具有恒定额外空间开销的合并排序)。

我所能找到的(网上)是“太复杂”或“超出本文范围”的网页。

  

就地合并(没有任何额外空间)的唯一已知方法太复杂,无法简化为实际程序。 (采取from here

即使它太复杂,如何使合并排序到位的基本概念是什么?

11 个答案:

答案 0 :(得分:124)

Knuth将此作为练习(Vol 3,5.2.5)。确实存在就地合并排序。必须谨慎实施。

首先,天真的就地合并(例如描述的here)不是正确的解决方案。它将性能降级为 O(N 2

我们的想法是对数组的一部分进行排序,同时将其余部分用作合并的工作区域。

例如,作为以下合并功能。

void wmerge(Key* xs, int i, int m, int j, int n, int w) {
    while (i < m && j < n)
        swap(xs, w++, xs[i] < xs[j] ? i++ : j++);
    while (i < m)
        swap(xs, w++, i++);
    while (j < n)
        swap(xs, w++, j++);
}  

它采用数组xs,两个排序的子数组分别表示为范围[i, m)[j, n)。工作区域从w开始。与大多数教科书中给出的标准合并算法相比,这个算法在排序的子阵列和工作区域之间交换内容。结果,前一个工作区包含合并的排序元素,而存储在工作区中的前一个元素被移动到两个子数组。

但是,必须满足两个约束条件:

  1. 工作区应位于数组的范围内。换句话说,它应该足够大以容纳交换的元素而不会导致任何越界错误;
  2. 工作区可以与两个已排序的数组中的任何一个重叠,但是应该确保没有任何未合并的元素被覆盖;
  3. 通过定义这种合并算法,很容易想象一个解决方案,它可以对数组的一半进行排序;接下来的问题是,如何处理存储在工作区中的其余未分类部分,如下所示:

    ... unsorted 1/2 array ... | ... sorted 1/2 array ...
    

    一个直观的想法是递归分类工作区域的另一半,因此只有1/4元素尚未排序。

    ... unsorted 1/4 array ... | sorted 1/4 array B | sorted 1/2 array A ...
    

    此阶段的关键点是我们必须合并排序的1/4元素B. 排序的1/2元素A迟早。

    是否剩下工作区域,只有1/4个元素,大到可以合并 A和B?不幸的是,事实并非如此。

    然而,上面提到的第二个约束给了我们一个提示,如果我们可以确保合并序列不会覆盖未合并的元素,我们可以通过安排工作区与子数组重叠来利用它。

    实际上,我们不是对工作区域的后半部分进行排序,而是对前半部分进行排序,并将工作区域放在两个排序的数组之间,如下所示:

    ... sorted 1/4 array B | unsorted work area | ... sorted 1/2 array A ...
    

    此设置效果安排工作区与子阵列A重叠。这个想法 在[Jyrki Katajainen,Tomi Pasanen,Jukka Teuhola提出。 ``实用的就地合并''。北欧计算杂志,1996年。

    所以剩下的就是重复上面的步骤,将工作区域从1/2,1 / 4,1 / 8 ......减少,当工作区域变得足够小时,例如,只有两个剩下的元素,我们可以切换到一个简单的插入排序来结束这个算法。

    以下是基于本文的ANSI C中的实现。

    void imsort(Key* xs, int l, int u);
    
    void swap(Key* xs, int i, int j) {
        Key tmp = xs[i]; xs[i] = xs[j]; xs[j] = tmp;
    }
    
    /* 
     * sort xs[l, u), and put result to working area w. 
     * constraint, len(w) == u - l
     */
    void wsort(Key* xs, int l, int u, int w) {
        int m;
        if (u - l > 1) {
            m = l + (u - l) / 2;
            imsort(xs, l, m);
            imsort(xs, m, u);
            wmerge(xs, l, m, m, u, w);
        }
        else
            while (l < u)
                swap(xs, l++, w++);
    }
    
    void imsort(Key* xs, int l, int u) {
        int m, n, w;
        if (u - l > 1) {
            m = l + (u - l) / 2;
            w = l + u - m;
            wsort(xs, l, m, w); /* the last half contains sorted elements */
            while (w - l > 2) {
                n = w;
                w = l + (n - l + 1) / 2;
                wsort(xs, w, n, l);  /* the first half of the previous working area contains sorted elements */
                wmerge(xs, l, l + n - w, n, u, w);
            }
            for (n = w; n > l; --n) /*switch to insertion sort*/
                for (m = n; m < u && xs[m] < xs[m-1]; ++m)
                    swap(xs, m, m - 1);
        }
    }
    

    先前定义了wmerge。

    可以找到完整的源代码here,可以找到详细说明here

    顺便说一下,这个版本不是最快的合并排序,因为它需要更多的交换操作。根据我的测试,它比标准版本更快,标准版本在每次递归中分配额外的空格。但它比优化版本慢,后者提前将原始数组加倍,并将其用于进一步合并。

答案 1 :(得分:57)

包括其“大结果”,本文描述了几种就地合并排序的变体(PDF):

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf

使用较少的移动进行就地排序

Jyrki Katajainen,Tomi A. Pasanen

  

显示n的数组   可以使用O(1)对元素进行排序   额外空间,O(n log n / log log n)   元素移动,并且n log 2 n + O(n log   记录n)比较。这是第一次   就地排序算法要求   o(n log n)在最坏的情况下移动   同时保证O(n log n)   比较,但由于常数   算法涉及的因素是   主要是理论上的兴趣。

我认为这也是相关的。我有一张打印出来的照片,由同事传给我,但我还没读过。它似乎涵盖了基本理论,但我对该主题不够熟悉,无法判断如何全面:

http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681

最佳稳定合并

Antonios Symvonis

  

本文介绍了如何稳定合并   两个大小为m和的序列A和B.   n,m≤n,分别为O(m + n)   作业,O(mlog(n / m + 1))   比较和仅使用常数   额外空间的数量。这个   结果匹配所有已知的下限......

答案 2 :(得分:10)

关键步骤是让 merge 本身就位。它并不像那些消息来源那么困难,但是当你尝试时会丢失一些东西。

查看合并的一个步骤:

  

[...列表 - 排序 ... | X ...列表 - A ... | ÿ ...列表 - ...

我们知道排序序列比其他所有序列都少, x A 中的其他所有内容都少,而且 > y 小于 B 中的所有其他内容。在 x 小于或等于 y 的情况下,只需将指针移动到 A 的开头即可。在 y 小于 x 的情况下,您必须将 y 随机移动到整个 A 之后排序。最后一步是使这种情况变得昂贵(除了简并的情况)。

它通常更便宜(特别是当数组实际上每个元素实际上只包含单个单词时,例如,指向字符串或结构的指针)来换取一些空间的时间并且有一个单独的临时数组,您可以在它们之间来回切换。

答案 3 :(得分:9)

这真的不容易或有效,我建议你不要这样做,除非你真的需要(除非这是家庭作业,否则你可能不必这样做,因为合并的应用主要是理论上的)。你不能使用quicksort吗? Quicksort无论如何都会更快,只需更简单的优化,其额外内存为 O(log N)

无论如何,如果你必须这样做,那么你必须这样做。以下是我找到的内容:onetwo。我不熟悉inplace合并排序,但似乎基本的想法是使用旋转来促进合并两个数组而不使用额外的内存。

请注意,这比不在适当位置的经典合并排序要慢。

答案 4 :(得分:8)

仅供参考,这是一个不错的implementation of a stable in-place merge sort。复杂但不太糟糕。

我最终在Java中实现了stable in-place merge sortstable in-place quicksort。请注意,复杂度为O(n(log n)^ 2)

答案 5 :(得分:4)

C。

中无缓冲区合并的示例
#define SWAP(type, a, b) \
    do { type t=(a);(a)=(b);(b)=t; } while (0)

static void reverse_(int* a, int* b)
{
    for ( --b; a < b; a++, b-- )
       SWAP(int, *a, *b);
}
static int* rotate_(int* a, int* b, int* c)
/* swap the sequence [a,b) with [b,c). */
{
    if (a != b && b != c)
     {
       reverse_(a, b);
       reverse_(b, c);
       reverse_(a, c);
     }
    return a + (c - b);
}

static int* lower_bound_(int* a, int* b, const int key)
/* find first element not less than @p key in sorted sequence or end of
 * sequence (@p b) if not found. */
{
    int i;
    for ( i = b-a; i != 0; i /= 2 )
     {
       int* mid = a + i/2;
       if (*mid < key)
          a = mid + 1, i--;
     }
    return a;
}
static int* upper_bound_(int* a, int* b, const int key)
/* find first element greater than @p key in sorted sequence or end of
 * sequence (@p b) if not found. */
{
    int i;
    for ( i = b-a; i != 0; i /= 2 )
     {
       int* mid = a + i/2;
       if (*mid <= key)
          a = mid + 1, i--;
     }
    return a;
}

static void ip_merge_(int* a, int* b, int* c)
/* inplace merge. */
{
    int n1 = b - a;
    int n2 = c - b;

    if (n1 == 0 || n2 == 0)
       return;
    if (n1 == 1 && n2 == 1)
     {
       if (*b < *a)
          SWAP(int, *a, *b);
     }
    else
     {
       int* p, * q;

       if (n1 <= n2)
          p = upper_bound_(a, b, *(q = b+n2/2));
       else
          q = lower_bound_(b, c, *(p = a+n1/2));
       b = rotate_(p, b, q);

       ip_merge_(a, p, b);
       ip_merge_(b, q, c);
     }
}

void mergesort(int* v, int n)
{
    if (n > 1)
     {
       int h = n/2;
       mergesort(v, h); mergesort(v+h, n-h);
       ip_merge_(v, v+h, v+n);
     }
}

自适应合并(优化)的一个例子。

添加支持代码和修改以在任何大小的辅助缓冲区可用时加速合并(仍可在没有额外内存的情况下工作)。使用前向和后向合并,环形旋转,小序列合并和排序,以及迭代合并。

#include <stdlib.h>
#include <string.h>

static int* copy_(const int* a, const int* b, int* out)
{
    int count = b - a;
    if (a != out)
       memcpy(out, a, count*sizeof(int));
    return out + count;
}
static int* copy_backward_(const int* a, const int* b, int* out)
{
    int count = b - a;
    if (b != out)
       memmove(out - count, a, count*sizeof(int));
    return out - count;
}

static int* merge_(const int* a1, const int* b1, const int* a2,
  const int* b2, int* out)
{
    while ( a1 != b1 && a2 != b2 )
       *out++ = (*a1 <= *a2) ? *a1++ : *a2++;
    return copy_(a2, b2, copy_(a1, b1, out));
}
static int* merge_backward_(const int* a1, const int* b1,
  const int* a2, const int* b2, int* out)
{
    while ( a1 != b1 && a2 != b2 )
       *--out = (*(b1-1) > *(b2-1)) ? *--b1 : *--b2;
    return copy_backward_(a1, b1, copy_backward_(a2, b2, out));
}

static unsigned int gcd_(unsigned int m, unsigned int n)
{
    while ( n != 0 )
     {
       unsigned int t = m % n;
       m = n;
       n = t;
     }
    return m;
}
static void rotate_inner_(const int length, const int stride,
  int* first, int* last)
{
    int* p, * next = first, x = *first;
    while ( 1 )
     {
       p = next;
       if ((next += stride) >= last)
          next -= length;
       if (next == first)
          break;
       *p = *next;
     }
    *p = x;
}
static int* rotate_(int* a, int* b, int* c)
/* swap the sequence [a,b) with [b,c). */
{
    if (a != b && b != c)
     {
       int n1 = c - a;
       int n2 = b - a;

       int* i = a;
       int* j = a + gcd_(n1, n2);

       for ( ; i != j; i++ )
          rotate_inner_(n1, n2, i, c);
     }
    return a + (c - b);
}

static void ip_merge_small_(int* a, int* b, int* c)
/* inplace merge.
 * @note faster for small sequences. */
{
    while ( a != b && b != c )
       if (*a <= *b)
          a++;
       else
        {
          int* p = b+1;
          while ( p != c && *p < *a )
             p++;
          rotate_(a, b, p);
          b = p;
        }
}
static void ip_merge_(int* a, int* b, int* c, int* t, const int ts)
/* inplace merge.
 * @note works with or without additional memory. */
{
    int n1 = b - a;
    int n2 = c - b;

    if (n1 <= n2 && n1 <= ts)
     {
       merge_(t, copy_(a, b, t), b, c, a);
     }
    else if (n2 <= ts)
     {
       merge_backward_(a, b, t, copy_(b, c, t), c);
     }
    /* merge without buffer. */
    else if (n1 + n2 < 48)
     {
       ip_merge_small_(a, b, c);
     }
    else
     {
       int* p, * q;

       if (n1 <= n2)
          p = upper_bound_(a, b, *(q = b+n2/2));
       else
          q = lower_bound_(b, c, *(p = a+n1/2));
       b = rotate_(p, b, q);

       ip_merge_(a, p, b, t, ts);
       ip_merge_(b, q, c, t, ts);
     }
}
static void ip_merge_chunk_(const int cs, int* a, int* b, int* t,
  const int ts)
{
    int* p = a + cs*2;
    for ( ; p <= b; a = p, p += cs*2 )
       ip_merge_(a, a+cs, p, t, ts);
    if (a+cs < b)
       ip_merge_(a, a+cs, b, t, ts);
}

static void smallsort_(int* a, int* b)
/* insertion sort.
 * @note any stable sort with low setup cost will do. */
{
    int* p, * q;
    for ( p = a+1; p < b; p++ )
     {
       int x = *p;
       for ( q = p; a < q && x < *(q-1); q-- )
          *q = *(q-1);
       *q = x;
     }
}
static void smallsort_chunk_(const int cs, int* a, int* b)
{
    int* p = a + cs;
    for ( ; p <= b; a = p, p += cs )
       smallsort_(a, p);
    smallsort_(a, b);
}

static void mergesort_lower_(int* v, int n, int* t, const int ts)
{
    int cs = 16;
    smallsort_chunk_(cs, v, v+n);
    for ( ; cs < n; cs *= 2 )
       ip_merge_chunk_(cs, v, v+n, t, ts);
}

static void* get_buffer_(int size, int* final)
{
    void* p = NULL;
    while ( size != 0 && (p = malloc(size)) == NULL )
       size /= 2;
    *final = size;
    return p;
}
void mergesort(int* v, int n)
{
    /* @note buffer size may be in the range [0,(n+1)/2]. */
    int request = (n+1)/2 * sizeof(int);
    int actual;
    int* t = (int*) get_buffer_(request, &actual);

    /* @note allocation failure okay. */
    int tsize = actual / sizeof(int);
    mergesort_lower_(v, n, t, tsize);
    free(t);
}

答案 6 :(得分:2)

这是我的C版:

void mergesort(int *a, int len) {
  int temp, listsize, xsize;

  for (listsize = 1; listsize <= len; listsize*=2) {
    for (int i = 0, j = listsize; (j+listsize) <= len; i += (listsize*2), j += (listsize*2)) {
      merge(& a[i], listsize, listsize);
    }
  }

  listsize /= 2;

  xsize = len % listsize;
  if (xsize > 1)
    mergesort(& a[len-xsize], xsize);

  merge(a, listsize, xsize);
}

void merge(int *a, int sizei, int sizej) {
  int temp;
  int ii = 0;
  int ji = sizei;
  int flength = sizei+sizej;

  for (int f = 0; f < (flength-1); f++) {
    if (sizei == 0 || sizej == 0)
      break;

    if (a[ii] < a[ji]) {
      ii++;
      sizei--;
    }
    else {
      temp = a[ji];

      for (int z = (ji-1); z >= ii; z--)
        a[z+1] = a[z];  
      ii++;

      a[f] = temp;

      ji++;
      sizej--;
    }
  }
}

答案 7 :(得分:1)

使用Kronrod的原始技术有一个相对简单的就地合并排序实现,但实现更简单。可以在此处找到说明此技术的图示示例:http://www.logiccoder.com/TheSortProblem/BestMergeInfo.htm

与此链接相关的同一作者也有更详细的理论分析链接。

答案 8 :(得分:1)

This answer有一个code example,它实现了Bing-Chao Huang和Michael A. Langston在论文Practical In-Place Merging中描述的算法。我不得不承认我不了解细节,但是合并步骤的给定复杂度为O(n)。

从实践的角度来看,有证据表明,在现场场景中,纯就地实现的效果不佳。例如,C ++标准定义了std::inplace_merge,顾名思义是就地合并操作。

假设C ++库通常进行了很好的优化,那么有趣的是如何实现它:

1)libstdc ++(GCC代码库的一部分):std::inplace_merge

该实现委托给__inplace_merge,它通过尝试分配一个临时缓冲区来避免该问题:

typedef _Temporary_buffer<_BidirectionalIterator, _ValueType> _TmpBuf;
_TmpBuf __buf(__first, __len1 + __len2);

if (__buf.begin() == 0)
  std::__merge_without_buffer
    (__first, __middle, __last, __len1, __len2, __comp);
else
  std::__merge_adaptive
   (__first, __middle, __last, __len1, __len2, __buf.begin(),
     _DistanceType(__buf.size()), __comp);

否则,它会退回到实现(__merge_without_buffer),该实现不需要额外的内存,但不再需要O(n)的时间运行。

2)libc ++(部分Clang代码库):std::inplace_merge

看起来相似。它委派给function,后者也尝试allocate a buffer。根据是否有足够的元素,它将选择实现。恒定内存后备函数称为__buffered_inplace_merge

甚至后备时间仍然是O(n)时间,但要点是,如果有临时内存可用,他们就不会使用实现。


请注意,C ++标准通过将所需的复杂度从O(n)降低到O(N log N),明确为实现提供了选择此方法的自由:

  

复杂度:   如果有足够的额外内存可用,则为N-1个比较。如果内存不足,则进行O(N log N)个比较。

当然,这不能作为不应该使用在O(n)时间内就地合并的恒定空间的证明。另一方面,如果速度更快,则优化的C ++库可能会切换到该类型的实现。

答案 9 :(得分:0)

我知道我迟到了,但这是我昨天写的一个解决方案。我也在别处发布了这个,但这似乎是 SO 上最受欢迎的就地合并线程。我也没有在其他任何地方看到过这个算法,所以希望这可以帮助一些人。

这个算法是最简单的形式,以便于理解。可以显着调整它以获得额外的速度。平均时间复杂度为:O(n.log₂n) 用于稳定的就地数组合并,O(n.(log₂n)²) 用于整体排序。

//                     Stable Merge In Place Sort
//
//
// The following code is written to illustrate the base algorithm. A good
// number of optimizations can be applied to boost its overall speed
// For all its simplicity, it does still perform somewhat decently.
// Average case time complexity appears to be: O(n.(log₂n)²)

#include <stddef.h>
#include <stdio.h>

#define swap(x, y)    (t=(x), (x)=(y), (y)=t)

// Both sorted sub-arrays must be adjacent in 'a'
// Assumes that both 'an' and 'bn' are always non-zero
// 'an' is the length of the first sorted section in 'a', referred to as A
// 'bn' is the length of the second sorted section in 'a', referred to as B
static void
merge_inplace(int A[], size_t an, size_t bn)
{
    int t, *B = &A[an];
    size_t  pa, pb;     // Swap partition pointers within A and B

    // Find the portion to swap.  We're looking for how much from the
    // start of B can swap with the end of A, such that every element
    // in A is less than or equal to any element in B.  This is quite
    // simple when both sub-arrays come at us pre-sorted
    for(pa = an, pb = 0; pa>0 && pb<bn && B[pb] < A[pa-1]; pa--, pb++);

    // Now swap last part of A with first part of B according to the
    // indicies we found
    for (size_t index=pa; index < an; index++)
        swap(A[index], B[index-pa]);

    // Now merge the two sub-array pairings.  We need to check that either array
    // didn't wholly swap out the other and cause the remaining portion to be zero
    if (pa>0 && (an-pa)>0)
        merge_inplace(A, pa, an-pa);

    if (pb>0 && (bn-pb)>0)
        merge_inplace(B, pb, bn-pb);
} // merge_inplace

// Implements a recursive merge-sort algorithm with an optional
// insertion sort for when the splits get too small.  'n' must
// ALWAYS be 2 or more.  It enforces this when calling itself
static void
merge_sort(int a[], size_t n)
{
    size_t  m = n/2;

    // Sort first and second halves only if the target 'n' will be > 1
    if (m > 1)
        merge_sort(a, m);

    if ((n-m)>1)
        merge_sort(a+m, n-m);

    // Now merge the two sorted sub-arrays together. We know that since
    // n > 1, then both m and n-m MUST be non-zero, and so we will never
    // violate the condition of not passing in zero length sub-arrays
    merge_inplace(a, m, n-m);
} // merge_sort

// Print an array */
static void
print_array(int a[], size_t size)
{
    if (size > 0) {
        printf("%d", a[0]);
        for (size_t i = 1; i < size; i++)
            printf(" %d", a[i]);
    }
    printf("\n");
} // print_array
 
// Test driver
int
main()
{
    int a[] = { 17, 3, 16, 5, 14, 8, 10, 7, 15, 1, 13, 4, 9, 12, 11, 6, 2 };
    size_t  n = sizeof(a) / sizeof(a[0]);
 
    merge_sort(a, n);
 
    print_array(a, n);

    return 0;
} // main

答案 10 :(得分:-5)

我刚刚尝试使用插入排序算法在 JAVA 中进行合并排序的合并算法,使用以下步骤。
1)有两个排序的阵列 2)比较每个数组的第一个值;并将最小值放入第一个数组中 3)使用插入排序(从左到右遍历)将较大的值放入第二个数组中 4)然后再次比较第一个数组的第二个值和第二个数组的第一个值,并做同样的事情。但是当交换发生时,跳过比较其他项目有一些线索,但只需要交换。

我在这里做了一些优化;在插入排序中保持较小的比较。我在这个解决方案中找到的唯一缺点就是它需要在第二个阵列中更大规模地交换数组元素。

e.g)

首先___数组:3,7,8,9

第二阵列:1,2,4,5

然后7,8,9使第二个数组每次都将其所有元素交换(左移一个),将自己置于最后一个。

因此,与两个项目的比较相比,这里的假设是交换项目可以忽略不计。

https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java

package sorting;

import java.util.Arrays;

public class MergeSort {
    public static void main(String[] args) {
    int[] array = { 5, 6, 10, 3, 9, 2, 12, 1, 8, 7 };
    mergeSort(array, 0, array.length -1);
    System.out.println(Arrays.toString(array));

    int[] array1 = {4, 7, 2};
    System.out.println(Arrays.toString(array1));
    mergeSort(array1, 0, array1.length -1);
    System.out.println(Arrays.toString(array1));
    System.out.println("\n\n");

    int[] array2 = {4, 7, 9};
    System.out.println(Arrays.toString(array2));
    mergeSort(array2, 0, array2.length -1);
    System.out.println(Arrays.toString(array2));
    System.out.println("\n\n");

    int[] array3 = {4, 7, 5};
    System.out.println(Arrays.toString(array3));
    mergeSort(array3, 0, array3.length -1);
    System.out.println(Arrays.toString(array3));
    System.out.println("\n\n");

    int[] array4 = {7, 4, 2};
    System.out.println(Arrays.toString(array4));
    mergeSort(array4, 0, array4.length -1);
    System.out.println(Arrays.toString(array4));
    System.out.println("\n\n");

    int[] array5 = {7, 4, 9};
    System.out.println(Arrays.toString(array5));
    mergeSort(array5, 0, array5.length -1);
    System.out.println(Arrays.toString(array5));
    System.out.println("\n\n");

    int[] array6 = {7, 4, 5};
    System.out.println(Arrays.toString(array6));
    mergeSort(array6, 0, array6.length -1);
    System.out.println(Arrays.toString(array6));
    System.out.println("\n\n");

    //Handling array of size two
    int[] array7 = {7, 4};
    System.out.println(Arrays.toString(array7));
    mergeSort(array7, 0, array7.length -1);
    System.out.println(Arrays.toString(array7));
    System.out.println("\n\n");

    int input1[] = {1};
    int input2[] = {4,2};
    int input3[] = {6,2,9};
    int input4[] = {6,-1,10,4,11,14,19,12,18};
    System.out.println(Arrays.toString(input1));
    mergeSort(input1, 0, input1.length-1);
    System.out.println(Arrays.toString(input1));
    System.out.println("\n\n");

    System.out.println(Arrays.toString(input2));
    mergeSort(input2, 0, input2.length-1);
    System.out.println(Arrays.toString(input2));
    System.out.println("\n\n");

    System.out.println(Arrays.toString(input3));
    mergeSort(input3, 0, input3.length-1);
    System.out.println(Arrays.toString(input3));
    System.out.println("\n\n");

    System.out.println(Arrays.toString(input4));
    mergeSort(input4, 0, input4.length-1);
    System.out.println(Arrays.toString(input4));
    System.out.println("\n\n");
}

private static void mergeSort(int[] array, int p, int r) {
    //Both below mid finding is fine.
    int mid = (r - p)/2 + p;
    int mid1 = (r + p)/2;
    if(mid != mid1) {
        System.out.println(" Mid is mismatching:" + mid + "/" + mid1+ "  for p:"+p+"  r:"+r);
    }

    if(p < r) {
        mergeSort(array, p, mid);
        mergeSort(array, mid+1, r);
//      merge(array, p, mid, r);
        inPlaceMerge(array, p, mid, r);
        }
    }

//Regular merge
private static void merge(int[] array, int p, int mid, int r) {
    int lengthOfLeftArray = mid - p + 1; // This is important to add +1.
    int lengthOfRightArray = r - mid;

    int[] left = new int[lengthOfLeftArray];
    int[] right = new int[lengthOfRightArray];

    for(int i = p, j = 0; i <= mid; ){
        left[j++] = array[i++];
    }

    for(int i = mid + 1, j = 0; i <= r; ){
        right[j++] = array[i++];
    }

    int i = 0, j = 0;
    for(; i < left.length && j < right.length; ) {
        if(left[i] < right[j]){
            array[p++] = left[i++];
        } else {
            array[p++] = right[j++];
        }
    }
    while(j < right.length){
        array[p++] = right[j++];
    } 
    while(i < left.length){
        array[p++] = left[i++];
    }
}

//InPlaceMerge no extra array
private static void inPlaceMerge(int[] array, int p, int mid, int r) {
    int secondArrayStart = mid+1;
    int prevPlaced = mid+1;
    int q = mid+1;
    while(p < mid+1 && q <= r){
        boolean swapped = false;
        if(array[p] > array[q]) {
            swap(array, p, q);
            swapped = true;
        }   
        if(q != secondArrayStart && array[p] > array[secondArrayStart]) {
            swap(array, p, secondArrayStart);
            swapped = true;
        }
        //Check swapped value is in right place of second sorted array
        if(swapped && secondArrayStart+1 <= r && array[secondArrayStart+1] < array[secondArrayStart]) {
            prevPlaced = placeInOrder(array, secondArrayStart, prevPlaced);
        }
        p++;
        if(q < r) {     //q+1 <= r) {
            q++;
        }
    }
}

private static int placeInOrder(int[] array, int secondArrayStart, int prevPlaced) {
    int i = secondArrayStart;
    for(; i < array.length; i++) {
        //Simply swap till the prevPlaced position
        if(secondArrayStart < prevPlaced) {
            swap(array, secondArrayStart, secondArrayStart+1);
            secondArrayStart++;
            continue;
        }
        if(array[i] < array[secondArrayStart]) {
            swap(array, i, secondArrayStart);
            secondArrayStart++;
        } else if(i != secondArrayStart && array[i] > array[secondArrayStart]){
            break;
        }
    }
    return secondArrayStart;
}

private static void swap(int[] array, int m, int n){
    int temp = array[m];
    array[m] = array[n];
    array[n] = temp;
}
}