Введение
Сложность Mergesort для входных данных, которые были отсортированы в обратном порядке, составляет O (n^2), и Timsort генерируется путем оптимизации Mergesort для этой ситуации. Средняя сложность - n*o (log n), лучший случай - O (n), а худший случай - n*o (log n). И Тимскорт - это сорта стабильности. Идея состоит в том, чтобы сначала разделить отсортированный столбец, а затем объединить разделы, которые выглядят так же, как и шаг Mergesort, но есть некоторые оптимизации для обратных и крупномасштабных данных.
Идея оптимизации сортировки слияния
Есть несколько методов оптимизации для сортировки слияния:
Как и быстрое сортировку, вы можете использовать сортировку вставки или выбрать сортировку для небольших массивов, чтобы избежать рекурсивных вызовов.
Прежде чем вызовать Merge (), вы можете определить, меньше, меньше или равна [MID+1]. Если это так, то нет необходимости объединяться, массив уже заказан. Причина очень проста. Поскольку два субарра уже упорядочены, [MID] является максимальным значением первого субрай, а [MID+1] является минимальным значением второго субрай. Когда [Mid] <= a [середина+1], массив упорядочен в целом.
Чтобы сэкономить время, копирование элементов в вспомогательный массив, роли оригинального массива и вспомогательного массива могут быть заменены на каждом уровне рекурсивного вызова.
Процесс слияния в методе Merge () должен определить, пересекли ли я и J пересек границу, то есть половина края была использована. Еще один способ удалить код, который обнаруживает, была ли половина стороны исчерпана. Конкретный шаг состоит в том, чтобы скопировать вторую половину массива A [] в Aux [] в порядке убывания, а затем объединиться с обоих концов. Для массивов {1,2,3} и {2,3,5} первый субаррей скопирован как обычно, а второй скопирован сзади на фронт, а окончательный элемент в aux [] - {1,2,3,5,3,2}. Недостатком этого метода является то, что он делает сортировку слияния становиться нестабильной сортировкой. Реализация кода выглядит следующим образом:
void merge (int [] a, int lo, int mid, int hi, int [] aux) {for (int k = lo; k <= mid; k ++) {aux [k] = a [k];} для (int k = mid+1; k <= hi; // из двух концов до середины для (int k = lo; k <= hi; k ++) if (aux [i] <= aux [j]) a [k] = aux [i ++]; иначе a [k] = aux [j--];}Шаги Тимскорта
Профила
Идея разделения состоит в том, чтобы отсканировать массив один раз и использовать непрерывную положительную последовательность (если он сортируется по порядку восходящего, то положительная последовательность является восходящей последовательности) или [строго] (для обеспечения стабильности алгоритма сортировки) в качестве разделения (пробег). Если это обратная последовательность, отмените элементы в разделе. Например
1, 2, 3, 6, 4, 5, 8, 6, 4 Результаты разделения
[1,2,3,6], [4,5,8], [6,4]
Затем поверните обратную последовательность
[1,2,3,6], [4,5,8], [4,6]
слияние
Рассмотрим крайний пример, например, длина перегородков составляет 10000, 10, 1000, 10, 10 и 10, конечно, мы надеемся объединить 10 10, 20, 20 и 1000 в 1020. Если мы слияем слева направо, мы будем использовать массив 10000 и комбинацию небольших чисел каждый раз, что слишком дорого. Таким образом, мы можем использовать стратегию для оптимизации порядка слияний.
Пример
Принимая comparateTimsort.sort () в java в качестве примера, используется стек бега, чтобы определить, следует ли его объединить.
if (nremaing <min_merge) {int initrunlen = curvelunandmakeascending (a, lo, hi); BinarySort (A, Lo, Hi, lo + initrunlen); возвращаться; }Сортировать меньше, чем min_merge (32) и сортируйте непосредственно с бинарной вставкой после распределения
int minrun = minrunlength (nremaining); DO {// Узнайте исходную позицию следующего раздела, а также переверните обратную последовательность int runlen = curvunandmakeascending (a, lo, hi); // Убедитесь, что прогоны в стеке пробега больше, чем Minrun. Если текущий раздел слишком мал, выньте элементы сзади, чтобы макияж if (runlen <minrun) {int force = nremaing <= minrun? Nremaing: Minrun; BinarySort (A, Lo, Lo + Force, Lo + Runlen); Runlen = Force; } // Поместите бег в стек запуска ts.pushrun (lo, runlen); // Судите, следует ли это быть объединено. Я начинаю с вершины стека и знаю, что его нельзя объединить // 1. runlen [i - 3]> runlen [i - 2] + runlen [i - 1] // 2. runlen [i - 2]> runlen [i - 1] ts.mergecollapse (); lo += Runlen; nremaing -= runlen; } while (nremaing! = 0); // объединить все оставшиеся прогоны, чтобы завершить сортировку сортировки Assert lo == hi; // слияние оставшегося бега ts.mergeforcollapse (); Утверждение Ts.stacksize == 1;Глядя на более важную функцию
/ *** Если длины последних 2 прогонов добавляются дольше, чем предыдущий, используйте прогон в среднем положении, а пробег с более короткой длиной передней и спины, чтобы слияние*, если длина последних 2 прогонов добавляется короче предыдущего, а затем объединяйте два прогона*/ private void mergollapse () {while (stacksize> 1) {int n = stacksize -2; if (n> 0 && runlen [n-1] <= runlen [n] + runlen [n + 1]) {if (runlen [n-1] <runlen [n + 1]) n--; Mergeat (n); } else if (runlen [n] <= runlen [n + 1]) {mergeat (n); } else {break; // инвариант установлен}}