我正在通过“算法入门”第3版。解释的第一件事就是插入排序。在页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}
我在想,这不可能是正确的。伪代码表示2到array.Length。这是2&lt; array.Length,或2&lt; = array.Length?这是怎么回事?
我个人认为这是因为while循环中的0 > 0
谓词。它实际上每次都会短暂一次。
我的解释(从发送给教授的电子邮件中,懒得将其全部输入):
循环仍然以{ 5, 1, 2, 3, 4, 6 }
结尾的原因是i > 0
谓词。每次在while循环中,你减去i中的1(i--
)。这最终将导致0 > 0
结束为假(仅0 == 0
将返回true),但这是循环仍然需要再运行一次的时间。它不断下降。它应该在while循环中再进行一次正确的排序。
另一种解释:
当j以2开头时,键== 4,i == 1且a [i] == 2.在这种情况下,while循环不会运行,因为2&gt; 0但是2不大于4.
j == 3,
key == 6,
i == 2,
a[i] == 4
while循环不会运行,因为4不大于6
j == 4,
key == 1,
i == 3,
a[i] == 6
这次循环运行:
a[i + 1] = a[i] -> a[4] = a[3] -> { 5, 2, 4, 6, 6, 3 }
i-- -> i == 2
再次循环因为2&gt; 0和4> 1
a[i + 1] = a[i] -> a[3] = a[2] -> { 5, 2, 4, 4, 6, 3 }
i-- -> i == 1
再次循环因为1&gt; 0和2&gt; 1
a[i + 1] = a[i] -> a[2] = a[1] -> { 5, 2, 2, 4, 6, 3 }
i-- -> i == 0
这就是它(在我看来)错误的地方。我现在等于零,但是while循环应该再运行一次以从第零个位置获得5个。
教授向我保证他是正确的,但我无法得到正确的结果。我的想法在哪里出错?
教授发给我的C代码中的数组实际上是从索引1开始的。我不知道这个并检查C数组我看到它们都以0开头。是的,那么C代码不会产生正确的输出。教授向我解释了这件事,现在这些作品已经落到了原点。
答案 0 :(得分:7)
我认为教授正在使用基于1的数组表示法,因此使用while (i > 0 && a[i] > key)
时,您缺少循环中的a [0]元素。将您的初始代码更改为此然后它可以工作:
for (var j = 1; j < a.Length; j++)
{
var key = a[j];
var i = j - 1;
while(i >= 0 && a[i] > key) <----------- Try this, or you'd miss the first number
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
另外,如果你想使用教授的代码,只需忽略那里的第0个元素。
在旁注中,您与谁联系过?维斯特?科尔曼?下次当我感到困惑时,我想我也会尝试联系他,因为看起来这位教授回复邮件:)
答案 1 :(得分:2)
你不应该考虑翻译伪代码,而是考虑 翻译您对算法的理解。
首先,数组完全未排序。该算法的工作原理
取连续未分类的元素并将它们插入到
已经排序的部分。起始的“排序部分”是第一个元素,
这是一个简单的“排序”。所以,要插入的第一个元素是
第二。哪个是第二个元素的索引?您j
必须这样做
从那里开始。
然后,i
必须遍历每个已排序元素的索引,
向后,直到它找到插入当前值的位置
或者用完了元素。那么,它必须从哪里开始,在哪里
它必须结束吗?注意它实际上看每个元素
是必须的。
众所周知,一个错误很难发现(和混合 基于1和0的数组的概念肯定没有用,但是没有 只是摆弄它,直到它似乎工作。试着了解一下 代码实际上在做。
答案 2 :(得分:1)
我相信你关于i>0
的论证是正确的,无论教授是什么。说。在伪代码中,循环是while i > 0
,数组索引从1开始。在C#中,数组索引从0开始,因此你应该有while i >= 0
。
答案 3 :(得分:1)
我也遇到了你的问题,我找到了解决方法。我用java编写了算法编码如下。
int a[] = {5,2,4,3,1};
int key;
int i;
for(int j = 0; j < 5; j++)
{
key = a[j];
i = j - 1;
while(i>=0 && a[i]>key)
{
a[i+1]= a[i];
i--;
}
a[i+1] = key;
for(int k=0; k<a.length;k++)
{
System.out.print(a[k]+" ");
}
}
答案 4 :(得分:1)
我遇到了同样的问题。下面是C中的代码,它正确地实现了上面的伪代码。我不像其他解决方案那样使用指针。
事实上,关于这一点的棘手部分是伪代码使用的是基于1的数组符号,这与大多数编程语言不同!
#include <stdio.h>
int main(void)
{
int A[] = { 50, 20, 10, 40, 60, 30 };
int j, key, len, i;
len = (sizeof(A)) / (sizeof(A[0]));
for (j = 1; j < 6; j++) { <-- Change here
key = A[j];
// Insert key into the sorted sequence A[1 .. j - 1].
i = j - 1;
while (i >= 0 && A[i] > key) { <-- Change here
A[i + 1] = A[i];
i--;
}
A[i + 1] = key;
}
for (int z = 0; z < len; z++) {
printf("%d ", A[z]);
}
printf("\n");
}
答案 5 :(得分:0)
记住:A.length从0到n,所以Length应该是A.Length -1。我使用那本书用西班牙语的C ++为我的学生制作了这个算法。在C#中翻译很简单。
一些翻译,以便您更好地理解
largo = length
actual = current
cadena = chain
void InsertionSort::Sort(char cadena[])
{
int largo = strlen(cadena) - 1;
char actual = '0';
int i = 0;
for (int j = 1; j <= largo; j++)
{
actual = cadena[j];
i = j - 1;
while(i >= 0 && cadena[i] > actual)
{
cadena[i + 1] = cadena[i];
i--;
}
cadena[i + 1] = actual;
}
}