【问题标题】:Recursion behaving in an unexpected manner with Java递归在 Java 中以意想不到的方式表现
【发布时间】:2017-07-04 23:34:32
【问题描述】:

我是一名学生,我一直在努力应对以下挑战:在不使用substring 方法和使用递归的情况下,在更大的字符串(大海捞针)中找到子字符串(针)。递归不是我的强项,但我已经解决了以下问题:

public class Contains
{
    public static void main(String[] args)
    {
        System.out.println(contains("Java programming", "ogr", false));
    }

    public static boolean contains(String haystack, String needle, boolean doesContain)
    {
        if(haystack.length() < needle.length())
        {
            return false;
        }
        else
        {
            for(int i = 0; i < needle.length(); i++)
            {
                if(haystack.charAt(i) != needle.charAt(i))
                    if((i + 1) == needle.length())
                        {
                            doesContain = false;
                            break;
                        }
                    else
                        break;
                else
                    if((i + 1) == needle.length())
                    {
                        doesContain = true;
                        break;
                    }
                    else
                        continue;
            }
            char[] haystackChar = haystack.toCharArray();
            char[] newCharArray = new char[(haystackChar.length - 1)];

            for(int j = 1; j < haystackChar.length; j++)
            {
                newCharArray[j - 1] = haystackChar[j];
            }

            String newStr = new String(newCharArray);

            if(doesContain == false)
                contains(newStr, needle, doesContain);
        }
        return doesContain;
    }
}

我意识到这可能不是最好或最优雅的解决方案,但我主要只是想让它发挥作用。我一直在 Eclipse 调试器中运行它,直到在方法调用contain 期间调用if(doesContain == false) 之前,一切都按预期运行,其中doesContain 在for 循环的迭代期间设置为true。调试器显示doesContain 的值(正确)为真,并显示它跳过 if 语句并退出 else 块。然而,在那之后,它立即跳回到 else 块并且只调用对contain 的递归调用,而不是返回doesContain。然后,它继续递归工作,随后失败并返回 false,因为它现在正在搜索字符串的其余部分,其中没有“needle”。

我知道 StackOverflow 本身并不是一个“家庭作业帮助”位置,但我为学校以外的目的进行编程,我对它为什么会这样表现感到非常困惑。有谁知道它为什么这样做?我在这里遗漏了什么吗?

【问题讨论】:

  • 哦,伙计,我希望我是个女孩,会收到垃圾邮件!无论如何,您的(诚然格式不正确)代码运行良好。问题是您正在丢弃递归调用的结果。将contains(newStr, needle, doesContain); 更改为return contains(newStr, needle, doesContain);,然后瞧!

标签: java string recursion


【解决方案1】:

我查看了您的代码并自己在 Eclipse 中运行它。您将要研究的一个理论是堆栈如何在递归中工作。您的程序正在查找 true 然后离开堆栈,但到那时它已经重复发生了好几次。它返回 true,但随后也继续返回之前存储的所有 false 变量。

如果您还有其他问题,请告诉我。

编辑 如果你真的有兴趣进入高级递归,我强烈推荐这个视频:Java Recursion

嘿,我不需要走那么远就能让它工作。您可以删除 dosContain 作为参数并将其设置为静态实例变量,它对我有用。

public class Contains
{

    private static boolean doesContain = false;

    public static void main(String[] args)
    {
        System.out.println(contains("Java programming", "ogr"));
    }

    public static boolean contains(String haystack, String needle)
    {
        if(haystack.length() < needle.length())
        {
            return false;
        }
        else
        {
            for(int i = 0; i < needle.length(); i++)
            {
                if(haystack.charAt(i) != needle.charAt(i))
                    if((i + 1) == needle.length())
                        {
                            doesContain = false;
                            break;
                        }
                    else
                        break;
                else
                    if((i + 1) == needle.length())
                    {
                        doesContain = true;
                        break;
                    }
                    else
                        continue;
            }
            char[] haystackChar = haystack.toCharArray();
            char[] newCharArray = new char[(haystackChar.length - 1)];

            for(int j = 1; j < haystackChar.length; j++)
            {
                newCharArray[j - 1] = haystackChar[j];
            }

            String newStr = new String(newCharArray);

            if(doesContain == false)
                contains(newStr, needle);
        }
        return doesContain;
   }
}

