【问题标题】:Exam pratice sample with matrices and recursion带有矩阵和递归的考试练习样本
【发布时间】:2023-04-10 17:04:02
【问题描述】:

这是过去考试(编程基础)中的一个问题,我不知道如何解决(经过 2 个多小时的尝试):

编写一个方法void fillingSumOfNeighbours(int[][]m),假设m 是一个矩阵,其中最后一列和一行已经被填充(非零),用总和m[i][j]=m[i][j+1]+m[i+1][j]+m[i+1][j+1] 填充剩余的值。这个方法必须是递归的!

到目前为止,我可以以正确的方式填充矩阵,但如果不使用字段计数器就无法使递归有限(对我来说似乎不正确,因为使用字段计数器停止递归需要定义班级)。有没有办法定义直接递归方法只能在该方法内运行的次数?

基本上应该这样做:

_ _ 1
_ _ 1
1 1 1 

_ _ 1
_ 3 1
1 1 1

_ 5 1
_ 3 1
1 1 1

_ 5 1
5 3 1 
1 1 1

13 5 1
5  3 1
1  1 1

【问题讨论】:

  • 您知道,“尝试超过 2 小时”并没有那么令人印象深刻。有些问题需要几天、几个月、.. 甚至几代!!好吧,无论如何 - 我推荐好的 ole P&P - 纸笔 .. 但我必须说 2 小时是一个很好的最低限度;)
  • 虽然我是编程新手,但我对问题并不陌生(我在我的硕士课程中)。我并不是说要令人印象深刻。我这样说是为了让人们可以通过了解 x 难度问题所花费的时间来衡量我的理解/菜鸟水平(并调整答案)。
  • 如果我正确理解了您的代码,您将在每次递归调用时填写整个数组。这似乎是很多重复的工作。
  • 对这个问题使用递归是愚蠢的——这个问题本质上没有递归。尝试在没有基准的情况下解决它。
  • 您的解决方案的主要问题是“递归”解决方案并不是真正的递归。您正在迭代地解决问题。

标签: java recursion multidimensional-array matrix


【解决方案1】:

通过查看您的示例和问题陈述,很明显,您需要运行 3 次递归:-

  • 一个用于垂直向上移动
  • 一个用于向左水平移动
  • 还有一个用于斜向左。

因此,要管理该移动,您还必须将索引传递给您的方法。希望您可以修改方法。

所以,基本上你的递归调用将是这样的:-

fill(arr, row - 1, col);
fill(arr, row, col - 1);
fill(arr, row - 1, col - 1);

假设 fill 是您的方法,它采用 3 个参数:-

  • int[][] arr
  • int row
  • int col

现在,如您所见,第一次调用正在减少行值,从而向上移动索引。第二个电话向左移动。并且最后一次调用将需要覆盖top-left most 索引。

在您的方法中,您只需使用公式编写逻辑来填充您传递的当前索引。是的,不要忘记基本条件,即当任一索引小于 0 时,立即返回。

所以,这就是你的方法的样子:-

public static void fill(int[][] arr, int row, int col) {
    arr[row][col] = arr[row][col + 1] + arr[row + 1][col] + arr[row + 1][col + 1];
    if (row <= 0 || col <= 0) {
        return;
    }
    fill(arr, row - 1, col);
    fill(arr, row, col - 1);
    fill(arr, row - 1, col - 1);
}

初始调用将类似于:-

fill(arr, row - 2, col - 2);

其中row 是最大行大小,col 是最大列大小。

【讨论】:

  • 只是一个建议...您的解决方案似乎很不错。但是他也可以使用 counter 并递归调用那些具有 if 条件的方法来修改具有行和列索引的数组。它看起来会更奇怪,但会给出递归的真正本质。
  • 我不知道你在说什么计数器,但在我发布的方法中,你可以假设 rowcol 只是计数器,基于我拥有的值基本条件,另一方面,背靠背运行并行递归。在为merge sortquick sort 编码时,您可能熟悉这些类型的递归。所以,我真的不明白你真正的递归本质的定义是什么。
  • 我当时的想法是只调用一次方法,然后让方法完成其工作,例如使用 if 条件操作行和列。当计数达到 4 时,中断递归。
  • @smit.. 实际上,这个问题并不适合只进行一次递归调用。因为这基本上是divide-and-conquer 之类的问题,就像merge sort。正如我所解释的,完整的问题本身分为两个主要步骤。一个向上移动,一个向左移动。因此,您必须分别解决它们,然后最终达到最终结果。您所说的对于基本递归来说是正确的。但这一切都取决于问题的具体含义。
  • int count = 0; public static void fill(int[][] arr, int row, int col) { count++; if(count == 1){ row = row - 1; } else if(count == 2){ col = col - 1; } else if (count == 3){ row = row - 1; col = col - 1; } else{ return; } arr[row][col] = arr[row][col + 1] + arr[row + 1][col] + arr[row + 1][col + 1]; fill(arr, row, col); } 这就是我的想法。它和你的没有什么不同,并且有一个关于计数器的缺陷。
【解决方案2】:
preencheComSomaVizinhos (int[][]m){
    get(m,0,0)
}
get(int[][]m, int i, int j){
    if(!(i == m.length-1 || j==m.length-1)){
        m[i][j] = get(m, i, j+1) + get(m,i+1,j)+get(m,i+1,j+1);

    }
    return m[i][j]; 
}

【讨论】:

  • if(m[i][j]==Integer.MIN_VALUE)?仅当 m[i][j] == -2,147,483,648 时才会递归?
  • 用感叹号递归的方法,来自考试题。
  • 必须是void方法,所以不能返回数组。
【解决方案3】:

我会把它写成递归的行号和列号,当你在最后一行时有一个基本情况。如果数组是方形的,你只需要一个递归参数(我假设是这种情况)。

在递归的中途,你会遇到这样的情况:

0 0 0 0 0 *
0 x + + + *  <- currently working on row k
0 - & & & *
0 - & & & *
0 - & & & *
* * * * * *

在您开始之前,*s 已经填满。然后使用参数k+1 调用递归函数,它会填充所有标记为&amp; 的单元格。然后,您必须手动填写标有+s 的行、标有-s 的列以及标有x 的角上的单个单元格。然后您将控制权传回给调用您的函数,该函数将填充标记为0 的其余单元格。

我已经有一年没有真正写过 Java了,请原谅我草率的语法!

void fillNeighbours(int[][] m) {

  int size = m.length;

  run(m, size, 0);  

}

void run(int[][] m, size, row) {

  if (row < size) {

    // fill the rest of the rows and cols
    run(m, size, row+1)

    // fill the column and row for this iteration
    for (int i = 1; size-i > row; i++) {
      m[row][size-i] = m[row+1][size-i] + m[row][size-i+1];
      m[size-i][row] = m[size-i+1][row] + m[size-i][row+1];
    }

    // fill the corner cell
    m[row][row] = m[row+1][row] + m[row][row+1];

  } else {
    // Base case, do nothing
  } 

}

【讨论】:

  • 只能使用一种方法,并且只能有一个参数。
猜你喜欢
  • 2017-10-09
  • 1970-01-01
  • 2015-03-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多