在Java中递归反转字符串的最佳方法是啥?

Posted

技术标签:

【中文标题】在Java中递归反转字符串的最佳方法是啥?【英文标题】:Whats the best way to recursively reverse a string in Java?在Java中递归反转字符串的最佳方法是什么? 【发布时间】:2010-10-25 23:09:34 【问题描述】:

我今天一直在搞递归。通常是一种未被充分使用的编程技术。

我开始递归地反转一个字符串。这是我想出的:

//A method to reverse a string using recursion
    public String reverseString(String s)
        char c = s.charAt(s.length()-1);
        if(s.length() == 1) return Character.toString(c);   

        return c + reverseString(s.substring(0,s.length()-1));
    

我的问题:Java 中有没有更好的方法?

【问题讨论】:

【参考方案1】:

最好的方法是不使用递归。这些东西通常用于教授学生递归概念,而不是实际的最佳实践。所以你这样做的方式很好。只是不要在 Java 中将递归用于现实世界应用程序中的这类东西;)

附言。除了我刚才所说的,我会选择 "" 作为递归函数的基本情况:

public String reverseString(String s)
    if (s.length() == 0) 
         return s;

    return reverseString(s.substring(1)) + s.charAt(0);

【讨论】:

如果我错了,请纠正我,但我相信 Mehrdad 只是指像这样的微不足道的情况。尽管与迭代解决方案相比,递归解决方案实际上可能更容易想到,但我相信它会占用更多的“堆栈”来执行递归。在反转字符串的情况下,为什么在根本没有必要的情况下要进行递归解决方案?基本上在你绝对需要的情况下使用递归。 这需要一大堆【参考方案2】:

如果你要这样做,你想对一个字符数组进行操作,因为字符串是不可变的,如果你这样做,你将在整个地方复制字符串。

这是未经测试的,完全是意识流。它可能在某处有一个OB1。而且非常不是 Java。

public String reverseString(String s)
  
  char[] cstr = s.getChars();
  reverseCStr(cstr, 0, s.length - 1);

  return new String(cstr);
  

/**
 * Reverse a character array in place.
 */
private void reverseCStr(char[] a, int s, int e)
  
  // This is the middle of the array; we're done.
  if (e - s <= 0)
    return;

  char t = a[s];
  a[s] = a[e];
  a[e] = t;
  reverseCStr(a, s + 1, e - 1);
  

【讨论】:

是的,我知道使用字符串的 C/C++ 类似方式,我对 Java 实现更感兴趣,但我确实喜欢相比之下它的效率。 嗯,这个用Java写的。它只是不是一种 Java 式的做事方式。在任何情况下,我都不会在 Java C 中递归地执行此操作,因此将 C 写到 Java 中并没有应有的错误。【参考方案3】:

你不想嵌套太深。分而治之是要走的路。还减少了临时字符串的总大小,并且可以并行化。

public static String reverseString(String str) 
    int len = str.length();
    return len<=1 ? str : (
        reverseString(str.substring(len/2))+
        reverseString(str.substring(0, len/2))
    );

(未测试 - 这是 ***。)

String.concat 而不是+ 会以清晰度为代价提高性能。

编辑:只是为了好玩,朴素算法的尾递归友好版本。

public static String reverseString(String str) 
    return reverseString("", str);

private static String reverseString(String reversed, String forward) 
    return forward.equals("") ? reversed : (
         reverseString(reversed+forward.charAt(0), forward.substring(1)) 
    );

代理对的正确处理留给感兴趣的读者。

【讨论】:

差别不大...仍然是 O(n) 操作(假设字符串连接为 O(1) 操作)。它只是看起来更像 MergeSort 和其他东西,但事实并非如此。唯一的好处是——正如你所描述的——递归树的深度。 串联是 O(n)(对于足够大的 n)。 我认为,我的方法给出 O(n log n) 临时字符串字符,而线性方法给出 O(n^2)。 两者都给出 O(n) 个临时字符串。 第二种方法在这部分有错误reversed+forward.charAt(0),应该是forward.charAt(0) + reversed。但是它很容易给出OutOfMemoryError: Java heap space 错误。【参考方案4】:

这是我工作正常的递归反向函数

