无法从《算法导论》第三版中获得插入排序。正确的。我的思维错误在哪里?
我正在阅读《算法导论》第三版这本书。首先解释的事情之一是插入排序。第18页有一些伪代码:
A = { 5, 2, 4, 6, 1, 3 };
INSERTION-SORT(A)
1 for j = 2 to A.length
2 key = A[j]
4 i = j - 1
5 while (i > 0 and A[i] > key)
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
它说使用伪代码,以便它可以轻松翻译为任何类型的语言(C、C++、Java,他们没有提到,但我猜 C# 也是如此)。由于我用 C# 编程,所以我在 LinqPad 中翻译了它。
int[] a = { 5, 2, 4, 6, 1, 3 };
for (var j = 1; j < a.Length; j++)
{
var key = a[j];
var i = j - 1;
while(i > 0 && a[i] > key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
a.Dump();
您可能会问,为什么 j 从 1 开始,而它明确表示为 2?在书中,数组的索引从 1 开始。是的,我现在可能应该更新所有 [i - 1]
和 [i + i]
以及。
不管怎样,完成后,我运行代码并注意到它实际上没有正确排序。输出为 { 5, 1, 2, 3, 4, 6 }
。已经很晚了,应该停止了,但我努力使代码正确。我做了所有的事情,甚至采用了书中的伪代码(从 2 开始)。仍然不是正确的输出。
我联系了这本书的一位教授,他给我发了插入排序的代码,C语言:
void insertion_sort(int *A, int n) {
for (int j = 2; j <= n; j++) {
int key = A[j];
int i = j-1;
while (i > 0 && A[i] > key) {
A[i+1] = A[i];
i--;
}
A[i+1] = key;
}
}
翻译成C#:
int[] a = { 5, 2, 4, 6, 1, 3 };
for (var j = 2; j <= a.Length; j++)
{
var key = a[j];
var i = j - 1;
while(i > 0 && a[i] > key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
我得到一个数组越界。好吧,也许:
int[] a = { 5, 2, 4, 6, 1, 3 };
for (var j = 2; j <= a.Length - 1; j++)
{
var key = a[j];
var i = j - 1;
while(i > 0 && a[i] > key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
输出: { 5, 1, 2, 3, 4, 6 }
我想,这不可能是正确的。伪代码表示 array.Length 为 2。是 2 < array.Length,或 2 <= array.Length?这是怎么回事?
我个人认为是因为 0 > while 循环中的 0
谓词。实际上每次都会达不到一次。
我的解释(来自我发给教授的电子邮件,懒得把它全部打一遍):
循环仍然以 { 5, 1, 2, 3, 4, 6 }
结束的原因是因为 i > 0 谓词。每次在 while 循环中,您都会减去 i 的 1 (
i--
)。这最终会导致 0 > 0
最终结果为 false(只有 0 == 0
会返回 true),但此时循环仍需要再运行一次。它不断落后一分。它应该再执行一次 while 循环才能正确排序。
另一种解释:
当 j 以 2 开头时,key == 4,i == 1 且 a[i] == 2。在这种情况下 while 循环不会运行,因为 2 > > 0 但 2 不大于 4。
j == 3, 键==6, 我==2, a[i] == 4
While 循环不会运行,因为 4 不大于 6
j == 4, 键== 1, 我==3, a[i] == 6
这次运行 While 循环:
a[i + 1] = a[i] -> a[4] = a[3] -> { 5, 2, 4, 6, 6, 3 } 我---> i == 2
再次 while 循环,因为 2 > 0和4> 1
a[i + 1] = a[i] -> a[3] = a[2] -> { 5, 2, 4, 4, 6, 3 } 我---> i == 1
再次 while 循环,因为 1 > 0和2> 1
a[i + 1] = a[i] -> a[2] = a[1] -> { 5, 2, 2, 4, 6, 3 } 我---> i == 0
这就是(在我看来)错误的地方。 i 现在等于 0,但 while 循环应该再运行一次,以将 5 从第 0 个位置取出。
教授向我保证他是正确的,但我无法得到正确的输出。我的想法哪里出了问题?
教授发给我的 C 代码中的数组实际上是以 1 的索引开始的。我不知道这一点,检查 C 数组时我发现它们都以 0 开头。是的,那么 C 代码就不会了。 t 产生正确的输出。教授向我解释了这一点,现在这些碎片就各归其位了。
I am working my way through the book Introduction to Algorithms, 3rd edition. One of the first things explained is the insertion sort. On page 18 there is some pseudo code:
A = { 5, 2, 4, 6, 1, 3 };
INSERTION-SORT(A)
1 for j = 2 to A.length
2 key = A[j]
4 i = j - 1
5 while (i > 0 and A[i] > key)
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
It says that pseudo code is used so that it is easily translated to any kind of language (C, C++, Java, they don't mention, but I guess C# too). Since I program in C#, I translated it in LinqPad.
int[] a = { 5, 2, 4, 6, 1, 3 };
for (var j = 1; j < a.Length; j++)
{
var key = a[j];
var i = j - 1;
while(i > 0 && a[i] > key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
a.Dump();
You're probably going to ask, why does j start at 1, when it clearly says 2? In the book, the array has an index starting at 1. And yes, I now I probably should have updated all the [i - 1]
and [i + i]
as well.
Anyways, after I was done, I run the code and notice that it doesn't actually sort correctly. The output is { 5, 1, 2, 3, 4, 6 }
. It was late and should have stopped, but I struggled on to make the code correct. I did everything, even taking the pseudo code as is from the book (starting at 2). Still not the correct output.
I contacted one of the professors of the book, and he send me the code for the insertion sort, in C:
void insertion_sort(int *A, int n) {
for (int j = 2; j <= n; j++) {
int key = A[j];
int i = j-1;
while (i > 0 && A[i] > key) {
A[i+1] = A[i];
i--;
}
A[i+1] = key;
}
}
Translated in C#:
int[] a = { 5, 2, 4, 6, 1, 3 };
for (var j = 2; j <= a.Length; j++)
{
var key = a[j];
var i = j - 1;
while(i > 0 && a[i] > key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
I get an array out of bounds. Okay then maybe:
int[] a = { 5, 2, 4, 6, 1, 3 };
for (var j = 2; j <= a.Length - 1; j++)
{
var key = a[j];
var i = j - 1;
while(i > 0 && a[i] > key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
Output: { 5, 1, 2, 3, 4, 6 }
I'm thinking, this can't be correct. The pseudo code says 2 to array.Length. Is that 2 < array.Length, or 2 <= array.Length? What is going on here?
I personally think it is because of the 0 > 0
predicate in the while loop. It actually falls short one time each time.
My explanation (from my email sent to the professor, to lazy to type it all over):
The reason why the loop still ends up with { 5, 1, 2, 3, 4, 6 }
is because of the i > 0
predicate. Every time in the while loop you subtract 1 of i (i--
). This will eventually lead to 0 > 0
which ends up false (only 0 == 0
will return true), but this is when the loop still needs to run one more time. It continuously falls one short. It should go do the while loop 1 more time to properly sort.
Another explanation:
When j starts with 2, key == 4, i == 1 and a[i] == 2. The while loop won't run in this case because 2 > 0 but 2 isn't greater than 4.
j == 3,
key == 6,
i == 2,
a[i] == 4
While loop won't run because 4 is not greater than 6
j == 4,
key == 1,
i == 3,
a[i] == 6
While loop runs this time:
a[i + 1] = a[i] -> a[4] = a[3] -> { 5, 2, 4, 6, 6, 3 }
i-- -> i == 2
Again while loop because 2 > 0 and 4 > 1
a[i + 1] = a[i] -> a[3] = a[2] -> { 5, 2, 4, 4, 6, 3 }
i-- -> i == 1
Again while loop because 1 > 0 and 2 > 1
a[i + 1] = a[i] -> a[2] = a[1] -> { 5, 2, 2, 4, 6, 3 }
i-- -> i == 0
And here is where it goes (in my opinion) wrong. i is now equal to zero, but the while loop should run one more time to get the 5 out of the zero-th position.
The professor assures me that he is correct, but I can't get the right output. Where is my thinking going wrong?
The array in the C code that got sent to me by the professor was actually starting with an index of 1. I did not know this and checking upon C arrays I saw that they all start with 0. Yes, then the C code doesn't produce the correct output. The professor explained this to me and the pieces now fall into its place.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我认为教授使用的是基于 1 的数组表示法,因此使用
while (i > 0 && a[i] > key)
时,您缺少 a[0] 元素循环。将您的初始代码更改为此,然后它就可以工作了:另外,如果您想使用教授的代码,只需忽略那里的第 0 个元素即可。
顺便说一句,您联系了谁?里维斯特?科曼?下次当我感到困惑时,我想我也会尝试联系他,因为这位教授似乎回复了邮件:)
I think the prof is using 1-based array notation, so with
while (i > 0 && a[i] > key)
, you are missing the a[0] element in the loop. Change your initial code to this then it works:Also, if you want to use the professor's code, just ignore the 0-th element there.
On a side note, who did you contact? Rivest? Corman? Next time I get confused I think I'll try to contact him too, since it seems this professor reply mails:)
你不应该考虑翻译伪代码,而应该考虑
翻译你对算法的理解。
该数组一开始是完全未排序的。该算法的工作原理是
获取连续的未排序元素并将它们插入到
已经排序的部分。起始“排序部分”是第一个元素,
这是简单的“排序”。因此,要插入的第一个元素是
第二。第二个元素的索引是哪个?你的
j
必须从那里开始。
然后,
i
必须遍历每个已排序元素的索引,向后,直到找到插入当前值的位置
或元素耗尽。那么,它必须从哪里开始,又从哪里开始呢?
它必须结束吗?注意它实际上会查看每个元素
是必须的。
众所周知,相差一的错误很难被发现(并且混合
基于 1 和基于 0 的数组的概念肯定没有帮助),但不要
只是摆弄直到它看起来有效。尝试了解什么是
代码实际上是在做。
You should not think about translating the pseudocode, but about
translating your understanding of the algorithm.
The array is completely unsorted at first. The algorithm works by
taking successive unsorted elements and inserting them into the
already sorted part. The starting "sorted part" is the first element,
which is trivially "sorted". So, the first element to insert is the
second. Which is the index of the second element? Your
j
has tostart from there.
Then,
i
has to go through each of the sorted elements' indices,backwards, until it either finds the place to insert the current value
or runs out of elements. So, where does it have to start, and where
does it have to end? Take care that it actually looks at each element
is has to.
Off-by-one errors are notoriously difficult to spot (and mixing
notions of 1-based and 0-based arrays surely does not help), but don't
just fiddle around until it seems to work. Try to understand what the
code is actually doing.
我相信你关于
i>0
的论点是正确的,无论教授如何。说。在伪代码中,循环是while i > 。 0
并且数组索引从 1 开始。在 C# 中,数组索引从 0 开始,因此您应该使用while i >= 0
。I believe your argument about
i>0
is correct, regardless of what the prof. says. In the pseudo-code, the loop iswhile i > 0
and the array indexing starts with 1. In C#, array indexing starts with 0, therefore you should havewhile i >= 0
.我遇到了同样的问题。下面是正确实现上述伪代码的 C 代码。我没有像其他解决方案一样使用指针。
事实上,棘手的部分是伪代码使用基于 1 的数组表示法,这与大多数编程语言不同!
I experienced the same problem. Below is the code in C which implements the above pseudo-code correctly. I am not using pointers, like other solutions.
Indeed, the tricky part about this was that the pseudo code is using 1-based array notations unlike most programming languages!
我也遇到了你的问题,并且找到了解决方案。我用java编写了算法,如下所示。
I also came across your problem, and I found the solution to this. I coded the algorithm in java as below.
请记住:A.length 从 0 到 n,因此 Length 应为 A.Length -1。我使用那本书,用西班牙语用 C++ 为我的学生编写了这个算法。用 C# 翻译很简单。
一些翻译,以便您可以更好地理解
Remember: A.length goes from 0 to n, so Length should be A.Length -1. I made this algorithm for my students in C++ in spanish, using that book. Is simple to translate in C#.
some translation so you can understand better
当转换为 0 数组时,请注意您可能会错过数组的第一个元素。
解决这个问题的一种方法是 while i > -1;
JavaScript:
When translating to 0 array, be aware that you can miss the first element of the array.
one way to solve it is to while i > -1;
Javascript: