Як об'єднати два відсортовані масиви в відсортований масив? [зачинено]


160

Про це мене запитали в інтерв'ю, і це рішення, яке я запропонував:

public static int[] merge(int[] a, int[] b) {

    int[] answer = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;
    while (i < a.length && j < b.length)
    {
        if (a[i] < b[j])
        {
            answer[k] = a[i];
            i++;
        }
        else
        {
            answer[k] = b[j];
            j++;
        }
        k++;
    }

    while (i < a.length)
    {
        answer[k] = a[i];
        i++;
        k++;
    }

    while (j < b.length)
    {
        answer[k] = b[j];
        j++;
        k++;
    }

    return answer;
}

Чи є більш ефективний спосіб зробити це?

Правка: Способи виправленої довжини.


30
Схоже, досить гарна відповідь на мене. Ця проблема матиме O (n) складність у кращому випадку, і ваша відповідь цього досягає. Все інше буде мікрооптимізацією.
Дрю Холл

3
Ти добре зробив! Це по суті частина сортування об'єднання: об'єднання двох відсортованих потоків (зі стрічки чи диска) в інший відсортований потік.
Володимир Дюжев

9
Ви отримали роботу?
Шай

5
Також ви можете використовувати потрійний оператор: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Специфікація мови Java: Умовний оператор? : .
Антон Дозорцев,

1
Ви забули коментувати !!!
LiziPizi

Відповіді:


33

Невелике поліпшення, але після основного циклу ви можете System.arraycopyскопіювати хвіст будь-якого вхідного масиву, коли дістанетеся до кінця іншого. Однак це не змінить O(n)експлуатаційні характеристики вашого рішення.


109
public static int[] merge(int[] a, int[] b) {

    int[] answer = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;

    while (i < a.length && j < b.length)  
       answer[k++] = a[i] < b[j] ? a[i++] :  b[j++];

    while (i < a.length)  
        answer[k++] = a[i++];

    while (j < b.length)    
        answer[k++] = b[j++];

    return answer;
}

Трохи більш компактний, але точно такий же!


Людині, яка сказала, що це спричинило показник поза межами меж виключенням, які матеріали ви використовуєте? Це працює у всіх випадках для мене.
Майк Солл

1
Використовуйте для циклу об'єднання ліній, декларуючи змінні циклу та керування циклом. Економно використовуйте подвійні порожні рядки - виглядає невимогливим між симетричними "хвостовими копіями".
сіра борода

58

Я здивований, що ніхто не згадав про таку більш круту, ефективну та компактну реалізацію:

public static int[] merge(int[] a, int[] b) {
    int[] answer = new int[a.length + b.length];
    int i = a.length - 1, j = b.length - 1, k = answer.length;

    while (k > 0)
        answer[--k] =
                (j < 0 || (i >= 0 && a[i] >= b[j])) ? a[i--] : b[j--];
    return answer;
}

Цікаві інтереси

  1. Зауважте, що він робить таку ж чи меншу кількість операцій, як і будь-який інший алгоритм O (n) , але буквально в одному операторі в одному циклі "while"!
  2. Якщо два масиви мають приблизно однаковий розмір, то константа для O (n) однакова. Однак якщо масиви дійсно незбалансовані, то версії з System.arraycopyби виграли, оскільки внутрішньо це можна зробити за допомогою однієї інструкції по збірці x86.
  3. Зауважте a[i] >= b[j]замість a[i] > b[j]. Це гарантує "стабільність", яка визначається як коли елементи a і b рівні, ми хочемо елементи з a до b.

Це дійсно дуже приємний підхід. У мене виникли проблеми з отриманням хороших орієнтирів на моїх алгоритмах сортування об'єднань у Swift lang. Перетворення це дало мені те, що мені було потрібно, дуже дякую
Chackle

Яка точка (j <0) у циклі while? Btw, +1, це дійсно круто! Дякую, що поділились.
Хенгамех

2
@Hengameh в разі j < 0, bвже вичерпаний, тому ми продовжуємо додавати залишилися aелементи в answer масив
Натан Streppel

6
Занадто "розумний" і важко читати у своєму розумі. Я вважаю за краще простіше читати код, тим більше, що ви дійсно не досягаєте значного покращення продуктивності за допомогою цього коду.
Кевін М

1
плюс точка для Notice, а a [i]> = b [j] замість [i]> b [j]. Це гарантує «стабільність»
Ян Хонський

16

Будь-які вдосконалення, які можна було б зробити, були б мікрооптимізаціями, загальний алгоритм правильний.


Якщо a великий, а b малий, то цей алгоритм помилковий.
джек

7
Це не неправильно, але не ефективно.
джек

