有人能用英语解释非递归归并排序是如何工作的吗?
谢谢
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];
}
}
}
}
b[m++]=a[i++];
替换为b[m]=a[i]; i++; m++;
。 - user3932000遍历元素并通过交换它们使每个相邻的两个组按顺序排序。
现在,处理两个组的分组(任意两个,最有可能是相邻的组,但您可以使用第一个和最后一个组),通过反复选择每个组中最小值元素将它们合并为一个组,直到所有4个元素都合并成一个4个元素的组。现在,您只剩下4个元素的组和可能的余数。使用循环实现前面逻辑,这次按照4个元素的组进行操作。这个循环一直运行,直到只剩下一个组为止。
引用自Algorithmist:
自底向上的归并排序是归并排序的一种非递归变体,其中数组通过一系列传递进行排序。在每个传递中,该数组被分成大小为m的块。(最初,m=1)相邻的两个块被合并(与正常的归并排序一样),然后下一个传递使用两倍大的值m。
递归和非递归归并排序的时间复杂度都是O(nlog(n))。这是因为两种方法都以一种或另一种方式使用堆栈。
在非递归方法中,用户/程序员定义并使用堆栈。
在递归方法中,系统内部使用堆栈来存储被递归调用的函数的返回地址。
part_size
,最初为1)和要合并的第一个这样的分区的索引(next_part
,最初为零)。对于每个“步骤”,合并从next_part
和next_part+part_size
开始的大小为part_size
的分区,然后将next_part
增加part_size*2
。如果这会超出数组的末尾... - supercatpart_size
加倍,并将next_part
设置为零。无需使用递归。 - supercat 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;
如果还有人在这个帖子里潜伏着...我已经改编了Rama Hoetzlein的非递归归并排序算法,用于对双向链表进行排序。这种新的排序方法是原地排序、稳定的,并且避免了其他链表归并排序实现中耗时的列表分割代码。
// 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日:修复了影响奇数列表的错误
还有人对此感兴趣吗?可能没有了。不过没关系,我们还是来试试看。
归并排序的精髓在于,你可以将两个(或多个)小的已排序记录运行合并成一个更大的已排序记录,并且你可以通过简单的流式操作“读取第一个/下一个记录”和“追加记录”来完成这个过程——这意味着你不需要一次性在RAM中拥有大量数据集:你只需要用来自不同运行的两个记录即可。如果你能跟踪到文件中已排序运行的起始位置和结束位置,你就可以简单地重复将相邻的运行对合并(到一个临时文件中),直到文件被排序:这需要对文件进行对数次遍历。
单个记录很容易排序:每次合并两个相邻的运行时,每个运行的大小都会翻倍。所以这是一种跟踪方式。另一种方法是使用运行的优先队列。从队列中取出最小的两个运行,将它们合并,并将结果入队,直到只剩下一个运行为止。如果你期望你的数据自然地以排序运行开始,那么这种方法是适当的。
在处理大量数据集时,您需要利用内存层次结构。假设您有几十亿字节的RAM和数千亿字节的数据。为什么不一次合并一千个运行呢?确实可以这样做,并且运行的优先级队列可以帮助您。这将显着减少您必须对文件进行的排序次数。某些细节留给读者作为练习。