Как работает рекурсия в этом случае? - PullRequest
0 голосов
/ 22 июня 2011
var arr = [7,3,28,8,9,13,1500,45];

function qsort(a) {
    if (a.length == 0) return [];

    var left = [], right = [], pivot = a[0];

    for (var i = 1; i < a.length; i++) {
        a[i] < pivot ? left.push(a[i]) : right.push(a[i]);
    }

    return qsort(left).concat(pivot, qsort(right));
}

alert(qsort(arr));

Эта процедура сортирует массив с помощью алгоритма быстрой сортировки.Вопрос в том, как базовый случай if (a.length == 0) return []; будет когда-либо верным, чтобы остановить рекурсию?

Ответы [ 7 ]

6 голосов
/ 22 июня 2011

Только помните:

  • pivot - это первый элемент в массиве
  • left - это элементы ниже pivot
  • rightэто предметы выше, чем pivot

Итак, когда у вас в первый раз qsort( [7,3,28,8,9,13,1500,45] ):

left=[3]      pivot=7      right=[28,8,9,13,1500,45]

Теперь qsort() рекурсивно вызывается на оба left и right Массивы.

Давайте просто посмотрим на left, поэтому qsort( [3] ):

left=[]       pivot=3        right=[]

Еще раз, qsort() рекурсивно вызывается для массивов left и right.

Опять же, давайте просто посмотрим на left, поэтому qsort( [] ):

И что делает самое первое qsort()?

if (a.length == 0) return [];

Если он получил пустой массив (что он и сделал здесь), он просто возвращает пустой массив, останавливая выполнение в этой ветви.

Поскольку каждый вызов qsort() выталкивает первый элемент из массива, каждый раз, когда вызывается qsort(), полученный массив становится все короче и короче, пока пустой массив не будет отправлен на qsort().


Другой способ думатьиз этого:

Потенциально сбивающая с толку часть состоит в том, что qsort() выбивает первый элемент, а затем разделяет остальную часть массива на 2 части.

Только представьте, если не сделалt разбил массив, но просто выбил первый предмет.

Эта функция на самом деле ничего не делает, но рекурсивно вызывает себя с остатком от массива.

function recursive_test( arr ) {
    if( arr.length === 0 ) { return []; } // empty Array? Just return.

    var first_item = arr.shift();      // first item in the Array (the head)
                            // now "arr" represents the remainder (the tail)

    return recursive_test( arr );  // Just send the "tail" to the same function
                                   //   so the next time through, the Array is 
}                                  //   shorter by 1

Поэтому, когда вы вызываете функцию, происходит следующее:

var array = [5,2,8,3,6,9,0];  // original Array

recursive_test( array );  

// the first time it gets the full Array
// [5,2,8,3,6,9,0]

// but then it pops the first item off, and calls itself with the "tail" of the Array
// [2,8,3,6,9,0]

// again it calls itself, just with the "tail"
// [8,3,6,9,0]

// and again, and again, and again...
// [3,6,9,0]
// [6,9,0]
// [9,0]
// [0]
// []

// The last time it gets an empty Array. 
// The function sees that it gets an empty Array, and just returns, 
//   halting the recursion

В вашей функции происходит то же самое, за исключением того, что вместо "головы" и "хвоста" с рекурсивным передачей "хвоста" вы получаете "голову" (pivot) и«Хвост», который разбивается на 2 массива (left и right).

Обе части хвоста отправляются на рекурсивные вызовы, отсекая головы, разбивая остаток и делая этоснова, пока ничего не осталось.

6 голосов
/ 22 июня 2011

Массив, передаваемый рекурсивному вызову, всегда как минимум на один меньше, чем тот, который был передан в функцию, потому что он не будет содержать элемент pivot. Таким образом, вы в конечном итоге попали в базовый случай, где a.length == 0, и вернетесь без повторения.

6 голосов
/ 22 июня 2011
if (a.length == 0) return [];

когда длина равна 0, она останавливается.

2 голосов
/ 22 июня 2011

Публикация этого как отдельного ответа, так как он занимает немного места.