@jack, як це можна зробити швидше, ніж O (n), коли ви створюєте масив з n елементів?
Буде

@will System.arrayCopy()є нерозумно швидким, оскільки він використовує оптимізовані CPU memcpyдзвінки. Тож є можливість покращити продуктивність, скопіювавши шматки. Існує також сфера двійкового пошуку меж.
струнка

Особливо, якщо ви можете використовувати відсортовану природу, щоб пропустити більшість записів і ніколи не порівнювати їх. Ви можете насправді перемогти O (n).
Tatarize

10

Це рішення також дуже схоже на інші пости, за винятком того, що воно використовує System.arrayCopy для копіювання решти елементів масиву.

private static int[] sortedArrayMerge(int a[], int b[]) {
    int result[] = new int[a.length +b.length];
    int i =0; int j = 0;int k = 0;
    while(i<a.length && j <b.length) {
        if(a[i]<b[j]) {
            result[k++] = a[i];
            i++;
        } else {
            result[k++] = b[j];
            j++;
        }
    }
    System.arraycopy(a, i, result, k, (a.length -i));
    System.arraycopy(b, j, result, k, (b.length -j));
    return result;
}

7

Тут оновлена ​​функція. Це видаляє дублікати, сподіваємось, хтось знайде це корисним:

public static long[] merge2SortedAndRemoveDublicates(long[] a, long[] b) {
    long[] answer = new long[a.length + b.length];
    int i = 0, j = 0, k = 0;
    long tmp;
    while (i < a.length && j < b.length) {
        tmp = a[i] < b[j] ? a[i++] : b[j++];
        for ( ; i < a.length && a[i] == tmp; i++);
        for ( ; j < b.length && b[j] == tmp; j++);
        answer[k++] = tmp;
    }
    while (i < a.length) {
        tmp = a[i++];
        for ( ; i < a.length && a[i] == tmp; i++);
        answer[k++] = tmp;
    }
    while (j < b.length) {
        tmp = b[j++];
        for ( ; j < b.length && b[j] == tmp; j++);
        answer[k++] = tmp;
    }
    return Arrays.copyOf(answer, k);
}

+1, дякую за обмін. Одне запитання: чому ви вибрали тип масиву та тип змінної 'temp', довгі?
Hengameh

(У мене є сумніви щодо назви методу.)
сіра борода

5

Це можна зробити в 4 твердженнях, як показано нижче

 int a[] = {10, 20, 30};
 int b[]= {9, 14, 11};
 int res[]=new int[a.legth+b.length]; 
 System.arraycopy(a,0, res, 0, a.length); 
 System.arraycopy(b,0,res,a.length, b.length);
 Array.sort(res)


5
Я не розумію, чому ця відповідь отримала негативні голоси. Це правда, що це не ефективно. Але іноді все, що вам потрібно, - це якнайшвидше виконати роботу. Якщо ви маєте справу з дуже маленькими масивами, скажімо, менше ніж 100 елементів, я вважаю за краще використовувати вищевказаний код, а не писати тривалий код, який не призведе до жодних важливих покращень продуктивності. Тож дякую Sudhir за надання цього простого рішення та SANN3 за його редагування.
Ахмедов

2
Неписана передумова полягає в тому, що sortфункція не може використовувати себе як метод сортування. Це було б нескінченним регресом замість рекурсії. Також інша передумова полягає в тому, що merge_array - це функція, що реалізує сортування. Таким чином, ця відповідь є непридатною у найбільш ймовірному контексті.
Акі Суйконен

У заданому запитанні не було зазначено, що потрібний код призначений лише для малого масиву. Тож ця відповідь була б оманливою, якщо в ній чітко не було зазначено обмеження. Також дивіться мою відповідь нижче. Для написання ефективного коду, який працює для будь-якого розміру масиву,
потрібна

Питання передбачало, що масиви вже впорядковані. Якщо масиви можуть бути дуже великими, це рішення призведе до зупинки і погано працює. Тож ви впевнені, що отримаєте необхідні кінцеві результати, але додаток не працює, і ви б не отримали роботу, якби я брав інтерв'ю.
Кевін М

Функція Array.sort () використовує TimSort, який в значній мірі знайде відсортовані запуски та застосує до них сортування злиття. Як не дивно, цей код насправді навіть не може бути винним за те, що він "неефективний", він фактично потрапляє на фінішну обробку через O (n) час через відсортовані прогони. Ви можете запустити на ньому безліч еталонів, шанси на це добре, що вони часто битимуть код OP.
Tatarize

4

Мені довелося написати це в JavaScript, ось це:

