Как подсчет сортировка стабильная сортировка?

предположим, что мой вход (a,b и c различать равные клавиши)

1 6a 8 3 6b 0 6c 4

мой подсчет сортировки сохранит как (отбрасывание a,b и c информация!!)

0(1) 1(1) 3(1) 4(1) 6(3) 8(1)

который даст мне результат

0 1 3 4 6 6 6 8

Итак, как этот стабильный вид? Я не уверен, как это "поддержание относительного порядка записей с равными ключами."

Пожалуйста, объясните.

4 ответов


простой, действительно: вместо простого счетчика для каждого "ведра" это связанный список.

то есть вместо

0(1) 1(1) 3(1) 4(1) 6(3) 8(1)

вы получаете

0(.) 1(.) 3(.) 4(.) 6(a,b,c) 8(.)

(здесь я использую . для обозначения некоторого элемента в ведре).

затем просто сбросьте их обратно в один отсортированный список:

0 1 3 4 6a 6b 6c 8

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

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

поэтому вместо использования O(k) пространство k счетчики, у вас есть O(k) изначально пустые списки, чья сумма длин будет n в конце "подсчета" части алгоритма. Этот вариант подсчета сортировки по-прежнему будет O(n + k) как и раньше.


чтобы понять, почему Счетная сортировка стабильна, вам нужно понять, что Счетная сортировка может использоваться не только для сортировки списка целых чисел, она также может использоваться для сортировки списка элементов, ключ которых является целым числом, и эти элементы будут отсортированы по их ключам, имея дополнительную информацию, связанную с каждым из них.

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

[(GOOG 3), (CSCO 1), (MSFT 1)]

здесь цены на акции являются целочисленными ключами и имена связанных с ними информацией.

ожидаемый выход для сортировки должен быть:

[(CSCO 1), (MSFT 1), (GOOG 3)] 
(containing both stock price and its name,
and the CSCO stock should appear before MSFT so that it is a stable sort)

массив подсчетов будет рассчитан для сортировки этого (скажем, цены на акции могут быть только от 0 до 3):

counts array: [0, 2, 0, 1] (price "1" appear twice, and price "3" appear once)

если вы просто сортируете целочисленный массив, вы можете пройти через массив счетчиков и вывести "1" дважды и" 3 " один раз, и это будет сделано, и после этого весь массив counts станет массивом all-zero.

но здесь мы хотим иметь имена запасов в сортировке вывода, а также. Как мы можем получить эту дополнительную информацию (кажется, массив counts уже отбрасывает эту часть информации)? Ну,связанная информация хранится в исходном несортированном массиве. В несортированном массиве [(GOOG 3), (CSCO 1), (MSFT 1)] у нас есть как название акции, так и ее цена. Если мы узнаем, какая позиция (GOOG 3) должен быть в конечном отсортированном массиве, мы можем скопировать этот элемент в отсортированную позицию в отсортированном массиве.

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

counts array: [0, 2, 2, 3] (i from 0 to 3: counts[i] = counts[i] + counts[i - 1]) 

этот массив накопительной суммы сообщает нам каждый позиция value в конечном отсортированном массиве в настоящее время. Например, counts[1]==2 означает в настоящее время элемент со значением 1 должен быть помещен в тег 2nd слот в отсортированном массиве. Интуитивно, потому что counts[i] является совокупной суммой слева, она показывает, сколько меньших элементов перед ith значение, которое говорит вам, где позиция должна быть для ith значение.

если цена акции 1 $появляется в первый раз, он должен быть выведен на второй позиции отсортированному массиву и если цена акции 3 $появляется в первый раз, он должен быть выведен на третью позицию в отсортированном массиве. Если появляется акция $1 и ее элемент копируется в отсортированный массив, мы уменьшим его количество в массиве counts.

