Подтвердить что ты не робот

Как объединить два отсортированных целочисленных массива на месте с использованием O (n) времени и O (1) пространственной стоимости

Например, для заданного целочисленного массива и его двух последовательных начальных позиций последовательности, которые являются "b1" и "b2", кроме того, предоставляется позиция "последняя", которая указывает вторую конечную позицию последовательности. От массива [b1] до массива [b2-1] и от массива [b2] до массива [последний] оба выполняются отдельно, как объединить их на месте с использованием O (n) времени и O (1) пространства стоимость?

4b9b3361

Ответ 1

Это отнюдь не простая проблема. Это возможно, но редко делается на практике, потому что она намного сложнее стандартного слияния с использованием пространства N-царапин. Документ Хуанга и Лангстона существует с конца 80-х годов, хотя практические реализации на самом деле не появились до конца. Раньше статья Л. Трабба-Прадо в 1977 году значительно превосходила Хуанга и Лэнгстона, но мне мешает найти точный текст этой статьи; только ссылки изобилуют.

Отличная последующая публикация Асимптотически эффективное слияние на месте (1995) Geert, Katajainenb и Pasanen - это хороший охват нескольких алгоритмы и ссылки. Вклад Trabb-Prado в тему.

Ответ 2

Слияние Kronrod было первым опубликованным алгоритмом для этого. Это примерно так:

Разделите обе части массива на блоки размером k = sqrt (n). Сортируйте блоки, используя их первые элементы, в качестве основы для сравнения. Это можно сделать в sqrt (n) ^ 2 = O (n) путем сортировки сортировки. Ключевым свойством сортировки здесь является то, что он имеет постоянные перемещения на блок, поэтому только # сравнения являются квадратными.

После этой фазы для каждого элемента A[i] в массиве не более k-1 элементов "неправильно отсортированы" под ним, то есть элементы в позициях j < i, такие что A[j]>A[i]. Это (возможно) в ближайшем блоке ниже, который исходит от другой объединенной части. Обратите внимание, что первый элемент блока (и все остальные блоки ниже) уже правильно отсортированы относительно A[i] из-за того, что блоки сортируются по их первым элементам. Вот почему работает вторая фаза, т.е. Достигает полностью отсортированного массива:

Теперь объедините первый блок со вторым, затем вторым с третьим и т.д., используя последние 2 блока как временное пространство для вывода слияния. Это будет скремблировать содержимое последних двух блоков, но на последней фазе они (вместе с предыдущим блоком) могут быть отсортированы по типу выбора в sqrt (n) ^ 2 = O (n) времени.

Ответ 3

Есть такие вещи, как истинные слияния на месте, но они недостаточно прямолинейны, чтобы кто-то собирался самостоятельно изобретать их в середине интервью - на протяжении многих лет на нем были описаны описания сложных алгоритмов для этого, Одно из них - практическое объединение на месте, Хуан и Лангстон, CACM, март 1988 года. Отправной идеей для этого является разделение данных длины n на блоки размера sqrt (n) и использование одного блока, заполненного самыми большими элементами данных, чтобы обеспечить буферное пространство, используемое при объединении других. Введение в эту статью гласит:

"Для двух отсортированных списков, длина которых равна n, очевидным методам слияния в шагах O (n) также требуется линейное количество дополнительной памяти. С другой стороны, легко слить, используя только постоянное количество дополнительного пространства путем сортировки кучи, но при стоимости O (n log n) времени"

Следовательно, я утверждаю, что истинное слияние на месте может быть сделано, но неочевидно.

Ответ 4

Хотя это невозможно во время O(n), у меня есть предложение сделать это быстрее, чем O(n^2). Я использую только O(1) пробел, который является temp в моем коде. Я уверен, что он должен работать лучше, чем 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;
    }

Ответ 5

Здесь O (n-1) Память (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);
    }
}

Ответ 6

Несколько часов назад у меня было интервью (с очень важной компанией), и меня спросили об этом. В 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--;
     }
}