我尝试使用嵌套循环实现迭代Merge排序。虽然这个算法排序正确(如排序后的事情顺序正确),但我知道这个实现有问题,因为我试图用它来排序更大的集合并将时间与较慢的排序进行比较,我最终得到了缓慢的时间对于这个迭代实现。例如,排序500个项目的时间为31毫秒,这个实现就像冒泡排序一样。
int main()
{
int size;
cin >> size;
//assume vector is already initialized with values & size
vector<int> items(size);
IterativeMergeSort(items, 0, size - 1);
}
void IterativeMergeSort(vector<int> &items, int start, int end)
{
vector<int> temp(items.size());
int left, middle, right;
for(int outer = 1; outer < 2; outer *= 2)
{
for(int inner = start; inner < end; inner = inner * outer + 1)
{
left = outer - 1;
middle = inner;
right = inner + 1;
ItMerge(items, left, middle, right, temp);
}
}
}
void ItMerge(vector<int> &items, int start, int mid, int end, vector<int> &temp)
{
int first1 = start;
int last1 = mid;
int first2 = mid + 1;
int last2 = end;
int index = first1;
while(first1 <= last1 && first2 <= last2)
{
if(items[first1] <= items[first2])
{
temp[index] = items[first1];
first1++;
}
else
{
temp[index] = items[first2];
first2++;
}
index++;
}
while(first1 <= last1)
{
temp[index] = items[first1];
first1++;
index++;
}
while(first2 <= last2)
{
temp[index] = items[first2];
first2++;
index++;
}
for(index = start; index <= end; index++)
{
items[index] = temp[index];
}
}
答案 0 :(得分:2)
您的算法不是合并排序。它试图成为,但它不是。
据我所知,应该发生的是内循环跨过子序列并合并它们,而外循环控制内循环的序列长度,从1开始,每次迭代加倍,直到那里只是两个子序列,他们合并。
但那不是你的算法正在做的事情。外循环的条件被破坏,因此外循环将只运行一次。并且内环并不成对地采用大致相等的子序列。相反,右子序列恰好是一个元素(mid
是inner
,right
是inner+1
),左子序列始终是目前使用的所有元素(left
是outer-1
,outer
是常数1)。因此,算法将重复合并已经排序的左子序列和单个元素右子序列。
这意味着实际上,您的算法是插入排序,除非您不插入到位,而是将排序的序列复制到缓冲区,在适当的时候插入新元素,然后复制结果背部。所以这是一种非常低效的插入排序。
答案 1 :(得分:0)
下面是自上而下和自下而上合并排序的一些优化示例的链接。自下而上合并排序有点快,因为它会跳过用于重复生成索引子对的递归序列,直到子对表示大小为1的运行。大部分时间用于合并,因此自下而上不是&#39那要快得多。自下而上合并传递的第一遍可以通过交换对而不是复制它们来优化。自下而上的合并排序以temp或原始数组中的排序数据结束。如果需要原始数组,则可以计算通过计数,如果计数为奇数,则第一次通过交换。
两个版本都可以在我的系统(Intel Core i7 2600k 3.4ghz)上在不到一秒的时间内对400万个64位无符号整数进行排序。
merge_sort using vectors works well with less than 9 inputs
对于向量或整数数组,计数/基数排序会更快。
答案 2 :(得分:0)
我终于明白了。
在伪代码中:
for( outer = 1, outer < length, outer *=2)
for(inner = 0; inner < length, inner = inner + (outer *2))
left = inner
middle = (inner + outer) - 1
right = (inner + (outer * 2)) - 1
merge(items, left, middle, right, temp)
在重新思考迭代合并排序应如何工作并查看几个实现之后,在合并方法中,我需要的只是检查传入的中间和右侧索引是否大于或等于向量大小(这样我们处理任何可能超出范围的值,然后像往常一样合并。另外,看this有助于理解它; also this。为了确保它与递归Merge Sort一样有效,我在两者上做了计时,并且两个(递归和迭代)实现产生500,1000,5000和10K值的相同时间进行排序(在某些情况下是迭代解决方案)产生了更快的时间)。