counts array: [0, 1, 2, 3] 
(so that the second appearance of  price stock's position will be 1)

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

sorted array: [null, null, null]
counts array: [0, 2, 2, 3]    

iterate stocks in unsorted stocks from backwards
1. the last stock (MSFT 1)
sorted array: [null, (MSFT 1), null] (copy to the second position because counts[1] == 2)
counts array: [0, 1, 2, 3] (decrease counts[1] by 1)

2. the middle stock (CSCO 1)
sorted array: [(CSCO 1), (MSFT 1), null] (copy to the first position because counts[1] == 1 now)
counts array: [0, 0, 2, 3] (decrease counts[1] by 1)

3. the first stock (GOOG 3)
sorted array: [(CSCO 1), (MSFT 1), (GOOG 3)] (copy to the third position because counts[3] == 3)
counts array: [0, 0, 2, 2] (decrease counts[3] by 1)

как вы можете видеть, после того, как массив сортируется, массив подсчетов (который [0, 0, 2, 2]) не становится массивом все-ноль, как сортировка массива целых чисел. Массив counts не используется для определения того, сколько раз целое число появляется в несортированном массиве, вместо этого он используется для определения положения элемента в конечном отсортированном массиве. И поскольку мы уменьшаем количество каждый раз, когда мы выводим элемент, мы по существу делаем элементы с одинаковыми ключами следующее появление конечная позиция меньше. Вот почему нам нужно перебирать несортированный массив назад, чтобы обеспечить его стабильность.

вывод:

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

ссылки:

некоторые хорошие материалы, объясняющие подсчет сортировки и ее стабильность:

  • http://www.algorithmist.com/index.php/Counting_sort (эта статья объясняет, этот вопрос довольно ну)
  • http://courses.csail.mit.edu/6.006/fall11/rec/rec07.pdf
  • http://rosettacode.org/wiki/Sorting_algorithms/Counting_sort (список реализаций сортировки подсчета на разных языках программирования. Если вы сравните их с алгоритмом в записи Википедии ниже о подсчете сортировки, вы найдете, что большинство из них не реализует точную сортировку подсчета правильно, но реализует только целочисленную функцию сортировки, и они у вас нет дополнительного шага для вычисления массива кумулятивной суммы. Но вы можете проверить реализацию на языке программирования " Go " в этой ссылке, которая предоставляет две разные реализации, одна используется только для сортировки целых чисел, а другая может использоваться для сортировки элементов, содержащих дополнительную информацию)
  • http://en.wikipedia.org/wiki/Counting_sort

ваше решение не является полной сортировкой подсчета и отбрасывает связанные значения.

здесь полное алгоритм сортировки подсчета.

после того, как вы рассчитали гистограммы:

0(1) 1(1) 3(1) 4(1) 6(3) 8(1)

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

0(1) 1(2) 3(3) 4(4) 6(7) 8(8)

теперь вы начинаете от конец из вашего первоначального списка и go назад.

последний элемент 4. Есть 4 элемента меньше или равно 4. Так что 4 пойдет на 4-й позиции. Вы уменьшаете счетчик для 4.

0(1) 1(2) 3(3) 4(3) 6(7) 8(8)

следующий элемент 6c. Есть 7 элементов меньше или равно 6. Так что 6c пойдет на 7-ю позицию. Опять же, вы уменьшаете счетчик для 6.

0(1) 1(2) 3(3) 4(3) 6(6) 8(8)
                      ^ next 6 will go now to 6th position

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


Если ваши три значения "6" различимы, то ваша сортировка подсчета неверна (она отбрасывает информацию о значениях, чего не делает истинная сортировка, потому что истинная сортировка только переупорядочивает значения).

Если ваши три значения" 6 "не различимы, то сортировка стабильна, потому что у вас есть три неразличимых"6" на входе и Три на выходе. Бессмысленно говорить о том, были ли они "переупорядочены" или нет: они идентичный.

понятие нестабильности применяется только тогда, когда значения имеют некоторую связанную информацию, которая не участвует в порядке. Например, если вы сортировали указатели для этих целых чисел, то вы могли бы "сказать разницу" между тремя 6s, глядя на их разные адреса. Тогда имело бы смысл спросить, является ли какой-либо конкретный вид стабильным. Сортировка подсчета, основанная на целочисленных значениях, не будет сортировать указатели. Ля подсчет сортировки на основе значений указателей не будет упорядочивать их по целочисленному значению, а по адресу.