无法从第3版算法的介绍中获得插入排序。对。我的思维错误在哪里?

时间:2011-07-22 10:56:38

标签: algorithm pseudocode insertion-sort

我正在通过“算法入门”第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代码不会产生正确的输出。教授向我解释了这件事,现在这些作品已经落到了原点。

6 个答案:

答案 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;
    }
}