这不是家庭作业,我没有钱上学,所以我在高速公路上的收费站工作(长时间没有顾客的情况下)教我自己
我试图首先通过思考来实现一个简单的“mergesort”,如果你喜欢一些实际的学习,那么我的大脑会稍微伸展一下,然后看着解决方案在我使用的手册上:“2008-08-21 |算法设计手册| Springer |作者:Steven S. Skiena | ISBN-1848000693”。
我提出了一个解决方案,它使用数组作为缓冲区来实现“合并”步骤,我将其粘贴到下面。作者使用队列,所以我想知道:
这是我的代码(为了完整起见,我也包括我对分裂功能的实现,但我认为我们只是在这里审查merge
步骤;我不相信这是代码审查的帖子顺便说一下,因为我的问题只针对一种方法,而且与另一种方法相比,它的表现是:
package exercises;
public class MergeSort {
private static void merge(int[] values, int leftStart, int midPoint,
int rightEnd) {
int intervalSize = rightEnd - leftStart;
int[] mergeSpace = new int[intervalSize];
int nowMerging = 0;
int pointLeft = leftStart;
int pointRight = midPoint;
do {
if (values[pointLeft] <= values[pointRight]) {
mergeSpace[nowMerging] = values[pointLeft];
pointLeft++;
} else {
mergeSpace[nowMerging] = values[pointRight];
pointRight++;
}
nowMerging++;
} while (pointLeft < midPoint && pointRight < rightEnd);
int fillFromPoint = pointLeft < midPoint ? pointLeft : pointRight;
System.arraycopy(values, fillFromPoint, mergeSpace, nowMerging,
intervalSize - nowMerging);
System.arraycopy(mergeSpace, 0, values, leftStart, intervalSize);
}
public static void mergeSort(int[] values) {
mergeSort(values, 0, values.length);
}
private static void mergeSort(int[] values, int start, int end) {
int intervalSize = end - start;
if (intervalSize < 2) {
return;
}
boolean isIntervalSizeEven = intervalSize % 2 == 0;
int splittingAdjustment = isIntervalSizeEven ? 0 : 1;
int halfSize = intervalSize / 2;
int leftStart = start;
int rightEnd = end;
int midPoint = start + halfSize + splittingAdjustment;
mergeSort(values, leftStart, midPoint);
mergeSort(values, midPoint, rightEnd);
merge(values, leftStart, midPoint, rightEnd);
}
}
以下是教科书中的参考解决方案:(它在C中,所以我添加标签)
merge(item_type s[], int low, int middle, int high)
{
int i; /* counter */
queue buffer1, buffer2; /* buffers to hold elements for merging */
init_queue(&buffer1);
init_queue(&buffer2);
for (i=low; i<=middle; i++) enqueue(&buffer1,s[i]);
for (i=middle+1; i<=high; i++) enqueue(&buffer2,s[i]);
i = low;
while (!(empty_queue(&buffer1) || empty_queue(&buffer2))) {
if (headq(&buffer1) <= headq(&buffer2))
s[i++] = dequeue(&buffer1);
else
s[i++] = dequeue(&buffer2);
}
while (!empty_queue(&buffer1)) s[i++] = dequeue(&buffer1);
while (!empty_queue(&buffer2)) s[i++] = dequeue(&buffer2);
}
答案 0 :(得分:2)
抽象地说,队列只是一些支持enqueue,dequeue,peek和is-empty操作的对象。它可以通过许多不同的方式实现(使用循环缓冲区,使用链接列表等)。
从逻辑上讲,合并算法最容易用队列来描述。首先,两个队列将值合并在一起,然后在这些队列上重复应用peek,is-empty和dequeue操作,以重建单个排序序列。
在使用数组的实现中,您实际上就像使用队列一样。您刚刚选择使用数组实现这些队列。与使用队列相比,不一定“更好”或“更差”。使用队列使得合并算法的高级操作更加清晰,但可能会引入一些低效率(尽管如果没有基准测试很难确定)。使用数组可能会稍微更高效(同样,你应该测试它!),但可能会模糊算法的高级操作。从Skienna的角度来看,使用队列可能会更好,因为它可以清除算法的高级细节。从您的角度来看,由于性能问题,数组可能会更好。
希望这有帮助!
答案 1 :(得分:0)
您一直担心微小的常数因素,这些因素在很大程度上取决于编译器的质量。鉴于你似乎担心这一点,数组就是你的朋友。下面是整数合并排序的C#实现,我认为它接近于你能得到的最紧密。 [编辑:修了一个小虫。]
如果你想在练习中做得更好,你需要像自然合并排序这样的东西,而不是合并为2的幂,你只需合并输入的相邻非递减序列。这肯定不比2的幂更差,但是当输入数据包含一些排序的序列(即除了纯粹下降的输入序列之外的任何东西)时肯定更快。这留给了学生练习。
int[] MSort(int[] src) {
var n = src.Length;
var from = (int[]) src.Clone();
var to = new int[n];
for (var span = 1; span < n; span += span) {
var i = 0;
for (var j = 0; j < n; j += span + span) {
var l = j;
var lend = Math.Min(l + span, n);
var r = lend;
var rend = Math.Min(r + span, n);
while (l < lend && r < rend) to[i++] = (from[l] <= from[r] ? from[l++] : from[r++]);
while (l < lend) to[i++] = from[l++];
while (r < rend) to[i++] = from[r++];
}
var tmp = from; from = to; to = tmp;
}
return from;
}