function merge(a, b) {
    var result = [];
    var ai = 0;
    var bi = 0;
    while (true) {
        if ( ai < a.length && bi < b.length) {
            if (a[ai] < b[bi]) {
                result.push(a[ai]);
                ai++;
            } else if (a[ai] > b[bi]) {
                result.push(b[bi]);
                bi++;
            } else {
                result.push(a[ai]);
                result.push(b[bi]);
                ai++;
                bi++;
            }
        } else if (ai < a.length) {
            result.push.apply(result, a.slice(ai, a.length));
            break;
        } else if (bi < b.length) {
            result.push.apply(result, b.slice(bi, b.length));
            break;
        } else {
            break;
        }
    }
    return result;
}

4

Колекції Apache підтримують метод collate з версії 4; це можна зробити за допомогою collateметоду в:

org.apache.commons.collections4.CollectionUtils

Ось цитата з javadoc:

collate(Iterable<? extends O> a, Iterable<? extends O> b, Comparator<? super O> c)

Об’єднує дві відсортовані колекції aта b, в єдиний, відсортований список, щоб зберегти впорядкованість елементів відповідно до компаратора c.

Не заново вигадуйте колесо! Посилання на документ: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

Злиття GallopSearch: O (log (n) * log (i)), а не O (n)

Я пішов вперед і реалізував пропозицію сірої бороди в коментарях. Переважно тому, що мені потрібна була високоефективна критична версія місії для цього коду.

  • Код використовує галоп-пошук, який є O (log (i)), де i - відстань від поточного індексу, відповідного індексу існує.
  • У коді використовується binarySearch, після того як пошук галопом визначив правильний, діапазон. Оскільки галоп обмежив це меншим діапазоном, то отриманий binarySearch також є O (log (i))
  • Галоп і злиття виконуються назад. Це не здається важливим для місії, але це дозволяє об'єднати масиви. Якщо в одному з ваших масивів є достатньо місця для зберігання значень результатів, ви можете просто використовувати його як масив об'єднання та масив результатів. У такому випадку потрібно вказати допустимий діапазон у масиві.
  • У цьому випадку він не потребує розподілу пам'яті (велика економія на критичних операціях). Він просто переконує, що він не може і не може перезаписати будь-які необроблені значення (що можна зробити лише назад). Насправді ви використовуєте один і той же масив як для входів, так і для результатів. Він не зазнає жодних негативних наслідків.
  • Я послідовно використовував Integer.compare (), щоб це можна було вимкнути для інших цілей.
  • Є певний шанс, що я, можливо, трохи поблукав і не використав інформацію, яку я раніше довів. Наприклад, бінарний пошук у діапазоні двох значень, для яких одне значення вже було перевірено. Також може бути кращий спосіб констатувати основний цикл, значення гортання c не знадобиться, якщо вони поєднуються у дві операції послідовно. Оскільки ви знаєте, щоразу будете робити один, а інший. Є місце для трохи польського.

Це має бути найефективнішим способом для цього, з часовою складністю O (log (n) * log (i)), а не O (n). І в гіршому випадку складність часу O (n). Якщо ваші масиви незграбні та мають довгі рядки значень разом, це дозволить зробити карлик будь-яким іншим способом, інакше це буде просто краще за них.

Він має два значення зчитування на кінці масиву, що об'єднується, і значення запису в масиві результатів. Дізнавшись, яке кінцеве значення менше, він здійснює галоп пошуку в цей масив. 1, 2, 4, 8, 16, 32 і т. Д. Коли він знаходить діапазон, де значення зчитування іншого масиву більше. Це двійковий пошук у цьому діапазоні (скорочує діапазон навпіл, шукає правильну половину, повторює до єдиного значення). Потім він масив копіює ці значення в положення запису. Маючи на увазі, що копія за необхідністю переміщується таким чином, що вона не може замінити однакові значення з масиву читання (а це означає, що масив запису та масив читання можуть бути однаковими). Потім він виконує ту саму операцію для іншого масиву, який, як відомо, є меншим, ніж нове значення зчитування іншого масиву.

static public int gallopSearch(int current, int[] array, int v) {
    int d = 1;
    int seek = current - d;
    int prevIteration = seek;
    while (seek > 0) {
        if (Integer.compare(array[seek], v) <= 0) {
            break;
        }
        prevIteration = seek;
        d <<= 1;
        seek = current - d;
        if (seek < 0) {
            seek = 0;
        }
    }
    if (prevIteration != seek) {
        seek = binarySearch(array, seek, prevIteration, v);
        seek = seek >= 0 ? seek : ~seek;
    }
    return seek;
}

static public int binarySearch(int[] list, int fromIndex, int toIndex, int v) {
    int low = fromIndex;
    int high = toIndex - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = list[mid];
        int cmp = Integer.compare(midVal, v);
        if (cmp < 0) {
            low = mid + 1;
        } else if (cmp > 0) {
            high = mid - 1;
        } else {
            return mid;// key found
        }
    }
    return -(low + 1);// key not found.
}

static public int[] sortedArrayMerge(int[] a, int[] b) {
    return sortedArrayMerge(null, a, a.length, b, b.length);
}

static public int[] sortedArrayMerge(int[] results, int[] a, int aRead, int b[], int bRead) {
    int write = aRead + bRead, length, gallopPos;
    if ((results == null) || (results.length < write)) {
        results = new int[write];
    }
    if (aRead > 0 && bRead > 0) {
        int c = Integer.compare(a[aRead - 1], b[bRead - 1]);
        while (aRead > 0 && bRead > 0) {
            switch (c) {
                default:
                    gallopPos = gallopSearch(aRead, a, b[bRead-1]);
                    length = (aRead - gallopPos);
                    write -= length;
                    aRead = gallopPos;
                    System.arraycopy(a, gallopPos--, results, write, length);
                    c = -1;
                    break;
                case -1:
                    gallopPos = gallopSearch(bRead, b, a[aRead-1]);
                    length = (bRead - gallopPos);
                    write -= length;
                    bRead = gallopPos;
                    System.arraycopy(b, gallopPos--, results, write, length);
                    c = 1;
                    break;
            }
        }
    }
    if (bRead > 0) {
        if (b != results) {
            System.arraycopy(b, 0, results, 0, bRead);
        }
    } else if (aRead > 0) {
        if (a != results) {
            System.arraycopy(a, 0, results, 0, aRead);
        }
    }
    return results;
}

Це має бути найефективнішим способом зробити це.


Деякі відповіді мали можливість повторного видалення дубліката. Для цього знадобиться алгоритм O (n), оскільки ви дійсно повинні порівнювати кожен елемент. Тож ось окремо для цього слід застосувати після факту. Ви не можете скачувати кілька записів наскрізь, якщо вам потрібно переглянути їх, хоч ви могли галопувати дублікати, якщо їх було багато.

static public int removeDuplicates(int[] list, int size) {
    int write = 1;
    for (int read = 1; read < size; read++) {
        if (list[read] == list[read - 1]) {
            continue;
        }
        list[write++] = list[read];
    }
    return write;
}

Оновлення: попередня відповідь, не жахливий код, але явно поступається вище.

Ще одна непотрібна гіпероптимізація. Він не тільки викликає масив масиву для кінцевих бітів, але і для початку. Обробка будь-якого вступного неперекриття в O (log (n)) за допомогою binarySearch у дані. O (log (n) + n) - O (n), а в деяких випадках ефект буде досить яскраво виражений, особливо в таких випадках, коли взагалі немає перекриття між об'єднувальними масивами.

private static int binarySearch(int[] array, int low, int high, int v) {
    high = high - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = array[mid];
        if (midVal > v)
            low = mid + 1;
        else if (midVal < v)
            high = mid - 1;
        else
            return mid; // key found
    }
    return low;//traditionally, -(low + 1);  // key not found.
}

private static int[] sortedArrayMerge(int a[], int b[]) {
    int result[] = new int[a.length + b.length];
    int k, i = 0, j = 0;
    if (a[0] > b[0]) {
        k = i = binarySearch(b, 0, b.length, a[0]);
        System.arraycopy(b, 0, result, 0, i);
    } else {
        k = j = binarySearch(a, 0, a.length, b[0]);
        System.arraycopy(a, 0, result, 0, j);
    }
    while (i < a.length && j < b.length) {
        result[k++] = (a[i] < b[j]) ? a[i++] : b[j++];
    }
    if (j < b.length) {
        System.arraycopy(b, j, result, k, (b.length - j));
    } else {
        System.arraycopy(a, i, result, k, (a.length - i));
    }
    return result;
}

1
Викликаний за те, щоб почати щось робити щодо симетрії, але навіщо зупинятися на цьому? Використовуйте галопуючий пошук, нехай він поверне індекс після рівних клавіш. Використовуйте копію масиву, якщо тільки більше 3 елементів. Зауважте, що після цієї копії нічого не змінилося, окрім а) початкового індексу на одному вході та вихідному масиві; б) ваших знань про те, який наступний елемент менший.
сіра борода

