Это правильный способ объяснить псевдокод MergeSort? Какой возврат здесь?

На разных веб-сайтах псевдокод похож на приведенный ниже:

merge_sort(num_list)
length <-- length of num_list

if length > 1

    shorter_list_A <-- first half of num_list
    shorter_list_B <-- second half of num_list

    result_A <-- merge_sort(shorter_list_A)
    result_B <-- merge_sort(shorter_list_B)

    sorted_list <-- merge(result_A, result_B)

    return sorted_list
else
    return num_list
end

Итак, если у нас есть 4 числа: 8,3,6,2

MergeSort(8,3,6,2)

первая половина чисел помещена в Shorter_list_A(8,3), а вторая половина чисел помещена в Shorter_list_B(6,2)

    shorter_list_A <-- first half of num_list
    shorter_list_B <-- second half of num_list

Итак, теперь у нас есть два списка: Shorter_list_A(8,3) и Shorter_list_B (6,2).

    result_A <-- merge_sort(shorter_list_A)

Merge_sort снова вызывает merge_sort, поэтому происходит следующее:

merge_sort(Shorter_list_A)  // also 8,3
length <-- length of Shorter_list_A

if length > 1

    shorter_list_A <-- first half of Shorter_list_A 
//8 put to Shorter_list_A
    shorter_list_B <-- second half of Shorter_list_A
//3 put to Shorter_list_B

    result_A <-- merge_sort(shorter_list_A)
//merge_sort calls merge_sort again
    result_B <-- merge_sort(shorter_list_B)

    sorted_list <-- merge(result_A, result_B)

    return sorted_list
else
    return Shorter_list_A
end

Итак, у нас снова есть два списка: short_list_A(8) и Shorter_list_B (6)

Исходя из объяснения, правильно ли я понял это или совершенно неправильно?

Затем он снова вызывает merge_sort. Поскольку длина Shorter_list_A равна всего 1, он содержит только одно число, равное 8. Вот что происходит:

merge_sort(Shorter_list_A)
length <-- length of Shorter_list_A
    return Shorter_list_A
end

Он возвращает Shorter_list_A, что равно 8.

Какой именно возврат здесь? Я застрял там.


person Coder88    schedule 13.09.2015    source источник


Ответы (2)


Вы должны помнить, что он выполняет рекурсию вниз по шорт_списку_A, пока не получит условие length <= 1, когда вы возвращаетесь из рекурсивного вызова, он возвращается к предыдущему вызову в стеке вызовов, который выглядит так:

result_A <-- merge_sort(shorter_list_A) 
// *** return here ***
result_B <-- merge_sort(shorter_list_B)
sorted_list <-- merge(result_A, result_B)
return sorted_list

Таким образом, он делает то же самое для result_B, затем объединяет результаты перед возвратом вверх по стеку вызовов.

Итак, возьмем небольшой пример, где отступ показывает глубину рекурсии:

merge_sort([4,7,2,3,1,8,6,5])
    a = merge_sort([4,7,2,3])
        a = merge_sort([4,7])
            a = merge_sort([4])   // Doesn't recurse anymore length <= 1
            b = merge_sort([7])
            r = merge(a,b) = merge([4], [7])
            return r = [4,7]
        b = merge_sort([2,3])
            a = merge_sort([2])
            b = merge_sort([3])
            r = merge(a, b) = merge([2], [3])
            return r = [2,3]
        r = merge(a,b) = merge([4,7], [2,3])
        return r = [2,3,4,7]
    b = merge_sort([1,8,6,5])
        a = merge_sort([1,8])
            a = merge_sort([1])
            b = merge_sort([8])
            r = merge(a, b) = merge([1],[8])
            return r = [1,8]
        b = merge_sort([6,5])
            a = merge_sort([6])
            b = merge_sort([5])
            r = merge(a, b) = merge([6],[5])
            return r = [5,6]
        r = merge(a,b) = merge([1,8], [5,6])
        return r = [1,5,6,8]
    r = merge(a, b) = merge([2,3,4,7], [1,5,6,8])
    return r = [1,2,3,4,5,6,7,8]

Надеюсь, это поможет.

person AChampion    schedule 13.09.2015
comment
Итак, что я понял: 1) существует не один Shorter_A_list. Есть несколько Shorter_A_lists; по одному на каждом уровне рекурсии. Это правда? 2) поскольку он никогда не возвращает num_list до завершения сортировки, может ли алгоритм работать, удаляя операторы IF, ELSE и RETURN NUM_LIST и просто используя только оператор WHILE LENGTH ›= 1? 3) Я до сих пор не понимаю функции операторов RETURN NUM_LIST и RETURN SORTED_LIST. - person Coder88; 14.09.2015
comment
Да, на каждом уровне рекурсии есть num_list. Если вы поместите его в цикл while, вы потеряете всю предыдущую информацию, поэтому он не будет работать. Возвращаемый num_list получает только выполнение и самую дальнюю глубину дерева рекурсии - когда он не может дальше разбивать num_list, везде он возвращает отсортированный список. - person AChampion; 14.09.2015
comment
Из ваших объяснений и объяснений других сайтов я понял, что делает RETURN. Но почему используется не просто RETURN, а RETURN NUM_LIST и RETURN SORTED_LIST? В статьях о RETURN на таких сайтах, как Википедия, пишут что-то вроде «Некоторые языки программирования позволяют функции указать возвращаемое значение, которое будет передано обратно в код, вызвавший функцию». Передача значения обратно в код, что это значит? - person Coder88; 14.09.2015
comment
См. стек выше, возвращаемое значение присваивается значению a или b в предыдущем вызове, чтобы вы могли затем объединить (a, b). Если бы вы не возвращали значение, вы бы никогда не отразили изменения в стеке вызовов. a на одной глубине отличается от a на другой глубине. - person AChampion; 14.09.2015

При каждом возврате вызывающему объекту будет отправлен отсортированный список элементов, полученных в качестве входных данных.

На самом нижнем уровне, когда есть только один элемент, список сортируется по умолчанию и возвращается. Когда вы получите два отсортированных списка по одному элементу в result_A и result_B, вы передадите их оба методу merge(), и теперь этот метод выполняет тяжелую работу. Это берет два списка и объединяет их, чтобы получить новый список, содержащий элементы обоих списков в отсортированном порядке.

Как выглядит Merge()?

Merge() создаст новый список. Назовем его sorted_list, а затем он начнет чтение из head обоих списков, которые он получил в качестве входных данных, возьмет минимум обоих элементов в позиции head и поместит его в только что созданный sorted_list. Метод будет продолжать потреблять head обоих входных списков, пока они оба не будут завершены. Теперь слияние обоих списков присутствует в sorted_list в отсортированном порядке. Я предположил, что вы хотите, чтобы список отсортировался от минимума к максимуму.

Выполнение вашего примера

На последнем уровне с отдельными элементами первый список из head -> 8 -> null и второй список из head -> 3 -> null объединятся в head -> 3 -> 8 -> null.

На самом этом уровне он объединит два списка head -> 6 -> null и head -> 2 -> null в head -> 2 -> 6 -> null.

Пусть нижний уровень называется 0. На уровне 0 у вас были только отдельные элементы. На уровне 1 у вас будут списки из двух элементов. На уровне 2 у вас будет два списка из 2 элементов, объединенных в один список.

Итак, из head -> 3 -> 8 -> null и head -> 2 -> 6 -> null получится head -> 2 -> 3 -> 6 -> 8 -> null.

И, наконец, этот список будет возвращен. Нет уровней, чтобы подняться выше. Это уровень, на котором вы получили исходный несортированный входной список, поэтому при этом возврате фактически вы вернете вызывающему объекту отсортированный список.

Подводя итоги

Level num_list Shorter_List_A Shorter_List_B Result_A Result_B sorted_list

2     8,3,6,2  8,3            6,2            3,8      2,6      2,3,6,8

1(a)  8,3      8              3              8        3        3,8  
1(b)  6,2      6              2              6        2        2,6

0(a)  8        -              -              -        -        8
0(b)  3        -              -              -        -        3 
0(c)  6        -              -              -        -        6
0(d)  2        -              -              -        -        2 

Итак, мы начали с уровня 2. Вызвали себя рекурсивно на уровне 1(a), 1(b). Затем с уровня 1 снова вызывали себя рекурсивно на уровне 0 (a), 0 (b), 0 (c), 0 (d), а затем снова поднимали объединенные результаты до уровня 2, а затем возвращали отсортированный список.