public static String rev(String instr)
    if(instr.length()<=1)
        return instr;
     else 
       return (instr.charAt(instr.length()-1)+rev(instr.substring(0,instr.length()-1)) );
           

【讨论】:

【参考方案5】:

顺便说一句,这里有一个使用StringBuilder 的尾递归方法(通常建议使用Strings)。

public String reverseString(String s_) 
    StringBuilder r = new StringBuilder();
    StringBuilder s = new StringBuilder(s_);
    r = reverseStringHelper(r, s);
    return r.toString();

private StringBuilder reverseStringHelper(StringBuilder r, StringBuilder s) 
    if (s.length() == 0)
        return r;
    else
        return reverseStringHelper(r.append(s.charAt(0)), s.deleteCharAt(0));

未经测试,我已经很多年没有接触过Java了,但这应该是正确的。

【讨论】:

我喜欢它,非常酷 :-) 当然,我们都可以很懒惰并使用 StringBuilder 反向方法,但其中的递归呢?!哈哈 如果 StringBuilder 的(常见实现)使用 off+len(或类似的)而不是仅仅使用 len,效果会更好。另外,我没有看到您的返回值的意义。 @Tom:假装这些方法返回新对象而不是就地变异,让它感觉更实用;)【参考方案6】:

如果您正在编写真正的代码(不是学习递归),请使用 StringBuilder 的 reverse() 方法。 Java Tutorial 给出了这个例子:

String palindrome = "Dot saw I was Tod";   
StringBuilder sb = new StringBuilder(palindrome);
sb.reverse();  // reverse it
System.out.println(sb);

【讨论】:

【参考方案7】:

这取决于您对“更好”的定义。 :-) 说真的,不过;您的解决方案本质上使用了最大递归深度;如果堆栈大小与您对“更好”的定义有关,那么您最好使用以下内容:

public String reverseString(String s) 
   if (s.length() == 1) return s;
   return reverseString(s.substring(s.length() / 2, s.length() -1) + reverseString(0, s.length() / 2);

【讨论】:

啊,我明白了。这种本质上将问题减半并将基本情况应用于问题的每一半的方法是“从递归中获得最佳效果”的常用方法吗? 好吧,就像我说的,这就是你定义“更好”的方式。这种方法并没有明显更快,但是使用这种方法获得的最高递归深度(因此也是最大的堆栈)是 log(s.length()),而使用最初提出的方法,递归的最高深度你得到的是 s.length()。这种方式会节省一点堆栈大小。 看起来你有一个流浪的 -1,缺少子字符串和其他一些东西在那很长的行中发生。它不适用于“”。 @tom:这只是伪代码。我不是在为 OP 编写解决方案;只是提供一个建议并快速了解我在说什么。【参考方案8】:

这是我发现的工作和使用递归。您可以将str.length() 作为 strLength 参数传递

private static String reverse(String str, int strLength) 
    String result = "";
    if(strLength > 0)
        result = str.charAt(strLength - 1) + reverse(str, strLength - 1);
    return result;

【讨论】:

【参考方案9】:

在 Java 中,由于 String 是不可变的,因此 String 连接会比看起来更复杂。

对于每个连接,它会创建一个复制原始字符串内容的新字符串,从而导致线性复杂度 O(n) 其中 n 是字符串的长度,所以对于m个这样的操作是O(m*n),我们可以说它是二次复杂度O(n^2)

我们可以使用 StringBuilder,它对每个追加都有 O(1) 复杂度。下面是使用 StringBuilder 的递归程序。这仅使用 n/2 堆栈帧,因此它的空间复杂度比普通递归调用要小,如 s.charAt(s.length-1) + reverse(s.subString(0, s.length-2);

public class StringReverseRecursive 
    public static void main(String[] args) 
        String s = "lasrever gnirts fo noitatnemelpmi evisrucer a si sihT";
        StringBuilder sb = new StringBuilder(s);
        reverse(s, sb, 0, sb.length() - 1);
        System.out.println(sb.toString());
    

    public static void reverse(String s, StringBuilder sb, int low, int high) 
        if (low > high)
            return;
        sb.setCharAt(low, s.charAt(high));
        sb.setCharAt(high, s.charAt(low));
        reverse(s, sb, ++low, --high);
    

【讨论】:

【参考方案10】:

这绝对是我递归反转字符串的方式(尽管在您的条件下将它扩展到空字符串的情况可能会很好。)我认为没有任何根本上更好的方法。

编辑:对字符数组进行操作并在递归链中传递一个“截止”长度可能更有效,如果你明白我的意思,而不是制作子字符串。然而,这并不值得吹毛求疵,因为它本来就不是一种非常有效的技术。

【讨论】:

【参考方案11】:

您捕获了基本概念,但提取最后一个字符并不能提高清晰度。我更喜欢以下内容,其他人可能不会:

public class Foo

    public static void main(String[] argv) throws Exception
    
        System.out.println(reverse("a"));
        System.out.println(reverse("ab"));
        System.out.println(reverse("abc"));
    


    public final static String reverse(String s)
    
        // oft-repeated call, so reduce clutter with var
        int length = s.length();

        if (length <= 1)
            return s;
        else
            return s.substring(length - 1) + reverse(s.substring(0, length - 1));
    

【讨论】:

【参考方案12】:

正如Mehrdad 所说,最好不要使用递归。但是,如果您确实使用它,您不妨保留每次调用的第一个和最后一个字符,从而将递归调用的数量减半。也就是说,

public String reverseString(String s)
    int len = s.length();

    if (len <= 1) 
        return s;
    

    char fst = s.charAt(0);
    char lst = s.charAt(len - 1);

    return lst + reverseString(s.substring(1, len - 2)) + fst;

这也处理空字符串的情况。也许传递具有适当容量的 StringBuilder 会加快速度,但这留给读者作为练习;)

【讨论】:

例如,这在字符串“a”上失败。为什么比原版好? s/==/ 我喜欢它,因为它将递归堆栈减半,但如前所述,它不适用于像“a”这样的字符串。也许可以将这种方法与上面的尾递归一起使用,首先验证字符串然后应用递归。正如我所看到的,您理解如果您反转一个字符的字符串,没有人会关心,因为反过来是一样的。我的假设是对的吗? 我不知道为什么更正后的版本不适用于“a”:它的长度为 1,因此返回原样。我想我不明白你最后的话。【参考方案13】:

您可以尝试使用外部变量,并将所有字符 1 1 相加:

    public static String back="";

public static String reverseString(String str)


    if(str.length()==0)

        return back;

    else 

        back+=str.charAt(str.length()-1);

        lees(str.substring(0,str.length()-1));


        return back;

    

【讨论】:

我想我在第一次尝试中确实尝试过这样的事情。【参考方案14】:

Here 是我的不可变版本:

 String reverse(String str) 
    if(str.length()<2) return str;
    return  reverse(str.substring(1)) +str.charAt(0);

和尾递归版本:

 String reverseTail(String str) 
    if(str.length()<2) return str;
    return str.charAt(str.length()-1)+ reverseTail(str.substring(0,str.length()-1));

【讨论】:

【参考方案15】:

在这种情况下,这是完全没有必要的,但如果您自己制作堆栈,则可以模拟递归并避免递归深度问题。

您可以迭代实现递归,这在您拥有本质上是递归的算法时可能是必要的,但也需要针对大问题运行它们。

String recIterReverse (String word)

    Stack <String> stack = new Stack <String> ();
    stack.push(word);
    String result = "";

    while (!stack.isEmpty())
        String temp = stack.pop();
        result = temp.charAt(0) + result;

        if (temp.length() > 1)
        stack.push(temp.substring(1));
        
    

    return result;

【讨论】:

【参考方案16】:

函数调用:

    //str:string to be reversed,i=0,j=str.length-1


    public void reverseString(String str,int i,int j)
    

     if(i==j)
        
         System.out.println(str);
         return;
        

     char x=str.charAt(i);
     str=str.replace(str.charAt(i),str.charAt(j));
     str=str.replace(str.charAt(j),x);
     i++;j--;
     reverseString(str,i,j);
    

这个方法也有效..

【讨论】:

【参考方案17】:

尝试以下方法:

public class reverse2

    public static void main(String name)
    
    String revname=new StringBuffer(name).reverse().toString();
    System.out.println("original string " + name + " and the reverse of it is " + revname);
    

【讨论】:

【参考方案18】:
public static String rev(String name)
    if(name.length()>=1)
    System.out.print(name.charAt(name.length()-1)); 
    return rev(name.substring(0,name.length()-1));
    
    else
        return ""+name.substring(0);
    

【讨论】:

【参考方案19】:
String rev="";

public String reverseString(String s)
        if (s.length()==0) return "";
        return rev+s.substring(s.length()-1,s.length())+reverseString(s.substring(0, s.length()-1));
    

【讨论】:

【参考方案20】:
public String reverseString (String s) 

    if (s != null && s.length () > 0 ) 
        rev = rev + s.substring (s.length () - 1);
        reverseString (s.substring (0, s.length () - 1));
    
    return rev;


【讨论】:

【参考方案21】:
public class StringUtility 

public static void main(String[] args) 
    StringUtility stringUtility = new StringUtility();

    String input = "santosh123";
    int middle = input.length() / 2;
    middle = middle - 1;
    System.out.println(stringUtility.stringReverse(input, middle));


public String stringReverse(String input, int middle) 

    if (middle == -1) 

        System.out.println("if");
        return input;

     else 

        System.out.println("else");
        input = swapChar(input, middle);
        middle = middle - 1;
        return stringReverse(input, middle);

    



private String swapChar(String input, int middle) 
    StringBuilder str = new StringBuilder(input);
    char begin = str.charAt(middle);
    int endIndex = input.length() - middle - 1;
    char end = str.charAt(endIndex);
    str.setCharAt(middle, end);
    str.setCharAt(endIndex, begin);
    System.out.println(str + "  " + middle + "  " + endIndex);
    return str.toString();



【讨论】:

【参考方案22】:

如果你认为代码越少越好......

static String reverse(String str)
    return str.length()>=2 ? str.charAt(str.length()-1) + reverse(str.substring(0,str.length()-1)) : str ;

【讨论】:

【参考方案23】:

已经有大约 20 个答案,但我也将加入我的递归算法。它可能有点冗长,但至少是可读的。

public static String reverseString(String str) 
   return reverseString("", str);


private static String reverseString(String result, String original) 
   if (original.length() == 0) 
      return result;
    else 
      int length = original.length();
      String lastLetter = original.substring(length - 1, length);
      original = original.substring(0, length - 1);
      return reverseString(result + lastLetter, original);
   

代码基本上递归地获取字符串的末尾并将其移到前面。例如,如果我们要反转的字符串是“jam”,那么每次调用辅助方法时,结果和原始字符串如下:

// result:  original:
// ""       jam
// m        ja
// ma       j
// maj      ""

【讨论】:

【参考方案24】:

使用递归方法调用反转字符串。

示例代码

public static String reverseString(String s) 
    if (s.length() == 0) 
        return s;
    
    else 
        return s.charAt(s.length() - 1) + reverseString(s.substring(0, s.length() - 1));
    

【讨论】:

能否请您添加一些解释,为什么这是最好的方法?【参考方案25】:

这是我的解决方案,我在上面的许多解决方案中看到我们正在获取字符串长度,但理想情况下我们不需要它。 Zen 是使用递归,只需将字符串的第一个字符切掉,然后将其余的传递给递归方法。耶!!我们得到了解决方案。

private static void printReverse(String str) 
            if (!str.isEmpty()) 
                String firstChar = str.substring(0, 1); //Get first char of String
                String newstr = str.substring(0, 0) + str.substring(1); // Get remaining string
                printReverse(newstr); // Recursion magic
                System.out.print(firstChar); //Output
            
        

【讨论】:

【参考方案26】:
public static String reverse(String s)

    int n = s.length()-1;

    if(n >=0)
    return  s.substring(s.length()-1)+ReverseString(s.substring(0,n--));
    else return "";

【讨论】:

以上是关于在Java中递归反转字符串的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Java中如何实现字符串反转?

如何用JS实现字符串反转

递归--字符串反转

由字符串反转(使用递归)引申出来一道Java面试题

LeetCode 206——字符串反转(JAVA)

为啥在 Python 2.7 中手动字符串反转比切片反转更糟糕? Slice 中使用的算法是啥?