Це абсолютно те, що робить реалізований Arrays.sort. Зокрема, це в гіршому випадку злиття. Я думаю, що вони поміняють місцями 2 елементи там, де потрібно, але потрапляють в масив копії для більш ніж 2 елементів. Я не впевнений, чи слід перевіряти наступний елемент лінійно чи бінарним пошуком у ньому. Буде досить велика перевага спекулятивно перевірити, чи можна було б галопом на велику відстань, якщо ви могли пропустити цю відстань. Як і перевірити 8 наперед, і якщо ви можете скопіювати, що ви зберегли себе, 7 операцій з речей, які вам не потрібно дивитися.
Tatarize

@greybeard ... і готово. Також пішов назад, щоб я міг повторно використовувати ту саму пам'ять.
Tatarize

Добре, що ви мотивували балістичний рух. Будемо детальніше придивлятися до денних мийок.
сіра борода

That is totally what the implemented Arrays.sort does( Що : з першої редакції вашої відповіді - або - з мого коментаря 19 лютого?) - не можна знайти жодного з JDK 8 Sunsoft: про яку реалізацію Arrays.sortви звертаєтесь?
сіра борода

2

Ось скорочена форма, написана у javascript:

function sort( a1, a2 ) {

    var i = 0
        , j = 0
        , l1 = a1.length
        , l2 = a2.length
        , a = [];

    while( i < l1 && j < l2 ) {

        a1[i] < a2[j] ? (a.push(a1[i]), i++) : (a.push( a2[j]), j++);
    }

    i < l1 && ( a = a.concat( a1.splice(i) ));
    j < l2 && ( a = a.concat( a2.splice(j) ));

    return a;
}

1
    public class Merge {

    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    public static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {

        // precondition: a[lo .. mid] and a[mid+1 .. hi] are sorted subarrays
        assert isSorted(a, lo, mid);
        assert isSorted(a, mid+1, hi);

        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k]; 
        }

        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)              a[k] = aux[j++];
            else if (j > hi)               a[k] = aux[i++];
            else if (less(aux[j], aux[i])) a[k] = aux[j++];
            else                           a[k] = aux[i++];
        }

        // postcondition: a[lo .. hi] is sorted
        assert isSorted(a, lo, hi);
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid);
        sort(a, aux, mid + 1, hi);
        merge(a, aux, lo, mid, hi);
    }

    public static void sort(Comparable[] a) {
        Comparable[] aux = new Comparable[a.length];
        sort(a, aux, 0, a.length-1);
        assert isSorted(a);
    }


   /***********************************************************************
    *  Helper sorting functions
    ***********************************************************************/

    // is v < w ?
    private static boolean less(Comparable v, Comparable w) {
        return (v.compareTo(w) < 0);
    }

    // exchange a[i] and a[j]
    private static void exch(Object[] a, int i, int j) {
        Object swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }


   /***********************************************************************
    *  Check if array is sorted - useful for debugging
    ***********************************************************************/
    private static boolean isSorted(Comparable[] a) {
        return isSorted(a, 0, a.length - 1);
    }

    private static boolean isSorted(Comparable[] a, int lo, int hi) {
        for (int i = lo + 1; i <= hi; i++)
            if (less(a[i], a[i-1])) return false;
        return true;
    }


   /***********************************************************************
    *  Index mergesort
    ***********************************************************************/
    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    private static void merge(Comparable[] a, int[] index, int[] aux, int lo, int mid, int hi) {

        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = index[k]; 
        }

        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)                    index[k] = aux[j++];
            else if (j > hi)                     index[k] = aux[i++];
            else if (less(a[aux[j]], a[aux[i]])) index[k] = aux[j++];
            else                                 index[k] = aux[i++];
        }
    }

    // return a permutation that gives the elements in a[] in ascending order
    // do not change the original array a[]
    public static int[] indexSort(Comparable[] a) {
        int N = a.length;
        int[] index = new int[N];
        for (int i = 0; i < N; i++)
            index[i] = i;

        int[] aux = new int[N];
        sort(a, index, aux, 0, N-1);
        return index;
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(Comparable[] a, int[] index, int[] aux, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, index, aux, lo, mid);
        sort(a, index, aux, mid + 1, hi);
        merge(a, index, aux, lo, mid, hi);
    }

    // print array to standard output
    private static void show(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            StdOut.println(a[i]);
        }
    }

    // Read strings from standard input, sort them, and print.
    public static void main(String[] args) {
        String[] a = StdIn.readStrings();
        Merge.sort(a);
        show(a);
    }
}

Що це скопіювати a[mid+1 .. hi]в auxпротягом?
сіра борода

1

Я думаю, що введення списку пропусків для більшого відсортованого масиву може зменшити кількість порівнянь і може прискорити процес копіювання в третій масив. Це може бути добре, якщо масив занадто великий.