person displayName    schedule 13.09.2015
comment
Каждый возврат будет отправлять обратно вызывающей стороне отсортированный список элементов, которые он получил в качестве входных данных. Можете ли вы объяснить, что вы имеете в виду здесь? Например, предположим, что последняя рекурсия (уровень 2) имеет только «8» в своем массиве, поэтому с помощью RETURN NUM_LIST она возвращает «8». Означает ли это, что он помещает 8 на вход своего вызывающего абонента? Также поставить «8» на входе уровня merge_sort 1? Чтобы затем он мог объединить 8 и 3 (что также помещается с возвратом num_list), когда вызывается merge_sort на уровне 1? - person Coder88; 14.09.2015
comment
@Coder88: Это означает, что на каждом уровне, какой бы список мы ни получили, мы возвращаем другой список с теми же элементами в отсортированном порядке. Последняя рекурсия - это уровень 0, а не уровень 2 (я начал именовать с последнего уровня и далее до верхнего уровня). Получаем полный несортированный список на верхнем уровне. Затем мы продолжаем разбивать список пополам и достигаем уровня 0. - person displayName; 14.09.2015
comment
@Coder88: мы получаем элементы на уровне 2 и возвращаем только с уровня 2. Но чтобы получить отсортированный список на уровне 2, мы переходим на уровни 1 (a) и 1 (b). А уровень 1 (a, b) идем на уровень 0 (a, b, c и d), чтобы получить отсортированные списки. - person displayName; 14.09.2015
comment
@ Coder88: еще раз посмотрите на таблицу. Я изменил его дизайн. Теперь вы понимаете это лучше? - person displayName; 14.09.2015
comment
Как я понял, RETURN X в подпрограмме означает, что она переводит X из подпрограммы в свою исходную-подпрограмму (или как ее назвать) и исходная-подпрограмма продолжает свою работу со значением, которое она получила от подпрограммы. рутина. Поэтому, когда подпрограмма MergeSort(shorter_list_A) завершена, она возвращает значение из этого списка, поэтому MergeSort(shorter_list_A)=этот список, а затем перемещает этот список в result_A, используя символ ‹--. - person Coder88; 15.09.2015
comment
Вот фиктивная презентация Prezi: prezi.com/5hxesubf5ibu/merge-sort-pseudocode синие стрелки - это вызов подпрограммы (или рекурсии в данном случае). И красные стрелки возвращаются. Как вы думаете, можно ли использовать эту презентацию Prezi для объяснения Merge_sort? (Я знаю, что на нем нет пояснительного текста, но его можно добавить или добавить пояснение за кадром). - person Coder88; 15.09.2015
comment
@Кодер88: Да. Объяснение, которое вы дали, и презентация, оба верны. - person displayName; 15.09.2015
comment
@Coder88: Если вы хотите объяснить это кому-то, держите алгоритм рядом с собой и нарисуйте дерево (именно оно станет, когда вы нарисуете рекурсивные вызовы), показывающее, что было отправлено в node (т. е. подпрограмма) и что было возвращено. - person displayName; 15.09.2015
comment
На самом деле, я просто хотел объяснить это, просто чтобы быть уверенным, что я сам все правильно понимаю. Но программирование немного похоже на математику, иногда его легко понять, но трудно объяснить. - person Coder88; 15.09.2015
comment
@ Coder88: все, что вам нравится. :) - person displayName; 15.09.2015
comment
Но интересно, а куда последний RETURN SORTED_LIST возвращает список? Я имею в виду, что когда весь список отсортирован, объединен и помещен в SORTED_LIST (например, 2,3,6,8), тогда есть RETURN SORTED_LIST. При выполнении последнего RETURN SORTED_LIST, куда возвращается SORTED_LIST? Или что происходит? - person Coder88; 15.09.2015
comment
@Coder88: исходному абоненту. Тот, кто отправил несортированный список на сортировку. - person displayName; 15.09.2015
comment
Но что, если исходного абонента нет? Я имею в виду, что если merge_sort — это настольное программное обеспечение, в котором вы вводите некоторые числа, а программное обеспечение их сортирует, где последний возврат возвращает sorted_list? - person Coder88; 15.09.2015
comment
@ Coder88: Чтобы начать эту сортировку слиянием, в каком-то месте будет строка типа merge_sort(inputList). В этом списке будут числа, которые нужно отсортировать. И окончательный возврат вернет управление обратно к самой этой строке. Обычно результаты хранятся в некоторой переменной, например var sortedList = merge_sort(inputList). - person displayName; 15.09.2015
comment
@ Coder88: элемент управления перейдет к merge_sort() только тогда, когда он откуда-то вызывается. А когда выполнение завершается, управление возвращается вызывающей стороне (будь то программа, командная строка или строка в другой функции). Например, в Java управление переходит к main(), когда пользователь его вызывает. main() - это просто функция, подобная merge_sort() - person displayName; 15.09.2015
comment
. . . И окончательный возврат вернет управление обратно к самой этой строке. Что вы имеете в виду здесь? - person Coder88; 15.09.2015
comment
@Coder88: Например, в Java, когда вы напишете List<int> sorted_list = merge_sort(inputUnsortedList), затем, когда merge_sort() завершится, элемент управления вернется к этой же строке, а sorted_list сохранит результат merge_sort(). Затем элемент управления перейдет к выполнению следующей строки в этом коде. - person displayName; 15.09.2015