7

I found similar question about interleaving two arraylists into one, but its in PHP. I was asked this question in interview as well but could'nt solve it, came back to SO to look if it was addressed already, but i could only find this paper

So any pointers to pseudo code or method definition ?

Big(O) restrictions : O(n) - time cost and O(1) - space cost

Example:
a[]= a1, a2, ..., an
b[]= b1, b2, ..., bn
Rearrange the arraylist to a1, b1, a2, b2, ..., an, bn

Editv1.0 : Arraylists a[] and b[] are of same size

Editv2.0 : What if the question is extended to rearrange in one of given two arrays, but not create a new array ?

Community
  • 1
  • 1
SuperMan
  • 3,532
  • 12
  • 45
  • 49
  • Does "`O(1)` space cost" mean "`O(1)` _in addition_ to the space needed to store the two arrays?" What is the expected behavior when the two arrays are not the same size? – Matt Ball Apr 13 '11 at 18:58
  • @Matt Yes ur right, Edited my question about arrays size. – SuperMan Apr 13 '11 at 18:59
  • Oh, btw, that paper has basically nothing to do with your question. – Matt Ball Apr 13 '11 at 19:06
  • 1
    Well it was attached in one of the similar questions, but the issue was to reaarange within the given 2 arrays but not create a new array. – SuperMan Apr 13 '11 at 19:09
  • 1
    You are using the terms array and arraylist interchangeably, and both answers so far deal with arrays. Which structure did the question ask about? – jmccarthy Apr 13 '11 at 19:32
  • If we have one array [a1 a2 ... an b1 b2 .. bn] instead of two, I believe the paper is relevant. OR even if the resulting interleaving is supposed to look like a = [a1 b1 a2 b2 ...a(n/2) b(n/2)], b = [ a(n/2+1) b(n/2+1) ... an bn]. – user127.0.0.1 Apr 13 '11 at 21:35
  • Found an old stackoverflow question: http://stackoverflow.com/questions/1777901/array-interleaving-problem – user127.0.0.1 Apr 15 '11 at 04:39
  • @user127.0.0.1 there is no proper implementation in that question – SuperMan Apr 15 '11 at 05:03
  • @mccarthy its arraylist – SuperMan Apr 15 '11 at 05:04
  • @SuperMan: Your question is contradictory. You want to rearrange in one array but only use O(1) space? Are you sure about your question? – user127.0.0.1 Apr 15 '11 at 17:36
  • Yes I'm sure about O(1) space – SuperMan Apr 15 '11 at 18:23

6 Answers6

7

For simplicity, assume that the arrays are the same length, and are int arrays.

int[] merge(int[] a, int[] b)
{
    assert (a.length == b.length);

    int[] result = new int[a.length + b.length];

    for (int i=0; i<a.length; i++)
    {
        result[i*2] = a[i];
        result[i*2+1] = b[i];
    }

    return result;
}
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • @Matt That is true, But what if i had to merge into one of the two arrays, but not create a new array ? – SuperMan Apr 13 '11 at 19:07
  • You can't do that unless you're willing to exclude half of the elements in each input array, because arrays cannot be resized. – Matt Ball Apr 13 '11 at 19:10
  • @Matt You have arraylists in Java , which can help us to increase the size? – SuperMan Apr 13 '11 at 19:13
  • Yes, but that's a list, not an array, and I'd have used completely different code in that case. – Matt Ball Apr 13 '11 at 19:15
  • Aahh, I have tagged the question under Java thought u would consider lists buy default.. – SuperMan Apr 13 '11 at 19:17
  • @SuperMan maybe you could stick half the interleaved elements in one of the original arrays and half the interleaved elements in the other original array, but I don't see how that could be done in O(n). – Matt Wonlaw Apr 13 '11 at 19:41
  • @mlaw: with arrays that's actually pretty easy, it's just a matter of swapping elements between the two arrays. – Matt Ball Apr 13 '11 at 19:48
  • 1
    @Matt That is what I thought, at first. But it doesn't seem to be the case. interleaving [1 2 3 4 5] and [6 7 8 9 0]: you would first start with replacing 2 with 6 leads to replacing 3 with 2 and 5 with 3 and 9 with 5 and so on. Then you need to replace 4 with 7 and propagate those replaces. – Matt Wonlaw Apr 13 '11 at 19:56
  • @mlaw care to post your ideas as answer ? – SuperMan Apr 13 '11 at 20:11
  • @mlaw it depends on the exact interleaving you're aiming for in the second array. To interleave the two arrays `x = [1, 2, 3, 4, 5]` and `y = [a, b, c, d, e]` you could create `x' = [1, b, 3, d, 5]` and `y' = [a, 2, c, 4, d]`. See what I mean? – Matt Ball Apr 13 '11 at 20:13
  • @Matt Yeah, in that case it would be straightforward. – Matt Wonlaw Apr 13 '11 at 20:28
  • @Matt: Creating x' and y' is not O(1) space, is it? I agree with mlaw. Without creating new arrays/extending the original ones, it seems hard. The paper linked in the question might be useful there. – user127.0.0.1 Apr 13 '11 at 21:26
  • @user: `x'` and `y'` wouldn't actually be new arrays, I used the "prime" to indicate that they were modified versions of the original. – Matt Ball Apr 13 '11 at 21:28
  • @Matt: Then how would you do the swapping? Care to elaborate? Pick arrays of 12 elements each for example. Remember the O(1) extra memory constraint. Note: I am talking about x' = [1 a 2 b 3], y' = [c 4 d 5 e]. (I just realized you are talking about a different interleaving, sorry about that). – user127.0.0.1 Apr 13 '11 at 21:29
  • btw, I see no point in interleaving as you did :-) Why not just leave the arrays as they are? – user127.0.0.1 Apr 13 '11 at 21:41