1
public int[] merge(int[] a, int[] b) {
    int[] result = new int[a.length + b.length];
    int aIndex, bIndex = 0;

    for (int i = 0; i < result.length; i++) {
        if (aIndex < a.length && bIndex < b.length) {
            if (a[aIndex] < b[bIndex]) {
                result[i] = a[aIndex];
                aIndex++;
            } else {
                result[i] = b[bIndex];
                bIndex++;
            }
        } else if (aIndex < a.length) {
            result[i] = a[aIndex];
            aIndex++;
        } else {
            result[i] = b[bIndex];
            bIndex++;
        }
    }

    return result;
}

2
Деякі пояснення було б добре. :)
gsamaras

1
public static int[] merge(int[] a, int[] b) {
    int[] mergedArray = new int[(a.length + b.length)];
    int i = 0, j = 0;
    int mergedArrayIndex = 0;
    for (; i < a.length || j < b.length;) {
        if (i < a.length && j < b.length) {
            if (a[i] < b[j]) {
                mergedArray[mergedArrayIndex] = a[i];
                i++;
            } else {
                mergedArray[mergedArrayIndex] = b[j];
                j++;
            }
        } else if (i < a.length) {
            mergedArray[mergedArrayIndex] = a[i];
            i++;
        } else if (j < b.length) {
            mergedArray[mergedArrayIndex] = b[j];
            j++;
        }
        mergedArrayIndex++;
    }
    return mergedArray;
}

Яка в цьому спасіння благодаті? Це можна скоротити for (int i, j, k = i = j = 0 ; k < c.length ; ) c[k++] = b.length <= j || i < a.length && a[i] < b[j] ? a[i++] : b[j++];. Чим він відрізняється від відповіді Андрія 2014 року ?
сіра борода

1

Алгоритм можна вдосконалити багатьма способами. Наприклад, доцільно перевірити, a[m-1]<b[0]чи є b[n-1]<a[0]. У будь-якому з цих випадків більше порівнянь не потрібно робити. Алгоритм міг просто копіювати вихідні масиви в отриманий в потрібному порядку.

Більш складні вдосконалення можуть включати пошук переплетених частин та запуск алгоритму злиття лише для них. Це може заощадити багато часу, коли розміри об'єднаних масивів різняться в кілька разів.


Для цього вдосконалення було б краще перевірити, де перший елемент потрапить у другий масив з бінарним пошуком, а потім масив скопіювати ці дані для початку. Тоді у випадку, якщо одна з цих перевірок відповідає дійсності, просто було б виконати arraycopy все, потім arraycopy тернар і ви отримаєте такий же результат. Але, у випадку невеликого біта перекриття, вам потрібно лише виконати належний алгоритм під час перекриття і не інший час. Оскільки ви застрягли з O (n), використовуючи деяку швидку команду O (logn), попередньо нічого не обійдеться.
Татариз

1

Ця проблема пов'язана з алгоритмом злиття, в якому два відсортовані підмасиви об'єднані в єдиний відсортований підмасив. Книга CLRS наводить приклад алгоритму та очищує необхідність перевірки, чи досягнуто кінця, додавши до кінця кожного масиву дозорне значення (те, що порівнюється та "більше, ніж будь-яке інше значення").

Я написав це в Python, але це також слід добре перекласти на Java:

def func(a, b):
    class sentinel(object):
        def __lt__(*_):
            return False

    ax, bx, c = a[:] + [sentinel()], b[:] + [sentinel()], []
    i, j = 0, 0

    for k in range(len(a) + len(b)):
        if ax[i] < bx[j]:
            c.append(ax[i])
            i += 1
        else:
            c.append(bx[j])
            j += 1

    return c

групове копіювання елементів знову (вміло) використовуйте дозорну…
сіра борода

1

Ви можете використовувати 2 нитки для заповнення отриманого масиву, один спереду, один ззаду.

Це може працювати без синхронізації у випадку чисел, наприклад, якщо кожен потік вставляє половину значень.


0
//How to merge two sorted arrays into a sorted array without duplicates?
//simple C Coding
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

main()
{
    int InputArray1[] ={1,4,5,7,8,9,12,13,14,17,40};
    int InputArray2[] ={4,5,11,14,15,17,18,19,112,122,122,122,122};
    int n=10;
    int OutputArray[30];
    int i=0,j=0,k=0;
    //k=OutputArray
    while(i<11 && j<13)
    {
        if(InputArray1[i]<InputArray2[j])
        {
            if (k == 0 || InputArray1[i]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray1[i];
            }
            i=i+1;
        }
        else if(InputArray1[i]>InputArray2[j])
        {
            if (k == 0 || InputArray2[j]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray2[j];
            }
            j=j+1;
        }
        else
        {
            if (k == 0 || InputArray1[i]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray1[i];
            }
            i=i+1;
            j=j+1;
        }
    };
    while(i<11)
    {
        if(InputArray1[i]!= OutputArray[k-1])
            OutputArray[k++] = InputArray1[i++];
        else
            i++;
    }
    while(j<13)
    {
        if(InputArray2[j]!= OutputArray[k-1])
            OutputArray[k++] = InputArray2[j++];
        else
            j++;
    }
    for(i=0; i<k; i++)
    {
        printf("sorted data:%d\n",OutputArray[i]);
    };
}

