如何重构这个例程以避免使用递归?
所以我在C#中编写mergesort作为练习,虽然它有效,但回顾代码,还有改进的空间。
基本上,算法的第二部分需要一个例程来合并两个排序列表。
这是我的太长的实现,可以使用一些重构:
private static List<int> MergeSortedLists(List<int> sLeft, List<int> sRight)
{
if (sLeft.Count == 0 || sRight.Count == 0)
{
sLeft.AddRange(sRight);
return sLeft;
}
else if (sLeft.Count == 1 && sRight.Count == 1)
{
if (sLeft[0] <= sRight[0])
sLeft.Add(sRight[0]);
else
sLeft.Insert(0, sRight[0]);
return sLeft;
}
else if (sLeft.Count == 1 && sRight.Count > 1)
{
for (int i=0; i<sRight.Count; i++)
{
if (sLeft[0] <= sRight[i])
{
sRight.Insert(i, sLeft[0]);
return sRight;
}
}
sRight.Add(sLeft[0]);
return sRight;
}
else if (sLeft.Count > 1 && sRight.Count == 1)
{
for (int i=0; i<sLeft.Count; i++)
{
if (sRight[0] <= sLeft[i])
{
sLeft.Insert(i, sRight[0]);
return sLeft;
}
}
sLeft.Add(sRight[0]);
return sLeft;
}
else
{
List<int> list = new List<int>();
if (sLeft[0] <= sRight[0])
{
list.Add(sLeft[0]);
sLeft.RemoveAt(0);
}
else
{
list.Add(sRight[0]);
sRight.RemoveAt(0);
}
list.AddRange(MergeSortedLists(sLeft, sRight));
return list;
}
}
当然可以通过删除递归等来改进/缩短这个例程。甚至还有其他方法来合并两个排序列表。因此,任何重构都是受欢迎的。
虽然我确实有答案,但我很好奇其他程序员将如何改进这个例程。
谢谢你!
So I was writing a mergesort in C# as an exercise and although it worked, looking back at the code, there was room for improvement.
Basically, the second part of the algorithm requires a routine to merge two sorted lists.
Here is my way too long implementation that could use some refactoring:
private static List<int> MergeSortedLists(List<int> sLeft, List<int> sRight)
{
if (sLeft.Count == 0 || sRight.Count == 0)
{
sLeft.AddRange(sRight);
return sLeft;
}
else if (sLeft.Count == 1 && sRight.Count == 1)
{
if (sLeft[0] <= sRight[0])
sLeft.Add(sRight[0]);
else
sLeft.Insert(0, sRight[0]);
return sLeft;
}
else if (sLeft.Count == 1 && sRight.Count > 1)
{
for (int i=0; i<sRight.Count; i++)
{
if (sLeft[0] <= sRight[i])
{
sRight.Insert(i, sLeft[0]);
return sRight;
}
}
sRight.Add(sLeft[0]);
return sRight;
}
else if (sLeft.Count > 1 && sRight.Count == 1)
{
for (int i=0; i<sLeft.Count; i++)
{
if (sRight[0] <= sLeft[i])
{
sLeft.Insert(i, sRight[0]);
return sLeft;
}
}
sLeft.Add(sRight[0]);
return sLeft;
}
else
{
List<int> list = new List<int>();
if (sLeft[0] <= sRight[0])
{
list.Add(sLeft[0]);
sLeft.RemoveAt(0);
}
else
{
list.Add(sRight[0]);
sRight.RemoveAt(0);
}
list.AddRange(MergeSortedLists(sLeft, sRight));
return list;
}
}
Surely this routine can be improved/shortened by removing recursion, etc. There are even other ways to merge 2 sorted lists. So any refactoring is welcome.
Although I do have an answer, I'm curious as to how would other programmers would go about improving this routine.
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
合并两个排序列表可以在 O(n) 内完成。
Merging two sorted lists can be done in O(n).
我对此的看法是:
如果我必须手动完成,我会这么做。 =)
My take on this would be:
Exactly what I'd do if I had to do it by hand. =)
您真的确定您的代码完全有效吗?未经测试,我看到以下内容:
Are you really sure your code works at all? Without testing it, i see the following:
作为起点,我将删除当两个列表中的任何一个具有
Count == 1
时的特殊情况 - 它们可以由您更通用(当前递归)的情况来处理。if (sLeft.Count > 1 && sRight.Count == 0)
将永远为真,因为您已经检查了sRight.Count == 0
在开始 - 所以这个代码永远不会被到达并且是多余的。最后,我不会在您的
else
中执行类似的操作(实际上,这个可以替换您的整个方法):(理想情况下,我会重构它以对每个列表使用整数索引,而不是使用
.RemoveAt
,因为循环列表比销毁列表性能更高,并且因为不过,保持原始列表不变可能会很有用,但这仍然是比原始代码更有效的代码!)As a starting point, I would remove your special cases for when either of the lists has
Count == 1
- they can be handled by your more general (currently recursing) case.The
if (sLeft.Count > 1 && sRight.Count == 0)
will never be true because you've checked forsRight.Count == 0
at the start - so this code will never be reached and is redundant.Finally, instead of recursing (which is very costly in this case due to the number of new Lists you create - one per element!), I'd do something like this in your
else
(actually, this could replace your entire method):(Ideally I'd refactor this to use integer indexes against each list, instead of using
.RemoveAt
, because it's more performant to loop through the list than destroy it, and because it might be useful to leave the original lists intact. This is still more efficient code than the original, though!)您也要求不同的方法。根据使用情况,我可能会按照下面的方式进行操作。下面的代码是惰性的,因此它不会立即对整个列表进行排序,而是仅在请求元素时才进行排序。
编辑:这与 Sergey Osypchuk 的实现基本相同,他从开始到结束都只考虑排序最快,但由于预先对整个列表进行排序,延迟也会更高。因此,正如我所说,根据使用情况,我可能会采用这种方法,另一种方法是类似于 Sergey Osypchuk 的方法
You were asking for differrent approaches as well. I might do as below depending on the usage. The below code is lazy so it will not sort the entire list at once but only when elements are requested.
EDIT: It's basically the same implementatin as Sergey Osypchuk his will from start to finish when looking only at the sorting be fastest but the latency will be higher as well due to the fact of sorting the entire list upfront. So as I said depending on the usage I might go with this approach and an alternative would be something similar to Sergey Osypchuk
通常你可以使用堆栈而不是使用递归
Often you can use a stack instead of use recursion
合并列表(理论上,输入列表是预先排序的)排序可以通过以下方式实现:
如果您从未排序的列表开始,您需要按排序的子序列拆分它,然后使用上面的函数对它们进行合并
Merge list (by theory, input lists are sorted in advance) sorting could be implemented in following way:
In case you start with not sorted list you need to split it by sorted subsequence and than marge them using function above
我从不使用递归进行合并排序。您可以对输入进行迭代传递,利用每次合并传递时排序块大小加倍的事实。跟踪每个输入列表中的块大小和已处理的项目数;当它们相等时,列表就用完了。当两个列表都用完后,您可以继续处理下一对块。当块大小大于或等于您的输入大小时,您就完成了。
编辑:由于我的误解,我之前留下的一些信息是不正确的 - C# 中的列表类似于数组而不是链接列表。我很抱歉。
I never use recursion for merge sort. You can make iterative passes over the input, taking advantage of the fact that the sorted block size doubles with every merge pass. Keep track of the block size and the count of items you've processed from each input list; when they're equal, the list is exhausted. When both lists are exhausted you can move on to the next pair of blocks. When the block size is greater than or equal to your input size, you're done.
Edit: Some of the information I had left previously was incorrect, due to my misunderstanding - a List in C# is similar to an array and not a linked list. My apologies.