базовый вариант сортировки слиянием k-way

#algorithm #sorting

#алгоритм #сортировка

Вопрос:

я не понимаю этот конкретный пример кода из geeksforgeeks по сортировке слиянием k-way. Более конкретно, я не понимаю, что такое «n» в цикле for в базовом случае для divide() . Это количество учащихся в каждом массиве? (который в данном случае равен 4)

Также не могли бы вы объяснить, что делает цикл for в divide() по отношению к процессу divide()? что должен означать базовый вариант (l == r)?

Спасибо.

Подход: Идея становится понятной, как только мы начинаем рассматривать k массивов как промежуточное состояние алгоритма сортировки слиянием. Поскольку существует k массивов, которые уже отсортированы, объедините k массивов. Создайте рекурсивную функцию, которая будет принимать k массивов, делить их на две части и вызывать функцию рекурсивно с каждой половиной. Базовыми случаями являются случаи, когда значение k меньше 3. Смотрите эту статью о слиянии двух массивов за O (n) время.

Алгоритм: Инициализируйте выходной массив размером N *k. Вызовите функцию divide. Пусть l и r представляют диапазон массивов, которые должны быть объединены, и, следовательно, варьируются от 0 до k-1. На каждом шаге мы рекурсивно вызываем левую и правую половины диапазона, чтобы они были отсортированы и сохранены в выходном массиве. После этого мы объединяем левую и правую половины. Для слияния нам нужно определить диапазон индексов для левой и правой половин в выходном массиве. Мы можем легко найти это. Левая часть будет начинаться с индекса l * n выходного массива. Аналогично, правая часть будет начинаться с индекса ((l r) / 2 1) * n выходного массива.

 
import java.util.*; 
  
class GFG { 
  
    static int n = 4; 
  
    // Function to perform 
    // merge operation 
    static void merge( 
        int l, int r, int[] output) 
    { 
        // To store the starting point 
        // of left and right array 
        int l_in = l * n, r_in 
                          = ((l   r) / 2   1) * n; 
  
        // to store the size of left and 
        // right array 
        int l_c = ((l   r) / 2 - l   1) * n; 
        int r_c = (r - (l   r) / 2) * n; 
  
        // array to temporarily store left 
        // and right array 
        int l_arr[] = new int[l_c], 
            r_arr[] = new int[r_c]; 
  
        // storing data in left array 
        for (int i = 0; i < l_c; i  ) 
            l_arr[i] = output[l_in   i]; 
  
        // storing data in right array 
        for (int i = 0; i < r_c; i  ) 
            r_arr[i] = output[r_in   i]; 
  
        // to store the current index of 
        // temporary left and right array 
        int l_curr = 0, r_curr = 0; 
  
        // to store the current index 
        // for output array 
        int in = l_in; 
  
        // two pointer merge for two sorted arrays 
        while (l_curr   r_curr < l_c   r_c) { 
            if ( 
                r_curr == r_c 
                || (l_curr != l_c 
                    amp;amp; l_arr[l_curr] < r_arr[r_curr])) { 
                output[in] = l_arr[l_curr]; 
                l_curr  ; 
                in  ; 
            } 
            else { 
                output[in] = r_arr[r_curr]; 
                r_curr  ; 
                in  ; 
            } 
        } 
    } 
  
    // Code to drive merge-sort and 
    // create recursion tree 
    static void divide(int l, int r, int[] output, 
                       int arr[][]) 
    { 
        if (l == r) { 
  
            /* base step to initialize the output  
        array before performing merge  
        operation */
            for (int i = 0; i < n; i  ) 
                output[l * n   i] = arr[l][i]; 
  
            return; 
        } 
  
        // to sort left half 
        divide(l, (l   r) / 2, output, arr); 
  
        // to sort right half 
        divide((l   r) / 2   1, r, output, arr); 
  
        // merge the left and right half 
        merge(l, r, output); 
    } 
  
    // Driver Code 
    public static void main(String[] args) 
    { 
        // input 2D-array 
        int arr[][] = { { 5, 7, 15, 18 }, 
                        { 1, 8, 9, 17 }, 
                        { 1, 4, 7, 7 } }; 
  
        // Number of arrays 
        int k = arr.length; 
  
        // Output array 
        int[] output = new int[n * k]; 
  
        divide(0, k - 1, output, arr); 
  
        // Print merged array 
        for (int i = 0; i < n * k; i  ) 
            System.out.print(output[i]   " "); 
    } 
} 
  

откуда: https://www.geeksforgeeks.org/merge-k-sorted-arrays-set-3-using-divide-and-conquer-approach/?ref=rp

Ответ №1:

Я думаю, что это расширенная версия merge sort . Вот сценарий, мы k отсортировали массивы с n элементами (как вы их назвали students) в каждом. Цель состоит в том, чтобы иметь единый отсортированный массив ( output ) всех элементов, размер k*n которого равен . Поскольку k массивы отсортированы, нам просто нужен алгоритм для их объединения. Для этого в рекурсивной функции divide(l, r, output, arr) мы разделяем l-th r-th массивы на два набора и рекурсивно вызываем divide для них. Например, для k=3 нас есть l=0 и r=2 . В функции разделения, поскольку l!=r мы пропускаем первое if и вызываем две функции divide(0, 1, output, arr) and divide(2, 2, output, arr) . Опять же, при первом вызове функции мы вызываем divide(0, 0, output, arr) и divide(1, 1, output, arr) . Теперь, когда у нас есть l==r , мы должны сохранить l-й (равный r-му) массив для вывода (обратите внимание, что merge функция является функцией на месте). Для этого не забудьте преобразовать 2d массив A[n][m] в 1d массив B[n*m] , к которому мы можем получить доступ A[i][j] B[i*n j] . Для получения дополнительных разъяснений я поместил несколько отпечатков в функцию разделения:

 static void divide(int l, int r, int[] output, 
    int arr[][]) 
    { 
        if (l == r) { 
  
            /* base step to initialize the output  
                array before performing merge  
                operation */
            for (int i = 0; i < n; i  ) 
                output[l * n   i] = arr[l][i]; 

            System.out.println("After writing to output:");
            for (int i = 0; i < n * k; i  ) 
                System.out.print(output[i]   " ");
            System.out.println()
            return; 
        } 
  
        // to sort left half 
        divide(l, (l   r) / 2, output, arr); 
  
        // to sort right half 
        divide((l   r) / 2   1, r, output, arr); 
  
        // merge the left and right half 
        merge(l, r, output);
        System.out.print("After Merge:");
        for (int i = 0; i < n * k; i  ) 
                System.out.print(output[i]   " ");
        System.out.println();
    } 
  

и вот результат:

 After writing to output:
[5, 7, 15, 18, 0, 0, 0, 0, 0, 0, 0, 0]  # after divide(0, 0, output, arr)
After writing to output:
[5, 7, 15, 18, 1, 8, 9, 17, 0, 0, 0, 0] # after divide(1, 1, output, arr)
After Merge:
[1, 5, 7, 8, 9, 15, 17, 18, 0, 0, 0, 0] # after merge(0, 1, output)
After writing to output:
[1, 5, 7, 8, 9, 15, 17, 18, 1, 4, 7, 7] # after divide(2, 2, output, arr)
After Merge:
[1, 1, 4, 5, 7, 7, 7, 8, 9, 15, 17, 18] # after merge(0, 2, output)