0
public static int[] merge(int[] listA, int[] listB) {
        int[] mergedList = new int[ listA.length + listB.length];
        int i = 0; // Counter for listA
        int j = 0; // Counter for listB
        int k = 0; // Counter for mergedList
        while (true) {
            if (i >= listA.length && j >= listB.length) {
                break;
            }
            if (i < listA.length && j < listB.length) { // If both counters are valid.
                if (listA[i] <= listB[j]) {
                    mergedList[k] = listA[i];
                    k++;
                    i++;
                } else {
                    mergedList[k] = listB[j];
                    k++;
                    j++;
                }
            } else if (i < listA.length && j >= listB.length) { // If only A's counter is valid.
                mergedList[k] = listA[i];
                k++;
                i++;
            } else if (i <= listA.length && j < listB.length) { // If only B's counter is valid
                mergedList[k] = listB[j];
                k++;
                j++;
            }
        }
        return mergedList;
    }

0
var arrCombo = function(arr1, arr2){
  return arr1.concat(arr2).sort(function(x, y) {
    return x - y;
  });
};

2
Ця відповідь не стосується мови програмування Java, хоча це буде гарною відповіддю для JavaScript.
gknicker

Це було співбесідою. У цих випадках від вас не очікується писати "звичайний" код, як вище. Вони шукають "ефективний" код та демонстрацію того, що ви розумієте алгоритми, які використовуються.
d11wtq

0

Моя улюблена мова програмування - JavaScript

function mergeSortedArrays(a, b){
    var result = [];

    var sI = 0;
    var lI = 0;
    var smallArr;
    var largeArr;
    var temp;

    if(typeof b[0] === 'undefined' || a[0]<b[0]){
        smallArr = a;
        largeArr = b;
    } else{
        smallArr = b;
        largeArr = a;
    }

    while(typeof smallArr[sI] !== 'undefined'){
        result.push(smallArr[sI]);
        sI++;

        if(smallArr[sI]>largeArr[lI] || typeof smallArr[sI] === 'undefined'){
            temp = smallArr;
            smallArr = largeArr;
            largeArr = temp;
            temp = sI;
            sI = lI;
            lI = temp;
        }
    }
    return result;
}

0

Можливо, використовуйте System.arraycopy

public static byte[] merge(byte[] first, byte[] second){
    int len = first.length + second.length;
    byte[] full = new byte[len];
    System.arraycopy(first, 0, full, 0, first.length);
    System.arraycopy(second, 0, full, first.length, second.length);
    return full;
}

3
Ви просто їх зливаєте; Сам отриманий масив не відсортований, що було вимогою.
Саньєєв Діман

0
public static void main(String[] args) {
    int[] arr1 = {2,4,6,8,10,999};
    int[] arr2 = {1,3,5,9,100,1001};

    int[] arr3 = new int[arr1.length + arr2.length];

    int temp = 0;

    for (int i = 0; i < (arr3.length); i++) {
        if(temp == arr2.length){
            arr3[i] = arr1[i-temp];
        }
        else if (((i-temp)<(arr1.length)) && (arr1[i-temp] < arr2[temp])){
                arr3[i] = arr1[i-temp];
        }
        else{
            arr3[i] = arr2[temp];
            temp++;
        }
    }

    for (int i : arr3) {
        System.out.print(i + ", ");
    }
}

Вихід:

1, 2, 3, 4, 5, 6, 8, 9, 10, 100, 999, 1001,


Плутанина для іменування індексу arr2не ind2, але temp.
сіра борода

0

Ви можете використовувати потрійні оператори, щоб зробити код трохи більш компактним

public static int[] mergeArrays(int[] a1, int[] a2) {
    int[] res = new int[a1.length + a2.length];
    int i = 0, j = 0;

    while (i < a1.length && j < a2.length) {
        res[i + j] = a1[i] < a2[j] ? a1[i++] : a2[j++];
    }

    while (i < a1.length) {
        res[i + j] = a1[i++];
    }

    while (j < a2.length) {
        res[i + j] = a2[j++];
    }

    return res;
}