Вот представление рекурсивных вызовов.Я изменил left на lo, right на hi и pivot на piv, чтобы сэкономить место.

Несмотря на то, что за конкатенацией может быть трудно следовать, она показываеттекут довольно хорошо.

                          qsort( [7,3,28,8,9,13,1500,45] )
                                           |
             |-----------------------------v-----------------------------------|
             | lo=[3]                   piv=7           hi=[28,8,9,13,1500,45] |
                 |                                                  |
                 |                                                  |
                 v                                                  v
            qsort( [3] )                             qsort( [28,8,9,13,1500,45] )
                 |                                                  |
   |-------------v------------|       |-----------------------------v--------------------------------------------|
   | lo=[]    piv=3     hi=[] |       | lo=[8,9,13]               piv=28                            hi=[1500,45] |
       |                 |                   |                                                              |
       |                 |                   |                                                              |
       v                 v                   v                                                              v
  qsort( [] )       qsort( [] )       qsort( [8,9,13] )                                           qsort( [1500,45] )
                                             |                                                              |
                                |------------v---------------|                                |-------------v--------------|
                                | lo=[]   piv=8    hi=[9,13] |                                | lo=[45]   piv=1500   hi=[] |
                                    |                  |                                            |                  |
                                    |                  |                                            |                  |
                                    v                  v                                            v                  v
                                qsort( [] )      qsort( [9,13] )                              qsort( [45] )       qsort( [] )
                                                       |                                            |  
                                         |-------------v -----------|                  |------------v-------------|
                                         | lo=[]    piv=9   hi=[13] |                  | lo=[]    piv=45    hi=[] |
                                            |                  |                           |                  |
                                            |                  |                           |                  |
                                            v                  v                           v                  v
                                       qsort( [] )        qsort( [13] )                qsort( [] )         qsort( [] )
                                                               |           
                                                   |-----------v-------------|
                                                   | lo=[]   piv=13    hi=[] |          
                                                       |                 |          
                                                       |                 |    
                                                       v                 v   
                                                  qsort( [] )        qsort( [] ) 

Чтобы следовать за конкатенацией, в основном начинайте с корня, следуйте lo как можно дальше и глубже, пока не сможете углубиться.

Затем вернитесь назад, отметив последний пройденный вами piv, и следуйте за его веткой hi, за которой следуют все ветки lo, которые вы можете сделать.

Повторите этот процесс, всегда отдавая предпочтениеlo разветвляется и принимает к сведению piv при прохождении их во время обратного отслеживания.

Это даст вам полностью отсортированный массив.

2 голосов
/ 22 июня 2011

В быстрой сортировке вы используете подход «разделяй и властвуй» - вместо того, чтобы решать всю проблему сразу, вы делите задачу пополам, решаете каждую половину, а затем объединяете ответы обратно.

Чтобы решить каждую половину проблемы, мы просто вызываем рекурсивный вызов быстрой сортировки - то есть, мы снова делим проблему пополам, пока не доберемся до чего-то, что мы не сможем разделить дальше ... :

if (a.length == 0) return [];

Итак, теперь, когда мы разделили его пополам и решили все эти проблемы, мы можем объединить их вместе.

эта строка:

return qsort(left).concat(pivot, qsort(right));

говорит: «Возьми левую подзадачу и правую подзадачу, соедини их, и это мой ответ».

Такого рода пузыри наверху, склеивающие все разные подмассивы и генерирующие массив с ответом.

Это немного сложнее, но в любом случае это рекурсивный бит.

0 голосов
/ 22 июня 2011

Это будет строка if (a.length == 0) return [];. Рекурсия останавливается, когда вы передаете пустой массив в функцию. Поскольку ваш входной массив делится на два для каждой рекурсии, это обязательно произойдет.

Пример нижнего уровня:

+ это конкатенация

["a","b","c"] заканчивается

[] + [a] + [] + [b] + [] + [c] + [] b - это точка, а a (слева) и c (справа) проходят другую итерацию быстрой сортировки. это добавляет [] к каждой из сторон, а затем объединяет все три вместе.

0 голосов
/ 22 июня 2011

Это приведет к остановке рекурсии

if (a.length == 0) return [];
...