Нерекурсивная Сортировка Слиянием
может кто-нибудь объяснить на английском языке, как работает Нерекурсивная сортировка слияния ?
спасибо
7 ответов
цикл через элементы и сделать каждую смежную группу из двух отсортированных путем замены двух, когда это необходимо.
теперь, имея дело с группами из двух групп (любые две, скорее всего, смежные группы, но вы можете использовать первую и последнюю группы) объединить их в одну группу, выбирая наименьшее значение элемента из каждой группы неоднократно, пока все 4 элемента не будут объединены в группу из 4. Теперь у вас нет ничего, кроме групп по 4 плюс возможный остаток. Использование цикла вокруг предыдущего логика, сделайте все это снова, кроме этого времени работы в группах по 4. Этот цикл выполняется до тех пор, пока не останется только одна группа.
нерекурсивная сортировка слиянием работает с учетом размеров окон 1,2,4,8,16..2^n над входным массивом. Для каждого окна ("k" в коде ниже) все соседние пары окон объединяются во временное пространство, а затем помещаются обратно в массив.
вот моя единственная функция, основанная на C, нерекурсивная сортировка слияния. Вход и выход в "а". Временное хранение в "б". Однажды я хотел бы иметь версию, которая была на месте:
float a[50000000],b[50000000];
void mergesort (long num)
{
int rght, wid, rend;
int i,j,m,t;
for (int k=1; k < num; k *= 2 ) {
for (int left=0; left+k < num; left += k*2 ) {
rght = left + k;
rend = rght + k;
if (rend > num) rend = num;
m = left; i = left; j = rght;
while (i < rght && j < rend) {
if (a[i] <= a[j]) {
b[m] = a[i]; i++;
} else {
b[m] = a[j]; j++;
}
m++;
}
while (i < rght) {
b[m]=a[i];
i++; m++;
}
while (j < rend) {
b[m]=a[j];
j++; m++;
}
for (m=left; m < rend; m++) {
a[m] = b[m];
}
}
}
}
кстати, это также очень легко докажите, что это O (n log n). Внешний цикл по размеру окна растет как мощность двух, поэтому k имеет n итераций журнала. Хотя есть много окон, покрытых внутренним циклом, все окна для данного k точно покрывают входной массив, поэтому внутренний цикл-O(n). Объединение внутренней и внешней петель: O(n)*O(log n) = O (N log n).
цитирую архив:
сортировка слияния снизу вверх нерекурсивный вариант слияния сортировка, в которой массив сортируется по последовательность проходов. Во время каждого проходим, массив делится на блоки в размере m. (Первоначально m = 1). Каждые два соседних блока объединяются (как и в обычной сортировке слияния), и следующий проход сделан с вдвое большим значение m.
и рекурсивная, и нерекурсивная сортировка слияния имеют одинаковую временную сложность O(nlog(n)). Это связано с тем, что оба подхода используют стек тем или иным способом.
В нерекурсивном подходе пользователь/программист определяет и использует стек
В рекурсивном подходе стек используется внутренне системой для хранения обратного адреса функции, которая называется рекурсивно
основная причина, по которой вы хотите использовать нерекурсивный MergeSort, - это избежать переполнения стека рекурсии. Я, например, пытаюсь отсортировать 100 миллионов записей, каждая запись длиной около 1 кбайт (= 100 гигабайт), в алфавитно-цифровом порядке. Сортировка Порядка (N^2) заняла бы 10^16 операций, т. е. потребовались бы десятилетия, чтобы выполнить даже 0,1 микросекунды за операцию сравнения. Порядок(N log (N)) сортировка слиянием займет менее 10^10 операций или менее часа для выполнения одной и той же операции скорость. Однако в рекурсивной версии MergeSort сортировка 100 миллионов элементов приводит к 50 миллионам рекурсивных вызовов MergeSort (). При рекурсии в несколько сотен байтов на стек это переполняет стек рекурсии, хотя процесс легко помещается в память кучи. Выполнение сортировки слиянием с использованием динамически выделенной памяти в куче - я использую код, предоставленный рамой Хетцлайном выше, но я использую динамически выделенную память в куче вместо использования стека - я могу сортировки 100 миллионов записей с нерекурсивный алгоритм сортировки слиянием, а не переполнение стека. Соответствующий разговор для сайта "Stack Overflow"!
PS: Спасибо за код, рама Хетцлайн.
PPS: 100 гигабайт в куче?!! Ну, это виртуальная куча в кластере Hadoop, и MergeSort будет реализован параллельно на нескольких машинах, разделяющих нагрузку...
Я здесь новенький. Я изменил решение рамы Хетцлейна (спасибо за идеи ). Моя сортировка слиянием не использует последний цикл копирования. Кроме того, он возвращается к сортировке вставки. Я сравнил его на своем ноутбуке, и это самый быстрый. Даже лучше, чем рекурсивная версия. Кстати это в Java и от убывания к возрастанию. И, конечно, это итеративно. Его можно сделать многопоточным. Код стал сложным. Так что если кому интересно, есть смотреть.
код :
int num = input_array.length;
int left = 0;
int right;
int temp;
int LIMIT = 16;
if (num <= LIMIT)
{
// Single Insertion Sort
right = 1;
while(right < num)
{
temp = input_array[right];
while(( left > (-1) ) && ( input_array[left] > temp ))
{
input_array[left+1] = input_array[left--];
}
input_array[left+1] = temp;
left = right;
right++;
}
}
else
{
int i;
int j;
//Fragmented Insertion Sort
right = LIMIT;
while (right <= num)
{
i = left + 1;
j = left;
while (i < right)
{
temp = input_array[i];
while(( j >= left ) && ( input_array[j] > temp ))
{
input_array[j+1] = input_array[j--];
}
input_array[j+1] = temp;
j = i;
i++;
}
left = right;
right = right + LIMIT;
}
// Remainder Insertion Sort
i = left + 1;
j = left;
while(i < num)
{
temp = input_array[i];
while(( j >= left ) && ( input_array[j] > temp ))
{
input_array[j+1] = input_array[j--];
}
input_array[j+1] = temp;
j = i;
i++;
}
// Rama Hoetzlein method
int[] temp_array = new int[num];
int[] swap;
int k = LIMIT;
while (k < num)
{
left = 0;
i = k;// The mid point
right = k << 1;
while (i < num)
{
if (right > num)
{
right = num;
}
temp = left;
j = i;
while ((left < i) && (j < right))
{
if (input_array[left] <= input_array[j])
{
temp_array[temp++] = input_array[left++];
}
else
{
temp_array[temp++] = input_array[j++];
}
}
while (left < i)
{
temp_array[temp++] = input_array[left++];
}
while (j < right)
{
temp_array[temp++] = input_array[j++];
}
// Do not copy back the elements to input_array
left = right;
i = left + k;
right = i + k;
}
// Instead of copying back in previous loop, copy remaining elements to temp_array, then swap the array pointers
while (left < num)
{
temp_array[left] = input_array[left++];
}
swap = input_array;
input_array = temp_array;
temp_array = swap;
k <<= 1;
}
}
return input_array;
на всякий случай, если кто-то все еще скрывается в этой теме ... Я адаптировал нерекурсивный алгоритм сортировки слияния рамы Хетцлейна выше для сортировки двойных связанных списков. Эта Новая Сортировка на месте, стабильна и позволяет избежать дорогостоящего по времени разделения кода, который находится в других реализациях сортировки слиянием связанных списков.
// MergeSort.cpp
// Angus Johnson 2017
// License: Public Domain
#include "io.h"
#include "time.h"
#include "stdlib.h"
struct Node {
int data;
Node *next;
Node *prev;
Node *jump;
};
inline void Move2Before1(Node *n1, Node *n2)
{
Node *prev, *next;
//extricate n2 from linked-list ...
prev = n2->prev;
next = n2->next;
prev->next = next; //nb: prev is always assigned
if (next) next->prev = prev;
//insert n2 back into list ...
prev = n1->prev;
if (prev) prev->next = n2;
n1->prev = n2;
n2->prev = prev;
n2->next = n1;
}
void MergeSort(Node *&nodes)
{
Node *first, *second, *base, *tmp, *prev_base;
if (!nodes || !nodes->next) return;
int mul = 1;
for (;;) {
first = nodes;
prev_base = NULL;
//sort each successive mul group of nodes ...
while (first) {
if (mul == 1) {
second = first->next;
if (!second) {
first->jump = NULL;
break;
}
first->jump = second->next;
}
else
{
second = first->jump;
if (!second) break;
first->jump = second->jump;
}
base = first;
int cnt1 = mul, cnt2 = mul;
//the following 'if' condition marginally improves performance
//in an unsorted list but very significantly improves
//performance when the list is mostly sorted ...
if (second->data < second->prev->data)
while (cnt1 && cnt2) {
if (second->data < first->data) {
if (first == base) {
if (prev_base) prev_base->jump = second;
base = second;
base->jump = first->jump;
if (first == nodes) nodes = second;
}
tmp = second->next;
Move2Before1(first, second);
second = tmp;
if (!second) { first = NULL; break; }
--cnt2;
}
else
{
first = first->next;
--cnt1;
}
} //while (cnt1 && cnt2)
first = base->jump;
prev_base = base;
} //while (first)
if (!nodes->jump) break;
else mul <<= 1;
} //for (;;)
}
void InsertNewNode(Node *&head, int data)
{
Node *tmp = new Node;
tmp->data = data;
tmp->next = NULL;
tmp->prev = NULL;
tmp->jump = NULL;
if (head) {
tmp->next = head;
head->prev = tmp;
head = tmp;
}
else head = tmp;
}
void ClearNodes(Node *head)
{
if (!head) return;
while (head) {
Node *tmp = head;
head = head->next;
delete tmp;
}
}
int main()
{
srand(time(NULL));
Node *nodes = NULL, *n;
const int len = 1000000; //1 million nodes
for (int i = 0; i < len; i++)
InsertNewNode(nodes, rand() >> 4);
clock_t t = clock();
MergeSort(nodes); //~1/2 sec for 1 mill. nodes on Pentium i7.
t = clock() - t;
printf("Sort time: %d msec\n\n", t * 1000 / CLOCKS_PER_SEC);
n = nodes;
while (n)
{
if (n->prev && n->data < n->prev->data) {
printf("oops! sorting's broken\n");
break;
}
n = n->next;
}
ClearNodes(nodes);
printf("All done!\n\n");
getchar();
return 0;
}
отредактировано 2017-10-27: Исправлена ошибка, влияющая на нечетные нумерованные списки