0
public static int[] mergeSorted(int[] left, int[] right) {
    System.out.println("merging " + Arrays.toString(left) + " and " + Arrays.toString(right));
    int[] merged = new int[left.length + right.length];
    int nextIndexLeft = 0;
    int nextIndexRight = 0;
    for (int i = 0; i < merged.length; i++) {
        if (nextIndexLeft >= left.length) {
            System.arraycopy(right, nextIndexRight, merged, i, right.length - nextIndexRight);
            break;
        }
        if (nextIndexRight >= right.length) {
            System.arraycopy(left, nextIndexLeft, merged, i, left.length - nextIndexLeft);
            break;
        }
        if (left[nextIndexLeft] <= right[nextIndexRight]) {
            merged[i] = left[nextIndexLeft];
            nextIndexLeft++;
            continue;
        }
        if (left[nextIndexLeft] > right[nextIndexRight]) {
            merged[i] = right[nextIndexRight];
            nextIndexRight++;
            continue;
        }
    }
    System.out.println("merged : " + Arrays.toString(merged));
    return merged;
}

Просто невелике, що відрізняється від оригінального рішення


0

Для маржування двох відсортованих масивів у часовій складності O (m + n) використовуйте нижче підхід лише з одним циклом. m і n - довжина першого масиву і другого масиву.

public class MargeSortedArray {
    public static void main(String[] args) {
        int[] array = new int[]{1,3,4,7};
        int[] array2 = new int[]{2,5,6,8,12,45};
        int[] newarry = margeToSortedArray(array, array2);
        //newarray is marged array
    }

    // marge two sorted array with o(a+n) time complexity
    public static int[] margeToSortedArray(int[] array, int[] array2) {
        int newarrlen = array.length+array2.length;
        int[] newarr = new int[newarrlen];

        int pos1=0,pos2=0;
        int len1=array.length, len2=array2.length;

        for(int i =0;i<newarrlen;i++) {     
            if(pos1>=len1) {
                newarr[i]=array2[pos2];
                pos2++;
                continue;
            }
            if(pos2>=len2) {
                newarr[i]=array[pos1];
                pos1++;
                continue;
            }

            if(array[pos1]>array2[pos2]) {
                newarr[i]=array2[pos2];
                pos2++;
            } else {
                newarr[i]=array[pos1];
                pos1++;
            }   
        }

        return newarr;
    }

}

0
var arr1 = [2,10,20,30,100];
var arr2 = [2,4,5,6,7,8,9];
var j = 0;
var i =0;
var newArray = [];

for(var x=0;x< (arr1.length + arr2.length);x++){
    if(arr1[i] >= arr2[j]){                //check if element arr2 is equal and less than arr1 element
        newArray.push(arr2[j]);
      j++;
    }else if(arr1[i] < arr2[j]){            //check if element arr1 index value  is less than arr2 element
        newArray.push(arr1[i]);
        i++;
    }
    else if(i == arr1.length || j < arr2.length){    // add remaining arr2 element
        newArray.push(arr2[j]);
        j++
    }else{                                                   // add remaining arr1 element
        newArray.push(arr1[i]); 
        i++
    }

}

console.log(newArray);

-1

Оскільки питання не передбачає конкретної мови. Ось рішення в Python. Припускаючи, що масиви вже відсортовані.

Підхід 1 - використовуючи numpy масиви: імпортувати numpy

arr1 = numpy.asarray([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 11, 14, 15, 55])
arr2 = numpy.asarray([11, 32, 43, 45, 66, 76, 88])

array = numpy.concatenate((arr1,arr2), axis=0)
array.sort()

Підхід 2 - Використовуючи список, передбачаючи, що списки сортуються.

list_new = list1.extend(list2)
list_new.sort()

Since the question doesn't assume any specific languageвід 2011/5/11/19: 43, вона позначена як java .
сіра борода

Ваше рішення не скористатися тим фактом , списки вже відсортовані, і його виконання не O (п), так як .sort()це O(n log n)в кращому
dark_ruby

-1

Ось моя реалізація Java, яка видаляє дублікат.

public static int[] mergesort(int[] a, int[] b) {
    int[] c = new int[a.length + b.length];
    int i = 0, j = 0, k = 0, duplicateCount = 0;

    while (i < a.length || j < b.length) {
        if (i < a.length && j < b.length) {
            if (a[i] == b[j]) {
                c[k] = a[i];
                i++;j++;duplicateCount++;
            } else {
                c[k] = a[i] < b[j] ? a[i++] : b[j++];
            }
        } else if (i < a.length) {
            c[k] = a[i++];
        } else if (j < a.length) {
            c[k] = b[j++];
        }
        k++;
    }

    return Arrays.copyOf(c, c.length - duplicateCount);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.