您所拥有的非常接近,但是通过将其作为参数传递,您每次进行另一个递归时都会存储。这样你只返回你的最终值。

【讨论】:

  • 当您说将变量设置为null时,您的意思是什么变量?我当前的代码中已经存在的东西,或者为了评估它而添加了一个新变量?我有点理解你所说的堆栈,我之前没有考虑过。
  • 嘿@AbigailFox,很抱歉造成混乱。我所说的比你需要的要先进一点。通过设置一个实例变量,您可以保留一份 dosContain 副本,而不是为递归的每一步都存储一份。这样你只返回你的最终值。
  • 谢谢!有用!也谢谢你的解释,递归思维可能是我最不擅长的编程技能。我什至没有真正考虑过我的方法如何与堆栈交互,以及如何通过重复的方法调用处理布尔值。
  • 没问题!我最近才学习高级递归,堆栈操作背后的方法一开始可能有点令人难以置信。对于未来的努力,我建议像楼梯一样写下每个堆栈返回的内容,然后以相反的方式查看它做了什么。
【解决方案2】:

要以您想要的方式在haystack 中找到needle,您不需要使用递归。

只需从您的函数中删除以下代码行,它就可以正常工作:

        char[] haystackChar = haystack.toCharArray();
        char[] newCharArray = new char[(haystackChar.length - 1)];

        for(int j = 1; j < haystackChar.length; j++)
        {
            newCharArray[j - 1] = haystackChar[j];
        }

        String newStr = new String(newCharArray);

        if(doesContain == false)
            contains(newStr, needle, doesContain);

【讨论】:

  • 是的,不幸的是,使用递归是分配的要求。就个人而言,我发现递归更难理解,但我也意识到我必须练习它才能更好地使用它。不过感谢您的帮助!
【解决方案3】:

我认为您对递归函数有点困惑。传递给递归函数的变量之一是doesContain,但该函数应该返回字符串是否包含它!在行中

        if(doesContain == false)
            contains(newStr, needle, doesContain);

如果子字符串包含针,则对contains 的调用将返回。您需要获取该值,并将其返回到调用堆栈。

希望这有点道理。如果没有,我会给你代码,让你自己弄清楚:

public static boolean contains(String haystack, String needle)
{
    if(haystack.length() < needle.length())
    {
        return false;
    }
    else
    {
        boolean doesContain=false;
        for(int i = 0; i < needle.length(); i++)
        {
            if(haystack.charAt(i) != needle.charAt(i))
                if((i + 1) == needle.length())
                    {
                        doesContain = false;
                        break;
                    }
                else
                    break;
            else
                if((i + 1) == needle.length())
                {
                    doesContain = true;
                    break;
                }
                else
                    continue;
        }
        char[] haystackChar = haystack.toCharArray();
        char[] newCharArray = new char[(haystackChar.length - 1)];

        for(int j = 1; j < haystackChar.length; j++)
        {
            newCharArray[j - 1] = haystackChar[j];
        }

        String newStr = new String(newCharArray);

        if(doesContain == false)
            return contains(newStr, needle);
        else
            return true;
    }
}

【讨论】:

  • 我之前确实有过这样的情况,将布尔值添加到方法调用中是我试图解决我的问题的修复方法之一。我认为问题可能是我在方法开始时将doesContain 设置为false。它并没有真正解释为什么它向后跳,也没有修复它,但是,即使没有它,该方法仍然没有返回正确的值。
  • @AbigailFox 等等,我测试了它,它可以工作。不是跳回来,是跳出函数,回到调用它的函数
  • 你这是什么意思?它是怎么做到的?
  • @AbigailFox 我的意思是它返回到调用它的函数
  • 啊我现在明白了。我并没有真正考虑堆栈以及我的函数如何与之交互,但我通过将doesContain 设置为每个方法调用访问的实例变量来修复它。
猜你喜欢
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
  • 2014-01-19
  • 2013-11-07
  • 2019-01-15
  • 1970-01-01
  • 2012-03-20
  • 1970-01-01
相关资源
最近更新 更多