递归函数中的变量保持其值

本文关键字:变量 递归函数 | 更新日期: 2023-09-27 18:34:05

我正在尝试将此php代码转换为C#代码。

假设我们有一些数字矩阵

1 2 3
4 5 6
7 8 9

这个 php 代码查找了如果我们只能在任何方向上迈出一步而不重复自己,可以有多少组合。例如,从数字 1 开始,它可以是:

1,2
1,2,3
1,2,3,5
1,2,3,6
1,2,3,5,9

。等等。

这个 php 代码工作正常,但是当我在 C# 结果中做同样的事情时,结果不同,最后我收到错误。

我发现了可变path的问题。在递归中,它在深入时存储其值。

如何解决这个问题?我知道有静态变量问题,但找不到。

<?php 
$paths = array();
function find_path($graph, $start, $end, $path = array())
{
  global $paths;
  $path[] = $start;
  if ($start == $end)
     return $path;
  if (!isset($graph[$start]))
     return false;
  foreach($graph[$start] as $node) {
    if (!in_array($node, $path)) {
      $newpath = find_path($graph, $node, $end, $path);
      if ($newpath) 
        $paths[] = implode(',', $newpath);
    }
  }
  return array();
}
$graph = array(
  '1' => array('2', '4', '5'),
  '2' => array('1', '3', '5', '4', '6'),
  '3' => array('2', '5', '6'),
  '4' => array('1', '2', '7', '8', '5'),
  '5' => array('1', '2', '3', '4', '6', '7', '8'),
  '6' => array('3', '2', '5', '9', '8'),
  '7' => array('4', '5', '8'),
  '8' => array('4', '6', '6', '7', '9'),
  '9' => array('5', '6', '8')
);
for($i = 1; $i <= 9; $i++)  
  for($j = 1; $j <= 9; $j++)
    find_path($graph, (string) $i, (string) $j);
using System;
using System.Collections.Generic;

namespace ConsoleSlovoMania
{
    class Program
    {
        public static List<string> newpath = new List<string>();
        static void Main()
        {
            int[][] graph = new int[10][];
            graph[1] = new int[] { 2, 4, 5 };
            graph[2] = new int[] { 1, 3, 5, 4, 6 };
            graph[3] = new int[] { 2, 5, 6 };
            graph[4] = new int[] { 1, 2, 7, 8, 5 };
            graph[5] = new int[] { 1, 2, 3, 4, 6, 7, 8 };
            graph[6] = new int[] { 3, 2, 5, 9, 8 };
            graph[7] = new int[] { 4, 5, 8};
            graph[8] = new int[] { 4, 6, 6, 7, 9};
            graph[9] = new int[] { 5, 6, 8 };
            for (int i = 1; i <= 9; i++) 
            {
                for (int j = 1; j <= 9; j++) 
                {
                    find_path(graph, i, j);
                }
            }
            Console.ReadKey();
        }
        private static List<string> find_path(int[][] graph, int start, int end, List<string> path = null)
        {
            Console.Write("start = " + start + " | end = " + end);
            Console.WriteLine();
            if (path == null)
            {
                path = new List<string>();
            }
            path.Add(start.ToString());
            path.ForEach(Console.Write);
            Console.WriteLine();
            if (start == end)
            {
                return path;
            }
            foreach (int node in graph[start])
            {
                if (path.Exists(element => element == node.ToString()) == false)
                {
                    newpath.AddRange(find_path(graph, node, end, path));
                    if (newpath.Count > 0)
                    {
                        //newpath.ForEach(Console.WriteLine);
                        newpath.Clear();
                    }
                }
            }
            return path = null;
        }
    }
}

递归函数中的变量保持其值

这应该可以解决异常:

newpath.AddRange() 在返回

null 时引发异常find_path()

find_path()结果分配给变量,并在添加到变量之前检查 null newpath

若要修复逻辑错误,请记住,在 .NET 中List<String>是一个对象,在递归步骤中传递path时,将引用的值传递给该对象,而不是对象的副本。轻松修复:只需复制列表(使用 LINQ 很容易(,如下所示:

path.ToList()

下面是完整的固定 C# 代码。我重新排列了它并重命名了几个变量,使其更像是 PHP 代码的 1:1 音译;它使查找和修复List<String> path突变问题变得更加容易。

namespace ConsoleSlovoMania
{
    class Program
    {
        public static List<string> paths = new List<string>();
        private static List<string> find_path(int[][] graph, int start, int end, List<string> path = null)
        {
            if (path == null)
            {
                path = new List<string>();
            }
            path.Add(start.ToString());
            if (start == end)
            {
                return path;
            }
            foreach (int node in graph[start])
            {
                if (!path.Contains(node.ToString()))
                {
                    // before calling recursive step, copy path instead of passing around object reference value
                    List<String> newPath = find_path(graph, node, end, path.ToList());
                    if (newPath != null)
                    {
                        paths.Add(String.Join(",", newPath.ToArray()));
                    }
                }
            }
            return path = null;
        }
        static void Main()
        {
            int[][] graph = new int[10][];
            graph[1] = new int[] { 2, 4, 5 };
            graph[2] = new int[] { 1, 3, 5, 4, 6 };
            graph[3] = new int[] { 2, 5, 6 };
            graph[4] = new int[] { 1, 2, 7, 8, 5 };
            graph[5] = new int[] { 1, 2, 3, 4, 6, 7, 8 };
            graph[6] = new int[] { 3, 2, 5, 9, 8 };
            graph[7] = new int[] { 4, 5, 8};
            graph[8] = new int[] { 4, 6, 6, 7, 9};
            graph[9] = new int[] { 5, 6, 8 };
            for (int i = 1; i <= 9; i++) 
            {
                for (int j = 1; j <= 9; j++) 
                {
                    find_path(graph, i, j);
                }
            }
            Console.ReadKey();
        }
    }
}

若要验证结果,可以在带有 Console.ReadyKey() 的行上放置一个断点,并验证 paths 的内容是否等效于 PHP 代码返回的结果。对于 PHP,您可以使用调试器或print_r($paths)来吐出数组。我通过将paths$paths序列化为 JSON 来自动化此验证,这在 jsFiddle 中非常容易测试,请参阅此处:http://jsfiddle.net/ea89L8x8/