Функция принимает два отсортированных от меньшего к большему

Вам нужно написать реализацию функции

 void merge(ArrayList a, ArrayList b) { // тело функции }
 

Функция принимает два отсортированных от меньшего к большему ArrayList одинакового размера

[a1, a2, ..., an], 
[b1, b2, ..., bn]. 

В результате выполнения функции в первом(!) ArrayList (в данном случае это А) должны содержаться элементы обоих ArrayList, также отсортированные от меньшего к большему. Второй ArrayList должен остаться не изменённым.

Пример:

Входные данные

A [1,3,5]  
B [2,6,8]

Результат

A [1,2,3,5,6,8]  
B [2,6,8]

Оцениваться будут следующие параметры:

  • код должен работать корректно

  • выполнять полезную функцию

  • иметь максимально возможную эффективность (задумайтесь, пожалуйста, над тем насколько эффективно написанная вами функция будет использовать память и процессорное время)


Ответы (3 шт):

Автор решения: Leviathan_ZC
private static void merge(ArrayList<Integer> a, ArrayList<Integer> b) {
        int size = a.size();
        ArrayList<Integer> temp = new ArrayList<>(size * 2);
        int firstIdx = 0;
        int secondIdx = 0;
        for (; firstIdx < size && secondIdx < size; ) {
            temp.add(
                    a.get(firstIdx) < b.get(secondIdx) ? a.get(firstIdx++) : b.get(secondIdx++)
            );
        }

        ArrayList<Integer> last;
        int lastIdx;

        if (firstIdx < size) {
            last = a;
            lastIdx = firstIdx;
        } else {
            last = b;
            lastIdx = secondIdx;
        }

        for (int i = lastIdx; i < last.size(); i++) {
            temp.add(last.get(i));
        }
        a.clear();
        a.addAll(temp);
    }
→ Ссылка
Автор решения: Stanislav Volodarskiy

В условии специально указано что эффективность важна. Как слить массивы максимально быстро и в минимальной памяти?

Минимальность по памяти достигается следующей конструкцией:

a.ensureCapacity(a.size() + b.size());
// a.addAll(b);
for (int l = 0; l < b.size(); ++l) {
    a.add(null);
}

ensureCapacity убеждается что в списке a достаточно места чтобы он мог быть увеличен до финального размера без выделения новой памяти. Без этого вызова массив a может быть перенесён в памяти несколько раз.

Цикл ниже растягивает список до окончательного размера. Вместо цикла можно вызвать a.addAll(b). Я этого не делаю чтобы было ясно что добавленные значения не важны.

Дальше сливаем списки с конца к началу. Из индексов i и j читаем, в k пишем:

           i        k                     j
           |        |                     |
a = [1, 3, 5, ?, ?, ?]         b = [2, 6, 8]

Сравниваем a[i] и b[j]. Больший присваиваем a[k]. Передвигаем индексы j и k:

           i     k                     j
           |     |                     |
a = [1, 3, 5, ?, ?, 8]         b = [2, 6, 8]

Ещё раз:

           i  k                     j
           |  |                     |
a = [1, 3, 5, ?, 6, 8]         b = [2, 6, 8]

Теперь a[i] > b[j]. Копируем из a[i] в a[k] и двигаем индексы i и k:

        i  k                        j
        |  |                        |
a = [1, 3, 5, 5, 6, 8]         b = [2, 6, 8]

Ещё раз:

     i  k                           j
     |  |                           |
a = [1, 3, 3, 5, 6, 8]         b = [2, 6, 8]

И ещё раз. Список b исчерпан. Можно прекращать работу:

    i,k
     |
a = [1, 2, 5, 5, 6, 8]         b = [2, 6, 8]

Возможна ситуация когда первым будет исчерпан список a, тогда надо докопировать значения, которые остались в b.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MergeSortedArrayListsInPlace {
    public static void main(String... args) {
        ArrayList<Integer> a = new ArrayList<Integer>(Arrays.asList(1, 3, 5));
        List<Integer> b = Arrays.asList(2, 6, 8);
        System.out.println(a);
        System.out.println(b);
        merge(a, b);
        System.out.println(a);
    }

    public static void merge(ArrayList<Integer> a, List<Integer> b) {
        int i = a.size() - 1; // читает из a, движется к нулю
        a.ensureCapacity(a.size() + b.size());
        // a.addAll(b);
        for (int l = 0; l < b.size(); ++l) {
            a.add(null);
        }
        int j = b.size() - 1; // читает из b, движется к нулю
        int k = a.size() - 1; // пишет в a, движется к нулю

        // слияние от больших к меньшим
        while (i >= 0 && j >= 0) {
            Integer av = a.get(i);
            Integer bv = b.get(j);
            if (av > bv) {
                a.set(k, av);
                --i;
            } else {
                a.set(k, bv);
                --j;
            }
            --k;
        }

        // копирование хвоста из b в a
        while (j >= 0) {
            a.set(k, b.get(j));
            --j;
            --k;
        }
    }
}
$ javac MergeSortedArrayListsInPlace.java && java MergeSortedArrayListsInPlace 
[1, 3, 5]
[2, 6, 8]
[1, 2, 3, 5, 6, 8]
→ Ссылка
Автор решения: Pupa

Однако данный вариант кода не обрабатывает числа отрицательные. Сделал обработку и отрицательных.

public static void main(String[] args) {
        ArrayList<Integer> firstArray = new ArrayList<Integer>(Arrays.asList(-8, 22));
        List<Integer> secondArray = Arrays.asList(-5, 15);
        merge(firstArray, secondArray);
        System.out.println(firstArray);
        System.out.println(secondArray);
    }

    public static void merge(ArrayList<Integer> firstArray, List<Integer> secondArray) {
        int i = firstArray.size() - 1;
        int j = secondArray.size() - 1;
        firstArray.ensureCapacity(firstArray.size() + secondArray.size());
        for (int l = 0; l < secondArray.size(); ++l) {
            firstArray.add(null);
        }
        int k = firstArray.size() - 1;

        while (i >= 0 && j >= 0) {
            Integer firstArrayElement = firstArray.get(i);
            Integer secondArrayElement = secondArray.get(j);
            if (firstArrayElement > secondArrayElement) {
                firstArray.set(k, firstArrayElement);
                --i;
            } else {
                firstArray.set(k, secondArrayElement);
                --j;
            }
            --k;
            if (i < 0){
                firstArray.set(k, secondArrayElement);
            }  if (j == 1 && i == -1){
                firstArray.set(k-1, secondArray.get(k-1));
            }
        }
    }
→ Ссылка