Быстрая сортировка — нестабильная сортировка.

Стабильная сортировка говорит о том, что одинаковые ключи НЕ проходят друг мимо друга после сортировки

Рассмотрим дублирующийся ключ 4 в индексе массива 8 и 9 в приведенной ниже последовательности:

a = [5 20 19 18 17 8 4 5 4 4], где pivot = 0, i = 1, j = 9

Логика разделов говорит,

i указатель перемещается слева направо. Перемещайте i до тех пор, пока значение a[i] не превышает a[pivot]. swap(a[i], a[j])

j указатель перемещается справа налево. Переместите j до тех пор, пока значение a[j] не превысит a[pivot]. swap(a[i], a[j])

Проделав эту процедуру два раза,

a = [5 4 19 18 17 8 4 5 4 20] Обмен выполнен в i = 1 и j = 9.

a = [5 4 19 18 17 8 4 5 4 20] Остановки на i = 2 и j = 8

a = [5 4 4 18 17 8 4 5 19 20] Обмен выполнен в i = 2 & j = 8

Насколько я понимаю, поскольку дублирующийся ключ 4 потерял свой порядок после двух обменов, быстрая сортировка не является стабильной сортировкой.

Вопрос:

Насколько я понимаю, это причина того, что быстрая сортировка нестабильна? Если да, есть ли у нас какой-либо альтернативный подход к разделению для сохранения порядка ключа 4 в приведенном выше примере?


person overexchange    schedule 28.12.2016    source источник
comment
Быстрая сортировка не является стабильной — это точно. Есть способы сделать быструю сортировку стабильной; они включают в себя запись исходного порядка строк, так что, когда два элемента сравниваются равными, игнорируя исходное положение, вы можете сказать, в каком порядке они должны быть на выходе, используя информацию об исходном положении. Как вы это делаете в деталях, является более сложным обсуждением. Вы, вероятно, можете найти что-то в Интернете с помощью поиска, такого как «стабильная быстрая сортировка».   -  person Jonathan Leffler    schedule 29.12.2016
comment
@JonathanLeffler попытался сделать стабильную сортировку здесь, но не удалось   -  person overexchange    schedule 18.01.2017
comment
Вы смотрели на другие вопросы по SO, найденные при поиске «стабильной быстрой сортировки»? Их довольно много; по крайней мере, у некоторых будут хорошие ответы. Вы также, кажется, получили очень респектабельный ответ на этот вопрос (SO 4171-5443).   -  person Jonathan Leffler    schedule 18.01.2017


Ответы (1)


В определении быстрой сортировки само по себе нет ничего, что делало бы ее стабильной или нестабильной. Это может быть и то, и другое.

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

Хотя этот метод разбиения, безусловно, распространен, не требуется, чтобы алгоритм был быстрой сортировкой. Это просто метод, который является простым и распространенным при применении быстрой сортировки к массиву.

С другой стороны, рассмотрите возможность быстрой сортировки односвязного списка. В этом случае вы обычно делаете разбиение, создавая два отдельных связанных списка, один из которых содержит те, которые меньше, чем значение сводки, а другой содержит те, которые больше, чем значение сводки. Поскольку вы всегда просматриваете список от начала до конца (не так много других разумных вариантов с односвязным списком). Пока вы добавляете каждый элемент в конец подсписка, создаваемые вами подсписки содержат одинаковые ключи в их исходном порядке. Таким образом, в результате получается стабильная сортировка. С другой стороны, если вы не заботитесь о стабильности, вы можете соединить элементы с началом подсписков (это довольно легко сделать с постоянной сложностью). в этом случае сортировка (опять же) будет нестабильной.

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

node *list1 = dummy_node1;
node *add1 = list1;
node *list2 = dummy_node2;
node *add2 = list2;

T pivot = input->data; // easiest pivot value to choose

for (node *current = input; current != nullptr; current = current->next)
    if (current->data < pivot) {
        add1->next = current;
        add1 = add1 -> next;
    }
    else {
        add2->next = current;
        add2 = add2->next;
    }
add1->next = nullptr;
add2->next = nullptr;
person Jerry Coffin    schedule 17.01.2017
comment
Как бы вы выбрали опорный элемент, используя связанный список? Ok)? Можете ли вы представить лучшую производительность, используя связанный список? Слияние/быстрая сортировка известны своей производительностью, в отличие от сортировки элементов искусства. - person overexchange; 17.01.2017
comment
Да, я могу представить ситуации, когда связанный список даст лучшую производительность, чем массив. Нет, это не особенно распространено, но вопрос здесь был о характеристиках быстрой сортировки, а не об обстоятельствах, при которых использование связанного списка было бы оправданным (если бы это было так, я бы проголосовал за то, чтобы закрыть его как дубликат этот предыдущий вопрос). Я не совсем уверен, что вы подразумеваете под сортировкой элементов искусства, поэтому я не могу осмысленно это комментировать. - person Jerry Coffin; 17.01.2017
comment
Можете ли вы помочь мне понять, каков ваш способ разбиения с использованием связанного списка? что поможет обеспечить стабильную сортировку - person overexchange; 18.01.2017
comment
@overexchange: я добавил C-подобный псевдокод для разделения связанного списка. - person Jerry Coffin; 18.01.2017
comment
Вот что я вам скажу. Невозможно поддерживать стабильную сортировку, используя массив или связанный список, сохраняя j как mid вместо listSize-1 - person overexchange; 18.01.2017
comment
@overexchange: я думаю, что это утверждение нуждается в некотором контексте, прежде чем оно будет иметь смысл. j сам по себе ничего для меня не значит. - person Jerry Coffin; 18.01.2017