在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
的尾递归方法(通常建议使用String
s)。
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中递归反转字符串的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章