3

I think this is not doable with your given constraints (O(n) time and O(1) space, i.e. no additional space) for an array or array-based list. (Assuming of course, that we can't simply create a new List object delegating to the original ones.)

If you have two linked lists, this is doable - if we assume the garbage collector is fast enough, i.e. deleting an element from one list and adding it to another list does not violate the space limitation.

public <X> void interleaveLists(List<X> first, List<X> second)
{
    ListIterator<X> firstIt = first.listIterator();
    ListIterator<X> secondIt = second.listIterator();
    while(secondIt.hasNext()) {
        fistIt.next();
        firstIt.add(secondIt.next());
        secondIt.remove();
    }
}

This method works for any pair of lists, but is only O(n) for linked lists.

For a custom linked list where we can modify the pointers, we don't have to rely on the garbage collector, we would simply change the nodes. Here for a singly-linked list:

public void interleaveLinkedLists(Node<X> firstList, Node<X> secondList) {
    while(secondList != null) {
        Node<X> nextFirst = firstList.next;
        Node<X> nextSecond = secondList.next;
        firstList.next = secondList;
        secondList.next = nextFirst;
        firstList = nextFirst;
        secondList = nextSecond;
    }
}

For a doubly-linked list, we would also have to adapt the prev-pointers.

Here the wrapping variant mentioned in the first paragraph:

public List<X> interleaveLists(final List<X> first, final List<X> second)
{
   if (first.size() != second.size())
      throw new IllegalArgumentException();
   return new AbstractList<X>() {
      public int size() {
         return 2 * first.size();
      }
      public X get(int index) {
         return index % 2 == 0 ? first.get(index / 2) : second.get(index / 2);
      }
      // if necessary, add a similar set() method.  add/remove are not sensible here.
   };
}

This is actually O(1) in time, too.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
2

I've done up a small solution going on the assumption that you are talking about using the ArrayList (see my comment on the question). I may be oversimplifying the problem based on some of the responses here, but here goes anyway.

The below example takes a and b both of type ArrayList<Integer> and interleaves them by inserting b[0] after a[0], b[1] after a[1] etc. This snippet of course naively assumes that a and b are of the same size as per your Edit v1.0. It also does not create a new ArrayList as per your Edit v2.0.

//a and b are of type ArrayList<Integer>
for (int i = a.size(); i > 0; i--)
{
    a.add(i, b.get(i - 1));
}

No matter what happens if you are combining the ArrayLists you're going to have twice the size.

jmccarthy
  • 470
  • 4
  • 16
0

I believe the mod (%) operations in Matt's answer are incorrect. Under the same assumption (that the arrays are the same length), I'd propose the following solution instead:

static int[] merge(final int[] a, final int[] b)
{
    final int[] result = new int[a.length * 2];

    for (int i=0; i < a.length; i++)
    {
        result[i << 1] = a[i];
        result[(i << 1) + 1] = b[i];
    }

    return result;
}

I tested (very briefly), and it appears to work, but of course makes no attempt to handle error conditions such as null arguments or input arrays mismatched in size.

AaronD
  • 1,701
  • 13
  • 12
0

in the meantime lambda was introduced
O(n) time complexity
O(1) space complexity

int[] merge(int[] a, int[] b) {
    return( IntStream.range( 0, a.length ).flatMap(
        n -> IntStream.of( a[n], b[n] ) ).toArray() );
}
Kaplan
  • 2,572
  • 13
  • 14
-1

The lists don't have to be the same size:

public class InterleaveTwoLists<X> {

    public List<X> interleaveLists(final List<X> first, final List<X> second) {

        return new AbstractList<X>() {
            private int minSize;
            private int combinedMinSize;
            private int size;
            private List<X>largerList;
            {{
                minSize = Math.min(first.size(), second.size());
                combinedMinSize = minSize*2;
                size = first.size() + second.size();
                largerList = first.size() > minSize ? first : second;
            }}

            public int size() {
                return size;
            }

            public X get(int index) {
                if (index < combinedMinSize) {
                    return index % 2 == 0 
                        ? first.get(index / 2) 
                        : second.get(index / 2);
                }
                else { 
                    return largerList.get(index-minSize);
                }
            }
        };
    }
}

To test this:

public class InterleaveTwoListsTest {

    private static final Logger log = 
        LoggerFactory.getLogger(InterleaveTwoListsTest.class);

    List<String> first = new ArrayList<String>() {
    { 
        add("one"); add("three"); add("five"); 
        add("seven"); add("eight"); add("nine");
    }};

    List<String> second = new ArrayList<String>() {
    { 
        add("two"); add("four"); add("six"); 
    }};


    private InterleaveTwoLists<String> interleaveTwoLists;

    @Before
    public void setUp() throws Exception {
        interleaveTwoLists = new InterleaveTwoLists<>();
    }

    @Test
    public void test() {
        List<String> combinedList = interleaveTwoLists.interleaveLists(first, second);
        for( int i = 0; i < first.size() + second.size(); i++) { 
            log.debug("{}: {}", i, combinedList.get(i));
        }
    }
}
pfurbacher
  • 1,789
  • 3
  • 15
  • 23
  • I find your lack of assertion statements disturbing. – Makoto Jul 23 '13 at 04:59
  • Huh? Are you referring to JUnit assert statements? These are "tests" meant to demonstrate. Do you really use "main()" methods when you want to demonstrate? -1 on that. – pfurbacher Oct 09 '13 at 12:22
  • I don't use tests to demonstrate; I assert behavior about them instead. In a sense, the tests do demonstrate whether or not my assertions are valid. But a JUnit test without any assertions is...disturbing. – Makoto Oct 10 '13 at 02:52